diff --git a/ESP32_AD_Wandler/ESP32_AD_Wandler.ino b/ESP32_AD_Wandler/ESP32_AD_Wandler.ino index 09d5f2d..aa704bb 100644 --- a/ESP32_AD_Wandler/ESP32_AD_Wandler.ino +++ b/ESP32_AD_Wandler/ESP32_AD_Wandler.ino @@ -5,7 +5,7 @@ // jfs Wemos lolin32 // jfs Heltec WiFi kit 32 (weisses Board) -#define HELTEC +// #define HELTEC #define DEBUG // Initialize the OLED display using Wire library @@ -73,9 +73,9 @@ boolean init_wifi(){ void setup() { // test - adcAttachPin(37); + adcAttachPin(25); analogSetClockDiv(1); // 1338mS - analogSetPinAttenuation(13,ADC_0db); + analogSetPinAttenuation(26,ADC_0db); // put your setup code here, to run once: #ifdef HELTEC @@ -122,29 +122,30 @@ void loop() { // } // } //} - unsigned long currentMillis = millis(); - if (currentMillis - previousMillis >= interval) { - previousMillis = currentMillis; - Serial.print("Connecting to "); - Serial.println(server); - WiFiClient client; // Für TCP-Connection - if (!client.connect(server, port)) { - Serial.println("Connection failed"); - return; - } + //unsigned long currentMillis = millis(); + //if (currentMillis - previousMillis >= interval) { + // previousMillis = currentMillis; + //Serial.print("Connecting to "); + //Serial.println(server); + //WiFiClient client; // Für TCP-Connection + //if (!client.connect(server, port)) { + // Serial.println("Connection failed"); + // return; + //} int analogValue = analogRead(36);// Messwerterfassung - Serial.println(analogValue); - client.println(analogValue); // Sende Daten zum Server - client.stop();// Schließe TCP-Verbindung - } + //Serial.println(analogValue); + //client.println(analogValue); // Sende Daten zum Server + // client.stop();// Schließe TCP-Verbindung + //} - double volt2 = ReadVoltage(36)*127000/27000; - double volt1 = ReadVoltage0dB(37); + double volt2 = ReadVoltage(25)*127000/27000; + double volt1 = ReadVoltage0dB(26); Serial.print(volt2,1); Serial.print(" "); Serial.println(volt1); display.clear(); + display.drawString(0, 10, WiFi.localIP().toString()); display.drawString(0,20,"Volt 1 "+String(volt1)); display.drawString(0,30,"Volt 2 "+String(volt2)); display.display(); diff --git a/ESP32_AD_mqtt/ESP32_AD_mqtt.ino b/ESP32_AD_mqtt/ESP32_AD_mqtt.ino index 3ee5ca7..dd6586a 100644 --- a/ESP32_AD_mqtt/ESP32_AD_mqtt.ino +++ b/ESP32_AD_mqtt/ESP32_AD_mqtt.ino @@ -5,7 +5,7 @@ // jfs Wemos lolin32 // jfs Heltec WiFi kit 32 (weisses Board) -#define HELTEC +//define HELTEC #define DEBUG // Initialize the OLED display using Wire library diff --git a/ESP32_AD_test/ESP32_AD_test.ino b/ESP32_AD_test/ESP32_AD_test.ino new file mode 100644 index 0000000..5442bf9 --- /dev/null +++ b/ESP32_AD_test/ESP32_AD_test.ino @@ -0,0 +1,133 @@ +#include + +#include "SSD1306.h" // alias for `#include "SSD1306Wire.h" + +// jfs Wemos lolin32 +// jfs Heltec WiFi kit 32 (weisses Board) +// #define HELTEC +#define DEBUG + +// Initialize the OLED display using Wire library +#ifdef HELTEC +SSD1306 display(0x3c, 4, 15); +#else +SSD1306 display(0x3c, 5, 4); +#endif + +//// WIFI +const int mxSize=4; +String ssids[mxSize] ={"GAST","pipanet","FRITZ!Box Gastzugang","WLAN-DE8245"}; +String ssidp[mxSize] = {"passatvr6","passatvr6","praxis123","4955065570896956"}; +boolean conok =false; + +void netfound(int i){ + display.clear(); + display.setColor(BLACK); + display.fillRect(0, 0, 128, 10); + display.setColor(WHITE); + display.drawString(0,0,String(i)); + display.drawString(20,0,"networks found"); + display.display(); +} + +boolean init_wifi(){ + boolean ok = false; + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + int n = WiFi.scanNetworks(); + Serial.println("scan done"); + if (n == 0) { + Serial.println("no networks found"); + netfound(0); + } else { + Serial.print(n); + Serial.println(" networks found"); + netfound(n); + for (int i = 0; i < n; ++i) { + for (int p=0;p 4095) return 0; + return reading*3.3/4095; +} +double ReadVoltage(byte pin){ + double reading = analogRead(pin); // Reference voltage is 3v3 so maximum reading is 3v3 = 4095 in range 0 to 4095 + if(reading < 1 || reading > 4095) return 0; + // return -0.000000000009824 * pow(reading,3) + 0.000000016557283 * pow(reading,2) + 0.000854596860691 * reading + 0.065440348345433; + return -0.000000000000016 * pow(reading,4) + 0.000000000118171 * pow(reading,3)- 0.000000301211691 * pow(reading,2)+ 0.001109019271794 * reading + 0.034143524634089; +} // Added an improved polynomial, use either, comment out as required diff --git a/ESP32_FreqcounterLCD/ESP32_FreqcounterLCD.ino b/ESP32_FreqcounterLCD/ESP32_FreqcounterLCD.ino new file mode 100644 index 0000000..0b90a74 --- /dev/null +++ b/ESP32_FreqcounterLCD/ESP32_FreqcounterLCD.ino @@ -0,0 +1,234 @@ + +// BLOG Eletrogate +// ESP32 Frequencimetro +// ESP32 DevKit 38 pinos + LCD +// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao +// Rui Viana e Gustavo Murta agosto/2020 + +#include "stdio.h" // Biblioteca STDIO +#include "driver/ledc.h" // Biblioteca ESP32 LEDC +#include "driver/pcnt.h" // Biblioteca ESP32 PCNT + +#define LCD_OFF // Defina LCD_ON, para usar LCD, se não, defina LCD_OFF +#define LCD_I2C_OFF // Defina LCD_I2C_ON, para usar LCD I2C, se não, defina LCD_I2C_OFF + +#ifdef LCD_I2C_ON // Se habilitar LCD I2C +#define I2C_SDA 21 // LCD I2C SDA - GPIO_21 +#define I2C_SCL 22 // LCD I2C SCL - GPIO_22 +#include // Biblioteca para I2C +#include // Biblioteca para LCD com PCF8574 +LiquidCrystal_PCF8574 lcd(0x3F); // Instancia LCD I2C com endereço x3F +#endif // LCD I2C + +#ifdef LCD_ON // Se habilitar LCD com interface 4 bits +#include // Biblioteca para LCD +LiquidCrystal lcd(4, 16, 17, 5, 18, 19); // Instancia e define os ports +#endif // LCD + +#define PCNT_COUNT_UNIT PCNT_UNIT_0 // Unidade 0 do Contador de pulso PCNT do ESP32 +#define PCNT_COUNT_CHANNEL PCNT_CHANNEL_0 // Canal 0 do Contador de pulso PCNT do ESP32 + +#define PCNT_INPUT_SIG_IO GPIO_NUM_34 // Entrada do Frequencimetro - GPIO 34 +#define LEDC_HS_CH0_GPIO GPIO_NUM_33 // Saida do LEDC - gerador de pulsos - GPIO_33 +#define PCNT_INPUT_CTRL_IO GPIO_NUM_35 // Pino de controle do PCNT - HIGH = count up, LOW = count down +#define OUTPUT_CONTROL_GPIO GPIO_NUM_32 // Saida do timer - Controla a contagem - GPIO_32 +#define PCNT_H_LIM_VAL overflow // Limite superior de contagem + +#define IN_BOARD_LED GPIO_NUM_2 // LED nativo ESP32 - GPIO 2 + +bool flag = true; // Indicador de fim de contagem - libera impressão +uint32_t overflow = 20000; // Valor maximo para overflow do contador PCNT +int16_t pulses = 0; // Quantidade de pulsos contados +uint32_t multPulses = 0; // Quantidade de overflows do contador PCNT +uint32_t janela = 1000000; // Tempo de amostragem de 1 segundo para a contagem de pulsos 999990 +uint32_t oscilador = 12543; // Frequencia inicial do oscilador - 12543 Hz +uint32_t mDuty = 0; // Valor calculado do ciclo de carga +uint32_t resolucao = 0; // Valor calculado da resolucao +float frequencia = 0; // Variavel para calculo de frequencia +char buf[32]; // Buffer para guardar a pontuacao + +esp_timer_create_args_t create_args; // Argumentos do ESP-Timer +esp_timer_handle_t timer_handle; // Instancia de ESP-Timer + +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // variavel tipo portMUX_TYPE para sincronismo + +//---------------------------------------------------------------------------------------- +void setup() +{ + Serial.begin(115200); // Inicializa a serial 115200 Bps + Serial.println(" Digite uma frequencia - 1 a 40 MHz"); // Print na console + +#ifdef LCD_I2C_ON // Se estiver usando LCD I2C + Wire.begin(I2C_SDA, I2C_SCL); // Inicializa Interface I2C + lcd.setBacklight(255); // Ativa leds do backlight do LCD +#endif + +#if defined LCD_ON || defined LCD_I2C_ON // Se estiver usando LCD ou LCD I2C + lcd.begin(16, 2); // Inicializa LCD 16 colunas 2 linhas + lcd.print(" Frequencia:"); // Print no LCD +#endif + + inicializa_frequencimetro(); // Inicializa o frequencimetro +} + +//---------------------------------------------------------------------------- +void inicializa_oscilador () // Inicializa gerador de pulsos +{ + resolucao = (log (80000000 / oscilador) / log(2)) / 2 ; // Calculo da resolucao para o oscilador + if (resolucao < 1) resolucao = 1; // Resoluçao mínima + // Serial.println(resolucao); // Print + mDuty = (pow(2, resolucao)) / 2; // Calculo do ciclo de carga 50% do pulso + // Serial.println(mDuty); // Print + + ledc_timer_config_t ledc_timer = {}; // Instancia a configuracao do timer do LEDC + + ledc_timer.duty_resolution = ledc_timer_bit_t(resolucao); // Configura resolucao + ledc_timer.freq_hz = oscilador; // Configura a frequencia do oscilador + ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // Modo de operacao em alta velocidade + ledc_timer.timer_num = LEDC_TIMER_0; // Usar timer0 do LEDC + ledc_timer_config(&ledc_timer); // Configura o timer do LEDC + + ledc_channel_config_t ledc_channel = {}; // Instancia a configuracao canal do LEDC + + ledc_channel.channel = LEDC_CHANNEL_0; // Configura canal 0 + ledc_channel.duty = mDuty; // Configura o ciclo de carga + ledc_channel.gpio_num = LEDC_HS_CH0_GPIO; // Configura GPIO da saida do LEDC - oscilador + ledc_channel.intr_type = LEDC_INTR_DISABLE; // Desabilita interrupção do LEDC + ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; // Modo de operacao do canal em alta velocidade + ledc_channel.timer_sel = LEDC_TIMER_0; // Seleciona timer 0 do LEDC + ledc_channel_config(&ledc_channel); // Configura o canal do LEDC +} + +//---------------------------------------------------------------------------------- +static void IRAM_ATTR pcnt_intr_handler(void *arg) // Contagem do contador de Overflow +{ + portENTER_CRITICAL_ISR(&timerMux); // Bloqueia nova interrupção + multPulses++; // Incrementa contador de overflow + PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT); // Limpa indicador de interrupção + portEXIT_CRITICAL_ISR(&timerMux); // Libera nova interrupção +} + +//---------------------------------------------------------------------------------- +void inicializa_contador(void) // Inicializacao do contador de pulsos +{ + pcnt_config_t pcnt_config = { }; // Instancia PCNT config + + pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO; // Configura GPIO para entrada dos pulsos + pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO; // Configura GPIO para controle da contagem + pcnt_config.unit = PCNT_COUNT_UNIT; // Unidade de contagem PCNT - 0 + pcnt_config.channel = PCNT_COUNT_CHANNEL; // Canal de contagem PCNT - 0 + pcnt_config.counter_h_lim = PCNT_H_LIM_VAL; // Limite maximo de contagem - 20000 + pcnt_config.pos_mode = PCNT_COUNT_INC; // Incrementa contagem na subida do pulso + pcnt_config.neg_mode = PCNT_COUNT_INC; // Incrementa contagem na descida do pulso + pcnt_config.lctrl_mode = PCNT_MODE_DISABLE; // PCNT - modo lctrl desabilitado + pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // PCNT - modo hctrl - se HIGH conta incrementando + pcnt_unit_config(&pcnt_config); // Configura o contador PCNT + + pcnt_counter_pause(PCNT_COUNT_UNIT); // Pausa o contador PCNT + pcnt_counter_clear(PCNT_COUNT_UNIT); // Zera o contador PCNT + + pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM); // Configura limite superior de contagem + pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL); // Conigura rotina de interrupção do PCNT + pcnt_intr_enable(PCNT_COUNT_UNIT); // Habilita interrupções do PCNT + + pcnt_counter_resume(PCNT_COUNT_UNIT); // Reinicia a contagem no contador PCNT +} + +//---------------------------------------------------------------------------------- +void tempo_controle(void *p) // Fim de tempo de leitura de pulsos +{ + gpio_set_level(OUTPUT_CONTROL_GPIO, 0); // Controle do PCNT - para o contador + pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses); // Obtem o valor contado no PCNT + flag = true; // Informa que ocorreu interrupção de controle +} + +//--------------------------------------------------------------------------------- +void inicializa_frequencimetro() +{ + inicializa_oscilador (); // Inicia a geração de pulsos no oscilador + inicializa_contador(); // Inicializa o contador de pulsos PCNT + + gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO); // Define o port decontrole + gpio_set_direction(OUTPUT_CONTROL_GPIO, GPIO_MODE_OUTPUT); // Define o port de controle como saida + + create_args.callback = tempo_controle; // Instancia o tempo de controle + esp_timer_create(&create_args, &timer_handle); // Cria parametros do timer + + gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT); // Port LED como saida + + gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false); // Direciona a entrada de pulsos + gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false); // Para o LED do ESP32 +} + +//---------------------------------------------------------------------------------------- +char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos) // Formata um número longo de 32 bits com pontos +{ + int c; + if (val >= radix) + s = ultos_recursive(val / radix, s, radix, pos + 1); + c = val % radix; + c += (c < 10 ? '0' : 'a' - 10); + *s++ = c; + if (pos % 3 == 0) *s++ = '.'; + return s; +} +//---------------------------------------------------------------------------------------- +char *ltos(long val, char *s, int radix) // Formata um número longo de 32 bits com pontos +{ + if (radix < 2 || radix > 36) { + s[0] = 0; + } else { + char *p = s; + if (radix == 10 && val < 0) { + val = -val; + *p++ = '-'; + } + p = ultos_recursive(val, p, radix, 0) - 1; + *p = 0; + } + return s; +} + +//--------------------------------------------------------------------------------- +void loop() +{ + if (flag == true) // Se a contagem tiver terminado + { + flag = false; // Impede nova impressao + frequencia = (pulses + (multPulses * overflow)) / 2 ; // Calcula a soma dos pulsos contados no PCNT + printf("Frequencia : %s", (ltos(frequencia, buf, 10))); // Print frequencia com pontos + printf(" Hz \n"); // Print unidade Hz + +#if defined LCD_ON || defined LCD_I2C_ON // Se estiver usando LCD ou LCD I2C + lcd.setCursor(2, 1); // Posiciona cursor na posicao 2 da linha 1 + lcd.print((ltos(frequencia, buf, 10))); // Print frequencia no LCD + lcd.print(" Hz "); // Print unidade Hz no LCD +#endif + + multPulses = 0; // Zera contador de overflow + // Espaco para qualquer função + delay (100); // Delay 100 ms + // Espaco para qualquer função + + pcnt_counter_clear(PCNT_COUNT_UNIT); // Zera o contador PCNT + esp_timer_start_once(timer_handle, janela); // Inicia contador de tempo de 1 segundo + gpio_set_level(OUTPUT_CONTROL_GPIO, 1); // Porta de controle - habilita contagem dos pulsos + } + + String inputString = ""; // Limpa string para entrada de dados + oscilador = 0; // Zera o valor da frequencia + while (Serial.available()) // Enquanto tiver dados na serial + { + char inChar = (char)Serial.read(); // Le um byte: + inputString += inChar; // Adicione na string: + if (inChar == '\n') // Se pressionar ENTER: + { + oscilador = inputString.toInt(); // Transforma a string em inteiro + inputString = ""; // Limpa a string + } + } + if (oscilador != 0) // Se foi digitado algum valor + { + inicializa_oscilador (); // Reconfigura a frequencia do oscilador + } +} diff --git a/ESP32_jfs_freqcounter/ESP32_jfs_freqcounter.ino b/ESP32_jfs_freqcounter/ESP32_jfs_freqcounter.ino new file mode 100644 index 0000000..7607dfe --- /dev/null +++ b/ESP32_jfs_freqcounter/ESP32_jfs_freqcounter.ino @@ -0,0 +1,393 @@ +// BLOG Eletrogate +// ESP32 Frequency Meter +// ESP32 DevKit 38 pins + LCD +// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao +// Rui Viana and Gustavo Murta august/2020 + +#include "stdio.h" // Library STDIO +#include "driver/ledc.h" // Library ESP32 LEDC +#include "driver/pcnt.h" // Library ESP32 PCNT + +#define LCD_OFF // LCD_ON, if want use LCD 4 bits interface +#define LCD_I2C_OFF // LCD_I2C_ON, if want use I2C LCD (PCF8574) +#define JFS +#define TEST +#define WIFI + +#ifdef LCD_I2C_ON // If I2C LCD enabled +#define I2C_SDA 21 // LCD I2C SDA - GPIO_21 +#define I2C_SCL 22 // LCD I2C SCL - GPIO_22 +#include // I2C Libray +#include // Library LCD with PCF8574 +LiquidCrystal_PCF8574 lcd(0x3F); // Instance LCD I2C - adress x3F +#endif // LCD I2C + +#ifdef LCD_ON // LCD with 4 bits interface enabled +#include // Library LCD +LiquidCrystal lcd(4, 16, 17, 5, 18, 19); // Instance - ports RS,ENA,D4,D5,D6,D7 +#endif +// LCD +#ifdef JFS + #include "SSD1306.h" // alias for `#include "SSD1306Wire.h" // Board Handling + // jfs Wemos lolin32 + // jfs Heltec WiFi kit 32 (weisses Board) + // #define HELTEC + // Initialize the OLED display using Wire library + #ifdef HELTEC + SSD1306 display(0x3c, 4, 15); + #else + SSD1306 display(0x3c, 5, 4); + #endif +#endif + +#ifdef WIFI +#include +const int mxSize=4; +String ssids[mxSize] ={"GAST","pipanet","FRITZ!Box Gastzugang","WLAN-DE8245"}; +String ssidp[mxSize] = {"passatvr6","passatvr6","praxis123","4955065570896956"}; +boolean conok =false; + +void netfound(int i){ + display.clear(); + display.setColor(BLACK); + display.fillRect(0, 0, 128, 10); + display.setColor(WHITE); + display.drawString(0,0,String(i)); + display.drawString(20,0,"networks found"); + display.display(); +} + +boolean init_wifi(){ + boolean ok = false; + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + int n = WiFi.scanNetworks(); + Serial.println("scan done"); + if (n == 0) { + Serial.println("no networks found"); + netfound(0); + } else { + Serial.print(n); + Serial.println(" networks found"); + netfound(n); + for (int i = 0; i < n; ++i) { + for (int p=0;p= radix) + s = ultos_recursive(val / radix, s, radix, pos + 1); + c = val % radix; + c += (c < 10 ? '0' : 'a' - 10); + *s++ = c; + if (pos % 3 == 0) *s++ = ','; + return s; +} +//---------------------------------------------------------------------------------------- +char *ltos(long val, char *s, int radix) // Format an long (32 bits) into a string +{ + if (radix < 2 || radix > 36) { + s[0] = 0; + } else { + char *p = s; + if (radix == 10 && val < 0) { + val = -val; + *p++ = '-'; + } + p = ultos_recursive(val, p, radix, 0) - 1; + *p = 0; + } + return s; +} +#ifdef JFS +uint32_t start = 100000; +void test(){ + if (start < 100){ + start = 100000; + } else { + osc_freq = start; + init_osc_freq (); + start -= 100; + } +} +#endif +//--------------------------------------------------------------------------------- +void loop() +{ + if (flag == true) // If count has ended + { + flag = false; // change flag to disable print + frequency = (pulses + (multPulses * overflow)) / 2 ; // Calculation of frequency +#ifdef JFS + factor = 1000000/sample_time; + frequenz = frequency*factor; // correct frequency if sample_time is smaller then 1sec + Serial.println(frequency); +#else + printf("Frequency : %s", (ltos(frequency, buf, 10))); // Print frequency with commas + printf(" Hz \n"); // Print unity Hz +#endif +#if defined LCD_ON || defined LCD_I2C_ON // If using LCD or I2C LCD + lcd.setCursor(2, 1); // Set cursor position - column and row + lcd.print((ltos(frequency, buf, 10))); // LCD print frequency + lcd.print(" Hz "); // LCD print unity Hz +#endif +#ifdef JFS + display.clear(); +#ifdef WIFI + display.drawString(0,0,"IP : "+WiFi.localIP().toString()); +#endif + display.drawString(0, 10,"sample time ms "); + display.drawString(80,10,String(sample_time/1000)); + display.drawString(0, 20,"Pulses SVP"); + display.drawString(60, 20, (ltos(frequency, buf, 10))); + display.drawString(0, 30,"Freq"); + display.drawString(60, 30, (ltos(frequenz, buf, 10))); + display.drawString(0, 40,"Oscila 16"); + display.drawString(60, 40, (ltos(save_osc, buf, 10))); + display.display(); +#endif + multPulses = 0; // Clear overflow counter + // Put your function here, if you want +#ifdef JFS + #ifdef TEST + test(); + #endif + delay (5); // Delay 100 ms 100 +#else + delay(100); +#endif + // Put your function here, if you want + + pcnt_counter_clear(PCNT_COUNT_UNIT); // Clear Pulse Counter + esp_timer_start_once(timer_handle, sample_time); // Initialize High resolution timer (1 sec) + gpio_set_level(OUTPUT_CONTROL_GPIO, 1); // Set enable PCNT count + } + + String inputString = ""; // clear temporary string + osc_freq = 0; // Clear oscillator frequency + while (Serial.available()) + { + char inChar = (char)Serial.read(); // Reads a byte on the console + inputString += inChar; // Add char to string + if (inChar == '\n') // If new line (enter) + { + Serial.println(inputString); +#ifdef JFS + if (inputString.startsWith("s")){ + inputString.remove(0,1); + Serial.println(inputString); + sample_time = inputString.toInt(); + Serial.println(sample_time); + } + if (inputString.startsWith("o")){ + inputString.remove(0,1); + Serial.println(inputString); + osc_freq = inputString.toInt(); + Serial.println(sample_time); + } +#else + osc_freq = inputString.toInt(); +#endif + // Converts String into integer value + inputString = ""; // Clear string + } + } + if (osc_freq != 0) // If some value inputted to oscillator frequency + { + init_osc_freq (); // reconfigure ledc function - oscillator + } +} diff --git a/libraries/Adafruit_BME280_Library-master/.github/ISSUE_TEMPLATE.md b/libraries/Adafruit_BME280_Library-master/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..f0e2614 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,46 @@ +Thank you for opening an issue on an Adafruit Arduino library repository. To +improve the speed of resolution please review the following guidelines and +common troubleshooting steps below before creating the issue: + +- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use + the forums at http://forums.adafruit.com to ask questions and troubleshoot why + something isn't working as expected. In many cases the problem is a common issue + that you will more quickly receive help from the forum community. GitHub issues + are meant for known defects in the code. If you don't know if there is a defect + in the code then start with troubleshooting on the forum first. + +- **If following a tutorial or guide be sure you didn't miss a step.** Carefully + check all of the steps and commands to run have been followed. Consult the + forum if you're unsure or have questions about steps in a guide/tutorial. + +- **For Arduino projects check these very common issues to ensure they don't apply**: + + - For uploading sketches or communicating with the board make sure you're using + a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes + very hard to tell the difference between a data and charge cable! Try using the + cable with other devices or swapping to another cable to confirm it is not + the problem. + + - **Be sure you are supplying adequate power to the board.** Check the specs of + your board and plug in an external power supply. In many cases just + plugging a board into your computer is not enough to power it and other + peripherals. + + - **Double check all soldering joints and connections.** Flakey connections + cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. + + - **Ensure you are using an official Arduino or Adafruit board.** We can't + guarantee a clone board will have the same functionality and work as expected + with this code and don't support them. + +If you're sure this issue is a defect in the code and checked the steps above +please fill in the following fields to provide enough troubleshooting information. +You may delete the guideline and text above to just leave the following details: + +- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE** + +- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO + VERSION HERE** + +- List the steps to reproduce the problem below (if possible attach a sketch or + copy the sketch code in too): **LIST REPRO STEPS BELOW** diff --git a/libraries/Adafruit_BME280_Library-master/.github/PULL_REQUEST_TEMPLATE.md b/libraries/Adafruit_BME280_Library-master/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..7b641eb --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ +Thank you for creating a pull request to contribute to Adafruit's GitHub code! +Before you open the request please review the following guidelines and tips to +help it be more easily integrated: + +- **Describe the scope of your change--i.e. what the change does and what parts + of the code were modified.** This will help us understand any risks of integrating + the code. + +- **Describe any known limitations with your change.** For example if the change + doesn't apply to a supported platform of the library please mention it. + +- **Please run any tests or examples that can exercise your modified code.** We + strive to not break users of the code and running tests/examples helps with this + process. + +Thank you again for contributing! We will try to test and integrate the change +as soon as we can, but be aware we have many GitHub repositories to manage and +can't immediately respond to every request. There is no need to bump or check in +on a pull request (it will clutter the discussion of the request). + +Also don't be worried if the request is closed or not integrated--sometimes the +priorities of Adafruit's GitHub code (education, ease of use) might not match the +priorities of the pull request. Don't fret, the open source community thrives on +forks and GitHub makes it easy to keep your changes in a forked repo. + +After reviewing the guidelines above you can delete this text from the pull request. diff --git a/libraries/Adafruit_BME280_Library-master/.github/workflows/githubci.yml b/libraries/Adafruit_BME280_Library-master/.github/workflows/githubci.yml new file mode 100644 index 0000000..2df0174 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/.github/workflows/githubci.yml @@ -0,0 +1,32 @@ +name: Arduino Library CI + +on: [pull_request, push, repository_dispatch] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v1 + with: + python-version: '3.x' + - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + with: + repository: adafruit/ci-arduino + path: ci + + - name: pre-install + run: bash ci/actions_install.sh + + - name: test platforms + run: python3 ci/build_platform.py main_platforms + + - name: clang + run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . + + - name: doxygen + env: + GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} + PRETTYNAME : "Adafruit BME280 Library" + run: bash ci/doxy_gen_and_deploy.sh diff --git a/libraries/Adafruit_BME280_Library-master/.gitignore b/libraries/Adafruit_BME280_Library-master/.gitignore new file mode 100644 index 0000000..542d266 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/.gitignore @@ -0,0 +1,8 @@ +# osx +.DS_Store + +# doxygen +Doxyfile* +doxygen_sqlite3.db +html +*.tmp diff --git a/libraries/Adafruit_BME280_Library-master/Adafruit_BME280.cpp b/libraries/Adafruit_BME280_Library-master/Adafruit_BME280.cpp new file mode 100644 index 0000000..f040743 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/Adafruit_BME280.cpp @@ -0,0 +1,727 @@ +/*! + * @file Adafruit_BME280.cpp + * + * @mainpage Adafruit BME280 humidity, temperature & pressure sensor + * + * @section intro_sec Introduction + * + * Driver for the BME280 humidity, temperature & pressure sensor + * + * These sensors use I2C or SPI to communicate, 2 or 4 pins are required + * to interface. + * + * Designed specifically to work with the Adafruit BME280 Breakout + * ----> http://www.adafruit.com/products/2652 + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * @section author Author + * + * Written by Kevin "KTOWN" Townsend for Adafruit Industries. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + * See the LICENSE file for details. + * + */ + +#include "Adafruit_BME280.h" +#include "Arduino.h" +#include +#include + +/*! + * @brief class constructor + */ +Adafruit_BME280::Adafruit_BME280() : _cs(-1), _mosi(-1), _miso(-1), _sck(-1) {} + +/*! + * @brief class constructor if using hardware SPI + * @param cspin the chip select pin to use + * @param *theSPI + * optional SPI object + */ +Adafruit_BME280::Adafruit_BME280(int8_t cspin, SPIClass *theSPI) { + _cs = cspin; + _mosi = _miso = _sck = -1; + _spi = theSPI; +} + +/*! + * @brief class constructor if using software SPI + * @param cspin the chip select pin to use + * @param mosipin the MOSI pin to use + * @param misopin the MISO pin to use + * @param sckpin the SCK pin to use + */ +Adafruit_BME280::Adafruit_BME280(int8_t cspin, int8_t mosipin, int8_t misopin, + int8_t sckpin) + : _cs(cspin), _mosi(mosipin), _miso(misopin), _sck(sckpin) {} + +Adafruit_BME280::~Adafruit_BME280(void) { + if (temp_sensor) { + delete temp_sensor; + } + if (pressure_sensor) { + delete pressure_sensor; + } + if (humidity_sensor) { + delete humidity_sensor; + } +} + +/*! + * @brief Initialise sensor with given parameters / settings + * @param addr the I2C address the device can be found on + * @param theWire the I2C object to use, defaults to &Wire + * @returns true on success, false otherwise + */ +bool Adafruit_BME280::begin(uint8_t addr, TwoWire *theWire) { + bool status = false; + _i2caddr = addr; + _wire = theWire; + status = init(); + + return status; +} + +/*! + * @brief Initialise sensor with given parameters / settings + * @returns true on success, false otherwise + */ +bool Adafruit_BME280::init() { + // init I2C or SPI sensor interface + if (_cs == -1) { + // I2C + _wire->begin(); + } else { + digitalWrite(_cs, HIGH); + pinMode(_cs, OUTPUT); + if (_sck == -1) { + // hardware SPI + _spi->begin(); + } else { + // software SPI + pinMode(_sck, OUTPUT); + pinMode(_mosi, OUTPUT); + pinMode(_miso, INPUT); + } + } + + // check if sensor, i.e. the chip ID is correct + _sensorID = read8(BME280_REGISTER_CHIPID); + if (_sensorID != 0x60) + return false; + + // reset the device using soft-reset + // this makes sure the IIR is off, etc. + write8(BME280_REGISTER_SOFTRESET, 0xB6); + + // wait for chip to wake up. + delay(10); + + // if chip is still reading calibration, delay + while (isReadingCalibration()) + delay(10); + + readCoefficients(); // read trimming parameters, see DS 4.2.2 + + setSampling(); // use defaults + + delay(100); + + return true; +} + +/*! + * @brief setup sensor with given parameters / settings + * + * This is simply a overload to the normal begin()-function, so SPI users + * don't get confused about the library requiring an address. + * @param mode the power mode to use for the sensor + * @param tempSampling the temp samping rate to use + * @param pressSampling the pressure sampling rate to use + * @param humSampling the humidity sampling rate to use + * @param filter the filter mode to use + * @param duration the standby duration to use + */ +void Adafruit_BME280::setSampling(sensor_mode mode, + sensor_sampling tempSampling, + sensor_sampling pressSampling, + sensor_sampling humSampling, + sensor_filter filter, + standby_duration duration) { + _measReg.mode = mode; + _measReg.osrs_t = tempSampling; + _measReg.osrs_p = pressSampling; + + _humReg.osrs_h = humSampling; + _configReg.filter = filter; + _configReg.t_sb = duration; + + // making sure sensor is in sleep mode before setting configuration + // as it otherwise may be ignored + write8(BME280_REGISTER_CONTROL, MODE_SLEEP); + + // you must make sure to also set REGISTER_CONTROL after setting the + // CONTROLHUMID register, otherwise the values won't be applied (see + // DS 5.4.3) + write8(BME280_REGISTER_CONTROLHUMID, _humReg.get()); + write8(BME280_REGISTER_CONFIG, _configReg.get()); + write8(BME280_REGISTER_CONTROL, _measReg.get()); +} + +/*! + * @brief Encapsulate hardware and software SPI transfer into one + * function + * @param x the data byte to transfer + * @returns the data byte read from the device + */ +uint8_t Adafruit_BME280::spixfer(uint8_t x) { + // hardware SPI + if (_sck == -1) + return _spi->transfer(x); + + // software SPI + uint8_t reply = 0; + for (int i = 7; i >= 0; i--) { + reply <<= 1; + digitalWrite(_sck, LOW); + digitalWrite(_mosi, x & (1 << i)); + digitalWrite(_sck, HIGH); + if (digitalRead(_miso)) + reply |= 1; + } + return reply; +} + +/*! + * @brief Writes an 8 bit value over I2C or SPI + * @param reg the register address to write to + * @param value the value to write to the register + */ +void Adafruit_BME280::write8(byte reg, byte value) { + if (_cs == -1) { + _wire->beginTransmission((uint8_t)_i2caddr); + _wire->write((uint8_t)reg); + _wire->write((uint8_t)value); + _wire->endTransmission(); + } else { + if (_sck == -1) + _spi->beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); + digitalWrite(_cs, LOW); + spixfer(reg & ~0x80); // write, bit 7 low + spixfer(value); + digitalWrite(_cs, HIGH); + if (_sck == -1) + _spi->endTransaction(); // release the SPI bus + } +} + +/*! + * @brief Reads an 8 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the data byte read from the device + */ +uint8_t Adafruit_BME280::read8(byte reg) { + uint8_t value; + + if (_cs == -1) { + _wire->beginTransmission((uint8_t)_i2caddr); + _wire->write((uint8_t)reg); + _wire->endTransmission(); + _wire->requestFrom((uint8_t)_i2caddr, (byte)1); + value = _wire->read(); + } else { + if (_sck == -1) + _spi->beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); + digitalWrite(_cs, LOW); + spixfer(reg | 0x80); // read, bit 7 high + value = spixfer(0); + digitalWrite(_cs, HIGH); + if (_sck == -1) + _spi->endTransaction(); // release the SPI bus + } + return value; +} + +/*! + * @brief Reads a 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16(byte reg) { + uint16_t value; + + if (_cs == -1) { + _wire->beginTransmission((uint8_t)_i2caddr); + _wire->write((uint8_t)reg); + _wire->endTransmission(); + _wire->requestFrom((uint8_t)_i2caddr, (byte)2); + value = (_wire->read() << 8) | _wire->read(); + } else { + if (_sck == -1) + _spi->beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); + digitalWrite(_cs, LOW); + spixfer(reg | 0x80); // read, bit 7 high + value = (spixfer(0) << 8) | spixfer(0); + digitalWrite(_cs, HIGH); + if (_sck == -1) + _spi->endTransaction(); // release the SPI bus + } + + return value; +} + +/*! + * @brief Reads a signed 16 bit little endian value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16_LE(byte reg) { + uint16_t temp = read16(reg); + return (temp >> 8) | (temp << 8); +} + +/*! + * @brief Reads a signed 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); } + +/*! + * @brief Reads a signed little endian 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16_LE(byte reg) { + return (int16_t)read16_LE(reg); +} + +/*! + * @brief Reads a 24 bit value over I2C + * @param reg the register address to read from + * @returns the 24 bit data value read from the device + */ +uint32_t Adafruit_BME280::read24(byte reg) { + uint32_t value; + + if (_cs == -1) { + _wire->beginTransmission((uint8_t)_i2caddr); + _wire->write((uint8_t)reg); + _wire->endTransmission(); + _wire->requestFrom((uint8_t)_i2caddr, (byte)3); + + value = _wire->read(); + value <<= 8; + value |= _wire->read(); + value <<= 8; + value |= _wire->read(); + } else { + if (_sck == -1) + _spi->beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); + digitalWrite(_cs, LOW); + spixfer(reg | 0x80); // read, bit 7 high + + value = spixfer(0); + value <<= 8; + value |= spixfer(0); + value <<= 8; + value |= spixfer(0); + + digitalWrite(_cs, HIGH); + if (_sck == -1) + _spi->endTransaction(); // release the SPI bus + } + + return value; +} + +/*! + * @brief Take a new measurement (only possible in forced mode) + @returns true in case of success else false + */ +bool Adafruit_BME280::takeForcedMeasurement(void) { + bool return_value = false; + // If we are in forced mode, the BME sensor goes back to sleep after each + // measurement and we need to set it to forced mode once at this point, so + // it will take the next measurement and then return to sleep again. + // In normal mode simply does new measurements periodically. + if (_measReg.mode == MODE_FORCED) { + return_value = true; + // set to forced mode, i.e. "take next measurement" + write8(BME280_REGISTER_CONTROL, _measReg.get()); + // Store current time to measure the timeout + uint32_t timeout_start = millis(); + // wait until measurement has been completed, otherwise we would read the + // the values from the last measurement or the timeout occurred after 2 sec. + while (read8(BME280_REGISTER_STATUS) & 0x08) { + // In case of a timeout, stop the while loop + if ((millis() - timeout_start) > 2000) { + return_value = false; + break; + } + delay(1); + } + } + return return_value; +} + +/*! + * @brief Reads the factory-set coefficients + */ +void Adafruit_BME280::readCoefficients(void) { + _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1); + _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2); + _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3); + + _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1); + _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2); + _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3); + _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4); + _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5); + _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6); + _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7); + _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8); + _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9); + + _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1); + _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2); + _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3); + _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) | + (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF); + _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) | + (read8(BME280_REGISTER_DIG_H5) >> 4); + _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6); +} + +/*! + * @brief return true if chip is busy reading cal data + * @returns true if reading calibration, false otherwise + */ +bool Adafruit_BME280::isReadingCalibration(void) { + uint8_t const rStatus = read8(BME280_REGISTER_STATUS); + + return (rStatus & (1 << 0)) != 0; +} + +/*! + * @brief Returns the temperature from the sensor + * @returns the temperature read from the device + */ +float Adafruit_BME280::readTemperature(void) { + int32_t var1, var2; + + int32_t adc_T = read24(BME280_REGISTER_TEMPDATA); + if (adc_T == 0x800000) // value in case temp measurement was disabled + return NAN; + adc_T >>= 4; + + var1 = ((((adc_T >> 3) - ((int32_t)_bme280_calib.dig_T1 << 1))) * + ((int32_t)_bme280_calib.dig_T2)) >> + 11; + + var2 = (((((adc_T >> 4) - ((int32_t)_bme280_calib.dig_T1)) * + ((adc_T >> 4) - ((int32_t)_bme280_calib.dig_T1))) >> + 12) * + ((int32_t)_bme280_calib.dig_T3)) >> + 14; + + t_fine = var1 + var2 + t_fine_adjust; + + float T = (t_fine * 5 + 128) >> 8; + return T / 100; +} + +/*! + * @brief Returns the pressure from the sensor + * @returns the pressure value (in Pascal) read from the device + */ +float Adafruit_BME280::readPressure(void) { + int64_t var1, var2, p; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA); + if (adc_P == 0x800000) // value in case pressure measurement was disabled + return NAN; + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; + var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) << 17); + var2 = var2 + (((int64_t)_bme280_calib.dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) >> 8) + + ((var1 * (int64_t)_bme280_calib.dig_P2) << 12); + var1 = + (((((int64_t)1) << 47) + var1)) * ((int64_t)_bme280_calib.dig_P1) >> 33; + + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)_bme280_calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)_bme280_calib.dig_P8) * p) >> 19; + + p = ((p + var1 + var2) >> 8) + (((int64_t)_bme280_calib.dig_P7) << 4); + return (float)p / 256; +} + +/*! + * @brief Returns the humidity from the sensor + * @returns the humidity value read from the device + */ +float Adafruit_BME280::readHumidity(void) { + readTemperature(); // must be done first to get t_fine + + int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); + if (adc_H == 0x8000) // value in case humidity measurement was disabled + return NAN; + + int32_t v_x1_u32r; + + v_x1_u32r = (t_fine - ((int32_t)76800)); + + v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bme280_calib.dig_H4) << 20) - + (((int32_t)_bme280_calib.dig_H5) * v_x1_u32r)) + + ((int32_t)16384)) >> + 15) * + (((((((v_x1_u32r * ((int32_t)_bme280_calib.dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)_bme280_calib.dig_H3)) >> 11) + + ((int32_t)32768))) >> + 10) + + ((int32_t)2097152)) * + ((int32_t)_bme280_calib.dig_H2) + + 8192) >> + 14)); + + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)_bme280_calib.dig_H1)) >> + 4)); + + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + float h = (v_x1_u32r >> 12); + return h / 1024.0; +} + +/*! + * Calculates the altitude (in meters) from the specified atmospheric + * pressure (in hPa), and sea-level pressure (in hPa). + * @param seaLevel Sea-level pressure in hPa + * @returns the altitude value read from the device + */ +float Adafruit_BME280::readAltitude(float seaLevel) { + // Equation taken from BMP180 datasheet (page 16): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + float atmospheric = readPressure() / 100.0F; + return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); +} + +/*! + * Calculates the pressure at sea level (in hPa) from the specified + * altitude (in meters), and atmospheric pressure (in hPa). + * @param altitude Altitude in meters + * @param atmospheric Atmospheric pressure in hPa + * @returns the pressure at sea level (in hPa) from the specified altitude + */ +float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) { + // Equation taken from BMP180 datasheet (page 17): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255); +} + +/*! + * Returns Sensor ID found by init() for diagnostics + * @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280 + */ +uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; } + +/*! + * Returns the current temperature compensation value in degrees Celcius + * @returns the current temperature compensation value in degrees Celcius + */ +float Adafruit_BME280::getTemperatureCompensation(void) { + return float(((t_fine_adjust * 5) >> 8) / 100); +}; + +/*! + * Sets a value to be added to each temperature reading. This adjusted + * temperature is used in pressure and humidity readings. + * @param adjustment Value to be added to each tempature reading in Celcius + */ +void Adafruit_BME280::setTemperatureCompensation(float adjustment) { + // convert the value in C into and adjustment to t_fine + t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5; +}; + +/*! + @brief Gets an Adafruit Unified Sensor object for the temp sensor component + @return Adafruit_Sensor pointer to temperature sensor + */ +Adafruit_Sensor *Adafruit_BME280::getTemperatureSensor(void) { + if (!temp_sensor) { + temp_sensor = new Adafruit_BME280_Temp(this); + } + + return temp_sensor; +} + +/*! + @brief Gets an Adafruit Unified Sensor object for the pressure sensor + component + @return Adafruit_Sensor pointer to pressure sensor + */ +Adafruit_Sensor *Adafruit_BME280::getPressureSensor(void) { + if (!pressure_sensor) { + pressure_sensor = new Adafruit_BME280_Pressure(this); + } + return pressure_sensor; +} + +/*! + @brief Gets an Adafruit Unified Sensor object for the humidity sensor + component + @return Adafruit_Sensor pointer to humidity sensor + */ +Adafruit_Sensor *Adafruit_BME280::getHumiditySensor(void) { + if (!humidity_sensor) { + humidity_sensor = new Adafruit_BME280_Humidity(this); + } + return humidity_sensor; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's temperature sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + sensor->min_delay = 0; + sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C */ + sensor->max_value = +85.0; + sensor->resolution = 0.01; /* 0.01 C */ +} + +/**************************************************************************/ +/*! + @brief Gets the temperature as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + event->timestamp = millis(); + event->temperature = _theBME280->readTemperature(); + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's pressure sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_PRESSURE; + sensor->min_delay = 0; + sensor->min_value = 300.0; /* 300 ~ 1100 hPa */ + sensor->max_value = 1100.0; + sensor->resolution = 0.012; /* 0.12 hPa relative */ +} + +/**************************************************************************/ +/*! + @brief Gets the pressure as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_PRESSURE; + event->timestamp = millis(); + event->pressure = _theBME280->readPressure() / 100; // convert Pa to hPa + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's humidity sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + sensor->min_delay = 0; + sensor->min_value = 0; + sensor->max_value = 100; /* 0 - 100 % */ + sensor->resolution = 3; /* 3% accuracy */ +} + +/**************************************************************************/ +/*! + @brief Gets the humidity as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + event->timestamp = millis(); + event->relative_humidity = _theBME280->readHumidity(); + return true; +} diff --git a/libraries/Adafruit_BME280_Library-master/Adafruit_BME280.h b/libraries/Adafruit_BME280_Library-master/Adafruit_BME280.h new file mode 100644 index 0000000..921a996 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/Adafruit_BME280.h @@ -0,0 +1,378 @@ +/*! + * @file Adafruit_BME280.h + * + * Designed specifically to work with the Adafruit BME280 Breakout + * ----> http://www.adafruit.com/products/2650 + * + * These sensors use I2C or SPI to communicate, 2 or 4 pins are required + * to interface. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Kevin "KTOWN" Townsend for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * See the LICENSE file for details. + * + */ + +#ifndef __BME280_H__ +#define __BME280_H__ + +#include "Arduino.h" + +#include +#include +#include + +/*! + * @brief default I2C address + */ +#define BME280_ADDRESS (0x77) // Primary I2C Address + /*! + * @brief alternate I2C address + */ +#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address + +/*! + * @brief Register addresses + */ +enum { + BME280_REGISTER_DIG_T1 = 0x88, + BME280_REGISTER_DIG_T2 = 0x8A, + BME280_REGISTER_DIG_T3 = 0x8C, + + BME280_REGISTER_DIG_P1 = 0x8E, + BME280_REGISTER_DIG_P2 = 0x90, + BME280_REGISTER_DIG_P3 = 0x92, + BME280_REGISTER_DIG_P4 = 0x94, + BME280_REGISTER_DIG_P5 = 0x96, + BME280_REGISTER_DIG_P6 = 0x98, + BME280_REGISTER_DIG_P7 = 0x9A, + BME280_REGISTER_DIG_P8 = 0x9C, + BME280_REGISTER_DIG_P9 = 0x9E, + + BME280_REGISTER_DIG_H1 = 0xA1, + BME280_REGISTER_DIG_H2 = 0xE1, + BME280_REGISTER_DIG_H3 = 0xE3, + BME280_REGISTER_DIG_H4 = 0xE4, + BME280_REGISTER_DIG_H5 = 0xE5, + BME280_REGISTER_DIG_H6 = 0xE7, + + BME280_REGISTER_CHIPID = 0xD0, + BME280_REGISTER_VERSION = 0xD1, + BME280_REGISTER_SOFTRESET = 0xE0, + + BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 + + BME280_REGISTER_CONTROLHUMID = 0xF2, + BME280_REGISTER_STATUS = 0XF3, + BME280_REGISTER_CONTROL = 0xF4, + BME280_REGISTER_CONFIG = 0xF5, + BME280_REGISTER_PRESSUREDATA = 0xF7, + BME280_REGISTER_TEMPDATA = 0xFA, + BME280_REGISTER_HUMIDDATA = 0xFD +}; + +/**************************************************************************/ +/*! + @brief calibration data +*/ +/**************************************************************************/ +typedef struct { + uint16_t dig_T1; ///< temperature compensation value + int16_t dig_T2; ///< temperature compensation value + int16_t dig_T3; ///< temperature compensation value + + uint16_t dig_P1; ///< pressure compensation value + int16_t dig_P2; ///< pressure compensation value + int16_t dig_P3; ///< pressure compensation value + int16_t dig_P4; ///< pressure compensation value + int16_t dig_P5; ///< pressure compensation value + int16_t dig_P6; ///< pressure compensation value + int16_t dig_P7; ///< pressure compensation value + int16_t dig_P8; ///< pressure compensation value + int16_t dig_P9; ///< pressure compensation value + + uint8_t dig_H1; ///< humidity compensation value + int16_t dig_H2; ///< humidity compensation value + uint8_t dig_H3; ///< humidity compensation value + int16_t dig_H4; ///< humidity compensation value + int16_t dig_H5; ///< humidity compensation value + int8_t dig_H6; ///< humidity compensation value +} bme280_calib_data; +/*=========================================================================*/ + +class Adafruit_BME280; + +/** Adafruit Unified Sensor interface for temperature component of BME280 */ +class Adafruit_BME280_Temp : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the temp sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Temp(Adafruit_BME280 *parent) { _theBME280 = parent; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID = 280; + Adafruit_BME280 *_theBME280 = NULL; +}; + +/** Adafruit Unified Sensor interface for pressure component of BME280 */ +class Adafruit_BME280_Pressure : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Pressure(Adafruit_BME280 *parent) { _theBME280 = parent; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID = 280; + Adafruit_BME280 *_theBME280 = NULL; +}; + +/** Adafruit Unified Sensor interface for humidity component of BME280 */ +class Adafruit_BME280_Humidity : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Humidity(Adafruit_BME280 *parent) { _theBME280 = parent; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID = 280; + Adafruit_BME280 *_theBME280 = NULL; +}; + +/**************************************************************************/ +/*! + @brief Class that stores state and functions for interacting with BME280 IC +*/ +/**************************************************************************/ +class Adafruit_BME280 { +public: + /**************************************************************************/ + /*! + @brief sampling rates + */ + /**************************************************************************/ + enum sensor_sampling { + SAMPLING_NONE = 0b000, + SAMPLING_X1 = 0b001, + SAMPLING_X2 = 0b010, + SAMPLING_X4 = 0b011, + SAMPLING_X8 = 0b100, + SAMPLING_X16 = 0b101 + }; + + /**************************************************************************/ + /*! + @brief power modes + */ + /**************************************************************************/ + enum sensor_mode { + MODE_SLEEP = 0b00, + MODE_FORCED = 0b01, + MODE_NORMAL = 0b11 + }; + + /**************************************************************************/ + /*! + @brief filter values + */ + /**************************************************************************/ + enum sensor_filter { + FILTER_OFF = 0b000, + FILTER_X2 = 0b001, + FILTER_X4 = 0b010, + FILTER_X8 = 0b011, + FILTER_X16 = 0b100 + }; + + /**************************************************************************/ + /*! + @brief standby duration in ms + */ + /**************************************************************************/ + enum standby_duration { + STANDBY_MS_0_5 = 0b000, + STANDBY_MS_10 = 0b110, + STANDBY_MS_20 = 0b111, + STANDBY_MS_62_5 = 0b001, + STANDBY_MS_125 = 0b010, + STANDBY_MS_250 = 0b011, + STANDBY_MS_500 = 0b100, + STANDBY_MS_1000 = 0b101 + }; + + // constructors + Adafruit_BME280(); + Adafruit_BME280(int8_t cspin, SPIClass *theSPI = &SPI); + Adafruit_BME280(int8_t cspin, int8_t mosipin, int8_t misopin, int8_t sckpin); + ~Adafruit_BME280(void); + bool begin(uint8_t addr = BME280_ADDRESS, TwoWire *theWire = &Wire); + bool init(); + + void setSampling(sensor_mode mode = MODE_NORMAL, + sensor_sampling tempSampling = SAMPLING_X16, + sensor_sampling pressSampling = SAMPLING_X16, + sensor_sampling humSampling = SAMPLING_X16, + sensor_filter filter = FILTER_OFF, + standby_duration duration = STANDBY_MS_0_5); + + bool takeForcedMeasurement(void); + float readTemperature(void); + float readPressure(void); + float readHumidity(void); + + float readAltitude(float seaLevel); + float seaLevelForAltitude(float altitude, float pressure); + uint32_t sensorID(void); + + float getTemperatureCompensation(void); + void setTemperatureCompensation(float); + + Adafruit_Sensor *getTemperatureSensor(void); + Adafruit_Sensor *getPressureSensor(void); + Adafruit_Sensor *getHumiditySensor(void); + +protected: + TwoWire *_wire; //!< pointer to a TwoWire object + SPIClass *_spi; //!< pointer to SPI object + + Adafruit_BME280_Temp *temp_sensor = NULL; + //!< Adafruit_Sensor compat temperature sensor component + + Adafruit_BME280_Pressure *pressure_sensor = NULL; + //!< Adafruit_Sensor compat pressure sensor component + + Adafruit_BME280_Humidity *humidity_sensor = NULL; + //!< Adafruit_Sensor compat humidity sensor component + + void readCoefficients(void); + bool isReadingCalibration(void); + uint8_t spixfer(uint8_t x); + + void write8(byte reg, byte value); + uint8_t read8(byte reg); + uint16_t read16(byte reg); + uint32_t read24(byte reg); + int16_t readS16(byte reg); + uint16_t read16_LE(byte reg); // little endian + int16_t readS16_LE(byte reg); // little endian + + uint8_t _i2caddr; //!< I2C addr for the TwoWire interface + int32_t _sensorID; //!< ID of the BME Sensor + int32_t t_fine; //!< temperature with high resolution, stored as an attribute + //!< as this is used for temperature compensation reading + //!< humidity and pressure + + int8_t _cs; //!< for the SPI interface + int8_t _mosi; //!< for the SPI interface + int8_t _miso; //!< for the SPI interface + int8_t _sck; //!< for the SPI interface + + int32_t t_fine_adjust = 0; //!< add to compensate temp readings and in turn + //!< to pressure and humidity readings + + bme280_calib_data _bme280_calib; //!< here calibration data is stored + + /**************************************************************************/ + /*! + @brief config register + */ + /**************************************************************************/ + struct config { + // inactive duration (standby time) in normal mode + // 000 = 0.5 ms + // 001 = 62.5 ms + // 010 = 125 ms + // 011 = 250 ms + // 100 = 500 ms + // 101 = 1000 ms + // 110 = 10 ms + // 111 = 20 ms + unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode + + // filter settings + // 000 = filter off + // 001 = 2x filter + // 010 = 4x filter + // 011 = 8x filter + // 100 and above = 16x filter + unsigned int filter : 3; ///< filter settings + + // unused - don't set + unsigned int none : 1; ///< unused - don't set + unsigned int spi3w_en : 1; ///< unused - don't set + + /// @return combined config register + unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; } + }; + config _configReg; //!< config register object + + /**************************************************************************/ + /*! + @brief ctrl_meas register + */ + /**************************************************************************/ + struct ctrl_meas { + // temperature oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_t : 3; ///< temperature oversampling + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_p : 3; ///< pressure oversampling + + // device mode + // 00 = sleep + // 01 or 10 = forced + // 11 = normal + unsigned int mode : 2; ///< device mode + + /// @return combined ctrl register + unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; } + }; + ctrl_meas _measReg; //!< measurement register object + + /**************************************************************************/ + /*! + @brief ctrl_hum register + */ + /**************************************************************************/ + struct ctrl_hum { + /// unused - don't set + unsigned int none : 5; + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_h : 3; ///< pressure oversampling + + /// @return combined ctrl hum register + unsigned int get() { return (osrs_h); } + }; + ctrl_hum _humReg; //!< hum register object +}; + +#endif diff --git a/libraries/Adafruit_BME280_Library-master/LICENSE b/libraries/Adafruit_BME280_Library-master/LICENSE new file mode 100644 index 0000000..f4db09c --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015, Limor Fried & Kevin Townsend for Adafruit Industries +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Adafruit Industries nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/libraries/Adafruit_BME280_Library-master/README.md b/libraries/Adafruit_BME280_Library-master/README.md new file mode 100644 index 0000000..039e969 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/README.md @@ -0,0 +1,54 @@ +# Adafruit BME280 Library [![Build Status](https://github.com/adafruit/Adafruit_BME280_Library/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_BME280_Library/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_BME280_Library/html/index.html) + + + + +This is a library for the Adafruit BME280 Humidity, Barometric Pressure + Temp sensor + +Designed specifically to work with the Adafruit BME280 Breakout + * http://www.adafruit.com/products/2652 + +Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! + +# Installation +To install, use the Arduino Library Manager and search for "Adafruit BME280" and install the library. + +## Dependencies + * [Adafruit Unified Sensor Driver](https://github.com/adafruit/Adafruit_Sensor) + +# Contributing + +Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit_PM25AQI/blob/master/CODE_OF_CONDUCT.md>) +before contributing to help this project stay welcoming. + +## Documentation and doxygen +Documentation is produced by doxygen. Contributions should include documentation for any new code added. + +Some examples of how to use doxygen can be found in these guide pages: + +https://learn.adafruit.com/the-well-automated-arduino-library/doxygen + +https://learn.adafruit.com/the-well-automated-arduino-library/doxygen-tips + +## Formatting and clang-format +This library uses [`clang-format`](https://releases.llvm.org/download.html) to standardize the formatting of `.cpp` and `.h` files. +Contributions should be formatted using `clang-format`: + +The `-i` flag will make the changes to the file. +```bash +clang-format -i *.cpp *.h +``` +If you prefer to make the changes yourself, running `clang-format` without the `-i` flag will print out a formatted version of the file. You can save this to a file and diff it against the original to see the changes. + +Note that the formatting output by `clang-format` is what the automated formatting checker will expect. Any diffs from this formatting will result in a failed build until they are addressed. Using the `-i` flag is highly recommended. + +### clang-format resources + * [Binary builds and source available on the LLVM downloads page](https://releases.llvm.org/download.html) + * [Documentation and IDE integration](https://clang.llvm.org/docs/ClangFormat.html) + +## About this Driver +Written by Ladyada for Adafruit Industries. + +BSD license, check license.txt for more information + +All text above must be included in any redistribution diff --git a/libraries/Adafruit_BME280_Library-master/assets/board.jpg b/libraries/Adafruit_BME280_Library-master/assets/board.jpg new file mode 100644 index 0000000..581f4ba Binary files /dev/null and b/libraries/Adafruit_BME280_Library-master/assets/board.jpg differ diff --git a/libraries/Adafruit_BME280_Library-master/examples/advancedsettings/advancedsettings.ino b/libraries/Adafruit_BME280_Library-master/examples/advancedsettings/advancedsettings.ino new file mode 100644 index 0000000..df1f5eb --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/examples/advancedsettings/advancedsettings.ino @@ -0,0 +1,159 @@ +/*************************************************************************** + This is a library for the BME280 humidity, temperature & pressure sensor + + Designed specifically to work with the Adafruit BME280 Breakout + ----> http://www.adafruit.com/products/2650 + + These sensors use I2C or SPI to communicate, 2 or 4 pins are required + to interface. The device's I2C address is either 0x76 or 0x77. + + Adafruit invests time and resources providing this open source code, + please support Adafruit andopen-source hardware by purchasing products + from Adafruit! + + Written by Limor Fried & Kevin Townsend for Adafruit Industries. + BSD license, all text above must be included in any redistribution + See the LICENSE file for details. + ***************************************************************************/ + +#include +#include +#include +#include + +#define BME_SCK 13 +#define BME_MISO 12 +#define BME_MOSI 11 +#define BME_CS 10 + +#define SEALEVELPRESSURE_HPA (1013.25) + +Adafruit_BME280 bme; // I2C +//Adafruit_BME280 bme(BME_CS); // hardware SPI +//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI + +unsigned long delayTime; + +void setup() { + Serial.begin(9600); + Serial.println(F("BME280 test")); + + if (! bme.begin(0x77, &Wire)) { + Serial.println("Could not find a valid BME280 sensor, check wiring!"); + while (1); + } + + Serial.println("-- Default Test --"); + Serial.println("normal mode, 16x oversampling for all, filter off,"); + Serial.println("0.5ms standby period"); + delayTime = 5000; + + + // For more details on the following scenarious, see chapter + // 3.5 "Recommended modes of operation" in the datasheet + +/* + // weather monitoring + Serial.println("-- Weather Station Scenario --"); + Serial.println("forced mode, 1x temperature / 1x humidity / 1x pressure oversampling,"); + Serial.println("filter off"); + bme.setSampling(Adafruit_BME280::MODE_FORCED, + Adafruit_BME280::SAMPLING_X1, // temperature + Adafruit_BME280::SAMPLING_X1, // pressure + Adafruit_BME280::SAMPLING_X1, // humidity + Adafruit_BME280::FILTER_OFF ); + + // suggested rate is 1/60Hz (1m) + delayTime = 60000; // in milliseconds +*/ + +/* + // humidity sensing + Serial.println("-- Humidity Sensing Scenario --"); + Serial.println("forced mode, 1x temperature / 1x humidity / 0x pressure oversampling"); + Serial.println("= pressure off, filter off"); + bme.setSampling(Adafruit_BME280::MODE_FORCED, + Adafruit_BME280::SAMPLING_X1, // temperature + Adafruit_BME280::SAMPLING_NONE, // pressure + Adafruit_BME280::SAMPLING_X1, // humidity + Adafruit_BME280::FILTER_OFF ); + + // suggested rate is 1Hz (1s) + delayTime = 1000; // in milliseconds +*/ + +/* + // indoor navigation + Serial.println("-- Indoor Navigation Scenario --"); + Serial.println("normal mode, 16x pressure / 2x temperature / 1x humidity oversampling,"); + Serial.println("0.5ms standby period, filter 16x"); + bme.setSampling(Adafruit_BME280::MODE_NORMAL, + Adafruit_BME280::SAMPLING_X2, // temperature + Adafruit_BME280::SAMPLING_X16, // pressure + Adafruit_BME280::SAMPLING_X1, // humidity + Adafruit_BME280::FILTER_X16, + Adafruit_BME280::STANDBY_MS_0_5 ); + + // suggested rate is 25Hz + // 1 + (2 * T_ovs) + (2 * P_ovs + 0.5) + (2 * H_ovs + 0.5) + // T_ovs = 2 + // P_ovs = 16 + // H_ovs = 1 + // = 40ms (25Hz) + // with standby time that should really be 24.16913... Hz + delayTime = 41; + */ + + /* + // gaming + Serial.println("-- Gaming Scenario --"); + Serial.println("normal mode, 4x pressure / 1x temperature / 0x humidity oversampling,"); + Serial.println("= humidity off, 0.5ms standby period, filter 16x"); + bme.setSampling(Adafruit_BME280::MODE_NORMAL, + Adafruit_BME280::SAMPLING_X1, // temperature + Adafruit_BME280::SAMPLING_X4, // pressure + Adafruit_BME280::SAMPLING_NONE, // humidity + Adafruit_BME280::FILTER_X16, + Adafruit_BME280::STANDBY_MS_0_5 ); + + // Suggested rate is 83Hz + // 1 + (2 * T_ovs) + (2 * P_ovs + 0.5) + // T_ovs = 1 + // P_ovs = 4 + // = 11.5ms + 0.5ms standby + delayTime = 12; +*/ + + Serial.println(); +} + + +void loop() { + // Only needed in forced mode! In normal mode, you can remove the next line. + bme.takeForcedMeasurement(); // has no effect in normal mode + + printValues(); + delay(delayTime); +} + + +void printValues() { + Serial.print("Temperature = "); + Serial.print(bme.readTemperature()); + Serial.println(" *C"); + + Serial.print("Pressure = "); + + Serial.print(bme.readPressure() / 100.0F); + Serial.println(" hPa"); + + Serial.print("Approx. Altitude = "); + Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); + Serial.println(" m"); + + Serial.print("Humidity = "); + Serial.print(bme.readHumidity()); + Serial.println(" %"); + + Serial.println(); +} diff --git a/libraries/Adafruit_BME280_Library-master/examples/bme280_unified/bme280_unified.ino b/libraries/Adafruit_BME280_Library-master/examples/bme280_unified/bme280_unified.ino new file mode 100644 index 0000000..8a67af4 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/examples/bme280_unified/bme280_unified.ino @@ -0,0 +1,62 @@ +/*************************************************************************** + This is a library for the BME280 humidity, temperature & pressure sensor + This example shows how to take Sensor Events instead of direct readings + + Designed specifically to work with the Adafruit BME280 Breakout + ----> http://www.adafruit.com/products/2652 + + These sensors use I2C or SPI to communicate, 2 or 4 pins are required + to interface. + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing products + from Adafruit! + + Written by Limor Fried & Kevin Townsend for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ***************************************************************************/ + +#include +#include +#include + +Adafruit_BME280 bme; // use I2C interface +Adafruit_Sensor *bme_temp = bme.getTemperatureSensor(); +Adafruit_Sensor *bme_pressure = bme.getPressureSensor(); +Adafruit_Sensor *bme_humidity = bme.getHumiditySensor(); + +void setup() { + Serial.begin(9600); + Serial.println(F("BME280 Sensor event test")); + + if (!bme.begin()) { + Serial.println(F("Could not find a valid BME280 sensor, check wiring!")); + while (1) delay(10); + } + + bme_temp->printSensorDetails(); + bme_pressure->printSensorDetails(); + bme_humidity->printSensorDetails(); +} + +void loop() { + sensors_event_t temp_event, pressure_event, humidity_event; + bme_temp->getEvent(&temp_event); + bme_pressure->getEvent(&pressure_event); + bme_humidity->getEvent(&humidity_event); + + Serial.print(F("Temperature = ")); + Serial.print(temp_event.temperature); + Serial.println(" *C"); + + Serial.print(F("Humidity = ")); + Serial.print(humidity_event.relative_humidity); + Serial.println(" %"); + + Serial.print(F("Pressure = ")); + Serial.print(pressure_event.pressure); + Serial.println(" hPa"); + + Serial.println(); + delay(1000); +} \ No newline at end of file diff --git a/libraries/Adafruit_BME280_Library-master/examples/bme280test/bme280test.ino b/libraries/Adafruit_BME280_Library-master/examples/bme280test/bme280test.ino new file mode 100644 index 0000000..45a8c4d --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/examples/bme280test/bme280test.ino @@ -0,0 +1,90 @@ +/*************************************************************************** + This is a library for the BME280 humidity, temperature & pressure sensor + + Designed specifically to work with the Adafruit BME280 Breakout + ----> http://www.adafruit.com/products/2650 + + These sensors use I2C or SPI to communicate, 2 or 4 pins are required + to interface. The device's I2C address is either 0x76 or 0x77. + + Adafruit invests time and resources providing this open source code, + please support Adafruit andopen-source hardware by purchasing products + from Adafruit! + + Written by Limor Fried & Kevin Townsend for Adafruit Industries. + BSD license, all text above must be included in any redistribution + See the LICENSE file for details. + ***************************************************************************/ + +#include +#include +#include +#include + +#define BME_SCK 13 +#define BME_MISO 12 +#define BME_MOSI 11 +#define BME_CS 10 + +#define SEALEVELPRESSURE_HPA (1013.25) + +Adafruit_BME280 bme; // I2C +//Adafruit_BME280 bme(BME_CS); // hardware SPI +//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI + +unsigned long delayTime; + +void setup() { + Serial.begin(9600); + while(!Serial); // time to get serial running + Serial.println(F("BME280 test")); + + unsigned status; + + // default settings + status = bme.begin(); + // You can also pass in a Wire library object like &Wire2 + // status = bme.begin(0x76, &Wire2) + if (!status) { + Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!"); + Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(),16); + Serial.print(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n"); + Serial.print(" ID of 0x56-0x58 represents a BMP 280,\n"); + Serial.print(" ID of 0x60 represents a BME 280.\n"); + Serial.print(" ID of 0x61 represents a BME 680.\n"); + while (1) delay(10); + } + + Serial.println("-- Default Test --"); + delayTime = 1000; + + Serial.println(); +} + + +void loop() { + printValues(); + delay(delayTime); +} + + +void printValues() { + Serial.print("Temperature = "); + Serial.print(bme.readTemperature()); + Serial.println(" *C"); + + Serial.print("Pressure = "); + + Serial.print(bme.readPressure() / 100.0F); + Serial.println(" hPa"); + + Serial.print("Approx. Altitude = "); + Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); + Serial.println(" m"); + + Serial.print("Humidity = "); + Serial.print(bme.readHumidity()); + Serial.println(" %"); + + Serial.println(); +} \ No newline at end of file diff --git a/libraries/Adafruit_BME280_Library-master/library.properties b/libraries/Adafruit_BME280_Library-master/library.properties new file mode 100644 index 0000000..69712d1 --- /dev/null +++ b/libraries/Adafruit_BME280_Library-master/library.properties @@ -0,0 +1,10 @@ +name=Adafruit BME280 Library +version=2.1.2 +author=Adafruit +maintainer=Adafruit +sentence=Arduino library for BME280 sensors. +paragraph=Arduino library for BME280 humidity and pressure sensors. +category=Sensors +url=https://github.com/adafruit/Adafruit_BME280_Library +architectures=* +depends=Adafruit Unified Sensor diff --git a/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.cpp b/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.cpp new file mode 100644 index 0000000..7f8c37a --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.cpp @@ -0,0 +1,312 @@ +#include + +/*! + * @brief Create a register we access over an I2C Device (which defines the + * bus and address) + * @param i2cdevice The I2CDevice to use for underlying I2C access + * @param reg_addr The address pointer value for the I2C/SMBus register, can + * be 8 or 16 bits + * @param width The width of the register data itself, defaults to 1 byte + * @param byteorder The byte order of the register (used when width is > 1), + * defaults to LSBFIRST + * @param address_width The width of the register address itself, defaults + * to 1 byte + */ +Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice, + uint16_t reg_addr, + uint8_t width, + uint8_t byteorder, + uint8_t address_width) { + _i2cdevice = i2cdevice; + _spidevice = NULL; + _addrwidth = address_width; + _address = reg_addr; + _byteorder = byteorder; + _width = width; +} + +/*! + * @brief Create a register we access over an SPI Device (which defines the + * bus and CS pin) + * @param spidevice The SPIDevice to use for underlying SPI access + * @param reg_addr The address pointer value for the SPI register, can + * be 8 or 16 bits + * @param type The method we use to read/write data to SPI (which is not + * as well defined as I2C) + * @param width The width of the register data itself, defaults to 1 byte + * @param byteorder The byte order of the register (used when width is > 1), + * defaults to LSBFIRST + * @param address_width The width of the register address itself, defaults + * to 1 byte + */ +Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_SPIDevice *spidevice, + uint16_t reg_addr, + Adafruit_BusIO_SPIRegType type, + uint8_t width, + uint8_t byteorder, + uint8_t address_width) { + _spidevice = spidevice; + _spiregtype = type; + _i2cdevice = NULL; + _addrwidth = address_width; + _address = reg_addr; + _byteorder = byteorder; + _width = width; +} + +/*! + * @brief Create a register we access over an I2C or SPI Device. This is a + * handy function because we can pass in NULL for the unused interface, allowing + * libraries to mass-define all the registers + * @param i2cdevice The I2CDevice to use for underlying I2C access, if NULL + * we use SPI + * @param spidevice The SPIDevice to use for underlying SPI access, if NULL + * we use I2C + * @param reg_addr The address pointer value for the I2C/SMBus/SPI register, + * can be 8 or 16 bits + * @param type The method we use to read/write data to SPI (which is not + * as well defined as I2C) + * @param width The width of the register data itself, defaults to 1 byte + * @param byteorder The byte order of the register (used when width is > 1), + * defaults to LSBFIRST + * @param address_width The width of the register address itself, defaults + * to 1 byte + */ +Adafruit_BusIO_Register::Adafruit_BusIO_Register( + Adafruit_I2CDevice *i2cdevice, Adafruit_SPIDevice *spidevice, + Adafruit_BusIO_SPIRegType type, uint16_t reg_addr, uint8_t width, + uint8_t byteorder, uint8_t address_width) { + _spidevice = spidevice; + _i2cdevice = i2cdevice; + _spiregtype = type; + _addrwidth = address_width; + _address = reg_addr; + _byteorder = byteorder; + _width = width; +} + +/*! + * @brief Write a buffer of data to the register location + * @param buffer Pointer to data to write + * @param len Number of bytes to write + * @return True on successful write (only really useful for I2C as SPI is + * uncheckable) + */ +bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) { + + uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF), + (uint8_t)(_address >> 8)}; + + if (_i2cdevice) { + return _i2cdevice->write(buffer, len, true, addrbuffer, _addrwidth); + } + if (_spidevice) { + if (_spiregtype == ADDRBIT8_HIGH_TOREAD) { + addrbuffer[0] &= ~0x80; + } + if (_spiregtype == ADDRBIT8_HIGH_TOWRITE) { + addrbuffer[0] |= 0x80; + } + if (_spiregtype == AD8_HIGH_TOREAD_AD7_HIGH_TOINC) { + addrbuffer[0] &= ~0x80; + addrbuffer[0] |= 0x40; + } + return _spidevice->write(buffer, len, addrbuffer, _addrwidth); + } + return false; +} + +/*! + * @brief Write up to 4 bytes of data to the register location + * @param value Data to write + * @param numbytes How many bytes from 'value' to write + * @return True on successful write (only really useful for I2C as SPI is + * uncheckable) + */ +bool Adafruit_BusIO_Register::write(uint32_t value, uint8_t numbytes) { + if (numbytes == 0) { + numbytes = _width; + } + if (numbytes > 4) { + return false; + } + + // store a copy + _cached = value; + + for (int i = 0; i < numbytes; i++) { + if (_byteorder == LSBFIRST) { + _buffer[i] = value & 0xFF; + } else { + _buffer[numbytes - i - 1] = value & 0xFF; + } + value >>= 8; + } + return write(_buffer, numbytes); +} + +/*! + * @brief Read data from the register location. This does not do any error + * checking! + * @return Returns 0xFFFFFFFF on failure, value otherwise + */ +uint32_t Adafruit_BusIO_Register::read(void) { + if (!read(_buffer, _width)) { + return -1; + } + + uint32_t value = 0; + + for (int i = 0; i < _width; i++) { + value <<= 8; + if (_byteorder == LSBFIRST) { + value |= _buffer[_width - i - 1]; + } else { + value |= _buffer[i]; + } + } + + return value; +} + +/*! + * @brief Read cached data from last time we wrote to this register + * @return Returns 0xFFFFFFFF on failure, value otherwise + */ +uint32_t Adafruit_BusIO_Register::readCached(void) { return _cached; } + +/*! + * @brief Read a buffer of data from the register location + * @param buffer Pointer to data to read into + * @param len Number of bytes to read + * @return True on successful write (only really useful for I2C as SPI is + * uncheckable) + */ +bool Adafruit_BusIO_Register::read(uint8_t *buffer, uint8_t len) { + uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF), + (uint8_t)(_address >> 8)}; + + if (_i2cdevice) { + return _i2cdevice->write_then_read(addrbuffer, _addrwidth, buffer, len); + } + if (_spidevice) { + if (_spiregtype == ADDRBIT8_HIGH_TOREAD) { + addrbuffer[0] |= 0x80; + } + if (_spiregtype == ADDRBIT8_HIGH_TOWRITE) { + addrbuffer[0] &= ~0x80; + } + if (_spiregtype == AD8_HIGH_TOREAD_AD7_HIGH_TOINC) { + addrbuffer[0] |= 0x80 | 0x40; + } + return _spidevice->write_then_read(addrbuffer, _addrwidth, buffer, len); + } + return false; +} + +/*! + * @brief Read 2 bytes of data from the register location + * @param value Pointer to uint16_t variable to read into + * @return True on successful write (only really useful for I2C as SPI is + * uncheckable) + */ +bool Adafruit_BusIO_Register::read(uint16_t *value) { + if (!read(_buffer, 2)) { + return false; + } + + if (_byteorder == LSBFIRST) { + *value = _buffer[1]; + *value <<= 8; + *value |= _buffer[0]; + } else { + *value = _buffer[0]; + *value <<= 8; + *value |= _buffer[1]; + } + return true; +} + +/*! + * @brief Read 1 byte of data from the register location + * @param value Pointer to uint8_t variable to read into + * @return True on successful write (only really useful for I2C as SPI is + * uncheckable) + */ +bool Adafruit_BusIO_Register::read(uint8_t *value) { + if (!read(_buffer, 1)) { + return false; + } + + *value = _buffer[0]; + return true; +} + +/*! + * @brief Pretty printer for this register + * @param s The Stream to print to, defaults to &Serial + */ +void Adafruit_BusIO_Register::print(Stream *s) { + uint32_t val = read(); + s->print("0x"); + s->print(val, HEX); +} + +/*! + * @brief Pretty printer for this register + * @param s The Stream to print to, defaults to &Serial + */ +void Adafruit_BusIO_Register::println(Stream *s) { + print(s); + s->println(); +} + +/*! + * @brief Create a slice of the register that we can address without + * touching other bits + * @param reg The Adafruit_BusIO_Register which defines the bus/register + * @param bits The number of bits wide we are slicing + * @param shift The number of bits that our bit-slice is shifted from LSB + */ +Adafruit_BusIO_RegisterBits::Adafruit_BusIO_RegisterBits( + Adafruit_BusIO_Register *reg, uint8_t bits, uint8_t shift) { + _register = reg; + _bits = bits; + _shift = shift; +} + +/*! + * @brief Read 4 bytes of data from the register + * @return data The 4 bytes to read + */ +uint32_t Adafruit_BusIO_RegisterBits::read(void) { + uint32_t val = _register->read(); + val >>= _shift; + return val & ((1 << (_bits)) - 1); +} + +/*! + * @brief Write 4 bytes of data to the register + * @param data The 4 bytes to write + * @return True on successful write (only really useful for I2C as SPI is + * uncheckable) + */ +bool Adafruit_BusIO_RegisterBits::write(uint32_t data) { + uint32_t val = _register->read(); + + // mask off the data before writing + uint32_t mask = (1 << (_bits)) - 1; + data &= mask; + + mask <<= _shift; + val &= ~mask; // remove the current data at that spot + val |= data << _shift; // and add in the new data + + return _register->write(val, _register->width()); +} + +/*! + * @brief The width of the register data, helpful for doing calculations + * @returns The data width used when initializing the register + */ +uint8_t Adafruit_BusIO_Register::width(void) { return _width; } diff --git a/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.h b/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.h new file mode 100644 index 0000000..278ccc8 --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.h @@ -0,0 +1,87 @@ +#include +#include +#include + +#ifndef Adafruit_BusIO_Register_h +#define Adafruit_BusIO_Register_h + +typedef enum _Adafruit_BusIO_SPIRegType { + ADDRBIT8_HIGH_TOREAD = 0, + /*!< + * ADDRBIT8_HIGH_TOREAD + * When reading a register you must actually send the value 0x80 + register + * address to the device. e.g. To read the register 0x0B the register value + * 0x8B is sent and to write 0x0B is sent. + */ + AD8_HIGH_TOREAD_AD7_HIGH_TOINC = 1, + + ADDRBIT8_HIGH_TOWRITE = 2, + /*!< + * ADDRBIT8_HIGH_TOWRITE + * When writing to a register you must actually send the value 0x80 + + * the register address to the device. e.g. To write to the register 0x19 the + * register value 0x99 is sent and to read 0x19 is sent. + */ +} Adafruit_BusIO_SPIRegType; + +/*! + * @brief The class which defines a device register (a location to read/write + * data from) + */ +class Adafruit_BusIO_Register { +public: + Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice, uint16_t reg_addr, + uint8_t width = 1, uint8_t byteorder = LSBFIRST, + uint8_t address_width = 1); + Adafruit_BusIO_Register(Adafruit_SPIDevice *spidevice, uint16_t reg_addr, + Adafruit_BusIO_SPIRegType type, uint8_t width = 1, + uint8_t byteorder = LSBFIRST, + uint8_t address_width = 1); + + Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice, + Adafruit_SPIDevice *spidevice, + Adafruit_BusIO_SPIRegType type, uint16_t reg_addr, + uint8_t width = 1, uint8_t byteorder = LSBFIRST, + uint8_t address_width = 1); + + bool read(uint8_t *buffer, uint8_t len); + bool read(uint8_t *value); + bool read(uint16_t *value); + uint32_t read(void); + uint32_t readCached(void); + bool write(uint8_t *buffer, uint8_t len); + bool write(uint32_t value, uint8_t numbytes = 0); + + uint8_t width(void); + + void print(Stream *s = &Serial); + void println(Stream *s = &Serial); + +private: + Adafruit_I2CDevice *_i2cdevice; + Adafruit_SPIDevice *_spidevice; + Adafruit_BusIO_SPIRegType _spiregtype; + uint16_t _address; + uint8_t _width, _addrwidth, _byteorder; + uint8_t _buffer[4]; // we wont support anything larger than uint32 for + // non-buffered read + uint32_t _cached = 0; +}; + +/*! + * @brief The class which defines a slice of bits from within a device register + * (a location to read/write data from) + */ +class Adafruit_BusIO_RegisterBits { +public: + Adafruit_BusIO_RegisterBits(Adafruit_BusIO_Register *reg, uint8_t bits, + uint8_t shift); + bool write(uint32_t value); + uint32_t read(void); + +private: + Adafruit_BusIO_Register *_register; + uint8_t _bits, _shift; +}; + +#endif // BusIO_Register_h diff --git a/libraries/Adafruit_BusIO/Adafruit_I2CDevice.cpp b/libraries/Adafruit_BusIO/Adafruit_I2CDevice.cpp new file mode 100644 index 0000000..f94b1ee --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_I2CDevice.cpp @@ -0,0 +1,236 @@ +#include +#include + +//#define DEBUG_SERIAL Serial + +/*! + * @brief Create an I2C device at a given address + * @param addr The 7-bit I2C address for the device + * @param theWire The I2C bus to use, defaults to &Wire + */ +Adafruit_I2CDevice::Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire) { + _addr = addr; + _wire = theWire; + _begun = false; +#ifdef ARDUINO_ARCH_SAMD + _maxBufferSize = 250; // as defined in Wire.h's RingBuffer +#else + _maxBufferSize = 32; +#endif +} + +/*! + * @brief Initializes and does basic address detection + * @param addr_detect Whether we should attempt to detect the I2C address + * with a scan. 99% of sensors/devices don't mind but once in a while, they spaz + * on a scan! + * @return True if I2C initialized and a device with the addr found + */ +bool Adafruit_I2CDevice::begin(bool addr_detect) { + _wire->begin(); + _begun = true; + + if (addr_detect) { + return detected(); + } + return true; +} + +/*! + * @brief Scans I2C for the address - note will give a false-positive + * if there's no pullups on I2C + * @return True if I2C initialized and a device with the addr found + */ +bool Adafruit_I2CDevice::detected(void) { + // Init I2C if not done yet + if (!_begun && !begin()) { + return false; + } + + // A basic scanner, see if it ACK's + _wire->beginTransmission(_addr); + if (_wire->endTransmission() == 0) { + return true; + } + return false; +} + +/*! + * @brief Write a buffer or two to the I2C device. Cannot be more than + * maxBufferSize() bytes. + * @param buffer Pointer to buffer of data to write. This is const to + * ensure the content of this buffer doesn't change. + * @param len Number of bytes from buffer to write + * @param prefix_buffer Pointer to optional array of data to write before + * buffer. Cannot be more than maxBufferSize() bytes. This is const to + * ensure the content of this buffer doesn't change. + * @param prefix_len Number of bytes from prefix buffer to write + * @param stop Whether to send an I2C STOP signal on write + * @return True if write was successful, otherwise false. + */ +bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop, + const uint8_t *prefix_buffer, + size_t prefix_len) { + if ((len + prefix_len) > maxBufferSize()) { + // currently not guaranteed to work if more than 32 bytes! + // we will need to find out if some platforms have larger + // I2C buffer sizes :/ +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice could not write such a large buffer")); +#endif + return false; + } + + _wire->beginTransmission(_addr); + + // Write the prefix data (usually an address) + if ((prefix_len != 0) && (prefix_buffer != NULL)) { + if (_wire->write(prefix_buffer, prefix_len) != prefix_len) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice failed to write")); +#endif + return false; + } + } + + // Write the data itself + if (_wire->write(buffer, len) != len) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice failed to write")); +#endif + return false; + } + +#ifdef DEBUG_SERIAL + + DEBUG_SERIAL.print(F("\tI2CWRITE @ 0x")); + DEBUG_SERIAL.print(_addr, HEX); + DEBUG_SERIAL.print(F(" :: ")); + if ((prefix_len != 0) && (prefix_buffer != NULL)) { + for (uint16_t i = 0; i < prefix_len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(prefix_buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + } + } + for (uint16_t i = 0; i < len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (i % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif + +#ifdef DEBUG_SERIAL + // DEBUG_SERIAL.print("Stop: "); DEBUG_SERIAL.println(stop); +#endif + + if (_wire->endTransmission(stop) == 0) { +#ifdef DEBUG_SERIAL + // DEBUG_SERIAL.println("Sent!"); +#endif + return true; + } else { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("Failed to send!"); +#endif + return false; + } +} + +/*! + * @brief Read from I2C into a buffer from the I2C device. + * Cannot be more than maxBufferSize() bytes. + * @param buffer Pointer to buffer of data to read into + * @param len Number of bytes from buffer to read. + * @param stop Whether to send an I2C STOP signal on read + * @return True if read was successful, otherwise false. + */ +bool Adafruit_I2CDevice::read(uint8_t *buffer, size_t len, bool stop) { + if (len > maxBufferSize()) { + // currently not guaranteed to work if more than 32 bytes! + // we will need to find out if some platforms have larger + // I2C buffer sizes :/ +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println(F("\tI2CDevice could not read such a large buffer")); +#endif + return false; + } + + size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop); + if (recv != len) { + // Not enough data available to fulfill our obligation! +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tI2CDevice did not receive enough data: ")); + DEBUG_SERIAL.println(recv); +#endif + return false; + } + + for (uint16_t i = 0; i < len; i++) { + buffer[i] = _wire->read(); + } + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tI2CREAD @ 0x")); + DEBUG_SERIAL.print(_addr, HEX); + DEBUG_SERIAL.print(F(" :: ")); + for (uint16_t i = 0; i < len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (len % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif + + return true; +} + +/*! + * @brief Write some data, then read some data from I2C into another buffer. + * Cannot be more than maxBufferSize() bytes. The buffers can point to + * same/overlapping locations. + * @param write_buffer Pointer to buffer of data to write from + * @param write_len Number of bytes from buffer to write. + * @param read_buffer Pointer to buffer of data to read into. + * @param read_len Number of bytes from buffer to read. + * @param stop Whether to send an I2C STOP signal between the write and read + * @return True if write & read was successful, otherwise false. + */ +bool Adafruit_I2CDevice::write_then_read(const uint8_t *write_buffer, + size_t write_len, uint8_t *read_buffer, + size_t read_len, bool stop) { + if (!write(write_buffer, write_len, stop)) { + return false; + } + + return read(read_buffer, read_len); +} + +/*! + * @brief Returns the 7-bit address of this device + * @return The 7-bit address of this device + */ +uint8_t Adafruit_I2CDevice::address(void) { return _addr; } + +/*! + * @brief Change the I2C clock speed to desired (relies on + * underlying Wire support! + * @param desiredclk The desired I2C SCL frequency + * @return True if this platform supports changing I2C speed. + * Not necessarily that the speed was achieved! + */ +bool Adafruit_I2CDevice::setSpeed(uint32_t desiredclk) { +#if (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) + _wire->setClock(desiredclk); + return true; +#else + return false; +#endif +} diff --git a/libraries/Adafruit_BusIO/Adafruit_I2CDevice.h b/libraries/Adafruit_BusIO/Adafruit_I2CDevice.h new file mode 100644 index 0000000..28468ea --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_I2CDevice.h @@ -0,0 +1,33 @@ +#include + +#ifndef Adafruit_I2CDevice_h +#define Adafruit_I2CDevice_h + +///< The class which defines how we will talk to this device over I2C +class Adafruit_I2CDevice { +public: + Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire = &Wire); + uint8_t address(void); + bool begin(bool addr_detect = true); + bool detected(void); + + bool read(uint8_t *buffer, size_t len, bool stop = true); + bool write(const uint8_t *buffer, size_t len, bool stop = true, + const uint8_t *prefix_buffer = NULL, size_t prefix_len = 0); + bool write_then_read(const uint8_t *write_buffer, size_t write_len, + uint8_t *read_buffer, size_t read_len, + bool stop = false); + bool setSpeed(uint32_t desiredclk); + + /*! @brief How many bytes we can read in a transaction + * @return The size of the Wire receive/transmit buffer */ + size_t maxBufferSize() { return _maxBufferSize; } + +private: + uint8_t _addr; + TwoWire *_wire; + bool _begun; + size_t _maxBufferSize; +}; + +#endif // Adafruit_I2CDevice_h diff --git a/libraries/Adafruit_BusIO/Adafruit_I2CRegister.h b/libraries/Adafruit_BusIO/Adafruit_I2CRegister.h new file mode 100644 index 0000000..703e93b --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_I2CRegister.h @@ -0,0 +1,8 @@ +#include "Adafruit_BusIO_Register.h" +#ifndef _ADAFRUIT_I2C_REGISTER_H_ +#define _ADAFRUIT_I2C_REGISTER_H_ + +typedef Adafruit_BusIO_Register Adafruit_I2CRegister; +typedef Adafruit_BusIO_RegisterBits Adafruit_I2CRegisterBits; + +#endif diff --git a/libraries/Adafruit_BusIO/Adafruit_SPIDevice.cpp b/libraries/Adafruit_BusIO/Adafruit_SPIDevice.cpp new file mode 100644 index 0000000..975e31f --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_SPIDevice.cpp @@ -0,0 +1,439 @@ +#include +#include + +//#define DEBUG_SERIAL Serial + +/*! + * @brief Create an SPI device with the given CS pin and settins + * @param cspin The arduino pin number to use for chip select + * @param freq The SPI clock frequency to use, defaults to 1MHz + * @param dataOrder The SPI data order to use for bits within each byte, + * defaults to SPI_BITORDER_MSBFIRST + * @param dataMode The SPI mode to use, defaults to SPI_MODE0 + * @param theSPI The SPI bus to use, defaults to &theSPI + */ +Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, uint32_t freq, + BitOrder dataOrder, uint8_t dataMode, + SPIClass *theSPI) { + _cs = cspin; + _sck = _mosi = _miso = -1; + _spi = theSPI; + _begun = false; + _spiSetting = new SPISettings(freq, dataOrder, dataMode); + _freq = freq; + _dataOrder = dataOrder; + _dataMode = dataMode; +} + +/*! + * @brief Create an SPI device with the given CS pin and settins + * @param cspin The arduino pin number to use for chip select + * @param sckpin The arduino pin number to use for SCK + * @param misopin The arduino pin number to use for MISO, set to -1 if not + * used + * @param mosipin The arduino pin number to use for MOSI, set to -1 if not + * used + * @param freq The SPI clock frequency to use, defaults to 1MHz + * @param dataOrder The SPI data order to use for bits within each byte, + * defaults to SPI_BITORDER_MSBFIRST + * @param dataMode The SPI mode to use, defaults to SPI_MODE0 + */ +Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, int8_t sckpin, + int8_t misopin, int8_t mosipin, + uint32_t freq, BitOrder dataOrder, + uint8_t dataMode) { + _cs = cspin; + _sck = sckpin; + _miso = misopin; + _mosi = mosipin; + +#ifdef BUSIO_USE_FAST_PINIO + csPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(cspin)); + csPinMask = digitalPinToBitMask(cspin); + if (mosipin != -1) { + mosiPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(mosipin)); + mosiPinMask = digitalPinToBitMask(mosipin); + } + if (misopin != -1) { + misoPort = (BusIO_PortReg *)portInputRegister(digitalPinToPort(misopin)); + misoPinMask = digitalPinToBitMask(misopin); + } + clkPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(sckpin)); + clkPinMask = digitalPinToBitMask(sckpin); +#endif + + _freq = freq; + _dataOrder = dataOrder; + _dataMode = dataMode; + _begun = false; + _spiSetting = new SPISettings(freq, dataOrder, dataMode); + _spi = NULL; +} + +/*! + * @brief Release memory allocated in constructors + */ +Adafruit_SPIDevice::~Adafruit_SPIDevice() { + if (_spiSetting) { + delete _spiSetting; + _spiSetting = nullptr; + } +} + +/*! + * @brief Initializes SPI bus and sets CS pin high + * @return Always returns true because there's no way to test success of SPI + * init + */ +bool Adafruit_SPIDevice::begin(void) { + pinMode(_cs, OUTPUT); + digitalWrite(_cs, HIGH); + + if (_spi) { // hardware SPI + _spi->begin(); + } else { + pinMode(_sck, OUTPUT); + + if ((_dataMode == SPI_MODE0) || (_dataMode == SPI_MODE1)) { + // idle low on mode 0 and 1 + digitalWrite(_sck, LOW); + } else { + // idle high on mode 2 or 3 + digitalWrite(_sck, HIGH); + } + if (_mosi != -1) { + pinMode(_mosi, OUTPUT); + digitalWrite(_mosi, HIGH); + } + if (_miso != -1) { + pinMode(_miso, INPUT); + } + } + + _begun = true; + return true; +} + +/*! + * @brief Transfer (send/receive) one byte over hard/soft SPI + * @param buffer The buffer to send and receive at the same time + * @param len The number of bytes to transfer + */ +void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) { + if (_spi) { + // hardware SPI is easy + +#if defined(SPARK) + _spi->transfer(buffer, buffer, len, NULL); +#elif defined(STM32) + for (size_t i = 0; i < len; i++) { + _spi->transfer(buffer[i]); + } +#else + _spi->transfer(buffer, len); +#endif + return; + } + + uint8_t startbit; + if (_dataOrder == SPI_BITORDER_LSBFIRST) { + startbit = 0x1; + } else { + startbit = 0x80; + } + + bool towrite, lastmosi = !(buffer[0] & startbit); + uint8_t bitdelay_us = (1000000 / _freq) / 2; + + // for softSPI we'll do it by hand + for (size_t i = 0; i < len; i++) { + // software SPI + uint8_t reply = 0; + uint8_t send = buffer[i]; + + /* + Serial.print("\tSending software SPI byte 0x"); + Serial.print(send, HEX); + Serial.print(" -> 0x"); + */ + + // Serial.print(send, HEX); + for (uint8_t b = startbit; b != 0; + b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) { + + if (bitdelay_us) { + delayMicroseconds(bitdelay_us); + } + + if (_dataMode == SPI_MODE0 || _dataMode == SPI_MODE2) { + towrite = send & b; + if ((_mosi != -1) && (lastmosi != towrite)) { +#ifdef BUSIO_USE_FAST_PINIO + if (towrite) + *mosiPort |= mosiPinMask; + else + *mosiPort &= ~mosiPinMask; +#else + digitalWrite(_mosi, towrite); +#endif + lastmosi = towrite; + } + +#ifdef BUSIO_USE_FAST_PINIO + *clkPort |= clkPinMask; // Clock high +#else + digitalWrite(_sck, HIGH); +#endif + + if (bitdelay_us) { + delayMicroseconds(bitdelay_us); + } + + if (_miso != -1) { +#ifdef BUSIO_USE_FAST_PINIO + if (*misoPort & misoPinMask) { +#else + if (digitalRead(_miso)) { +#endif + reply |= b; + } + } + +#ifdef BUSIO_USE_FAST_PINIO + *clkPort &= ~clkPinMask; // Clock low +#else + digitalWrite(_sck, LOW); +#endif + } else { // if (_dataMode == SPI_MODE1 || _dataMode == SPI_MODE3) + +#ifdef BUSIO_USE_FAST_PINIO + *clkPort |= clkPinMask; // Clock high +#else + digitalWrite(_sck, HIGH); +#endif + + if (bitdelay_us) { + delayMicroseconds(bitdelay_us); + } + + if (_mosi != -1) { +#ifdef BUSIO_USE_FAST_PINIO + if (send & b) + *mosiPort |= mosiPinMask; + else + *mosiPort &= ~mosiPinMask; +#else + digitalWrite(_mosi, send & b); +#endif + } + +#ifdef BUSIO_USE_FAST_PINIO + *clkPort &= ~clkPinMask; // Clock low +#else + digitalWrite(_sck, LOW); +#endif + + if (_miso != -1) { +#ifdef BUSIO_USE_FAST_PINIO + if (*misoPort & misoPinMask) { +#else + if (digitalRead(_miso)) { +#endif + reply |= b; + } + } + } + if (_miso != -1) { + buffer[i] = reply; + } + } + } + return; +} + +/*! + * @brief Transfer (send/receive) one byte over hard/soft SPI + * @param send The byte to send + * @return The byte received while transmitting + */ +uint8_t Adafruit_SPIDevice::transfer(uint8_t send) { + uint8_t data = send; + transfer(&data, 1); + return data; +} + +/*! + * @brief Manually begin a transaction (calls beginTransaction if hardware + * SPI) + */ +void Adafruit_SPIDevice::beginTransaction(void) { + if (_spi) { + _spi->beginTransaction(*_spiSetting); + } +} + +/*! + * @brief Manually end a transaction (calls endTransaction if hardware SPI) + */ +void Adafruit_SPIDevice::endTransaction(void) { + if (_spi) { + _spi->endTransaction(); + } +} + +/*! + * @brief Write a buffer or two to the SPI device. + * @param buffer Pointer to buffer of data to write + * @param len Number of bytes from buffer to write + * @param prefix_buffer Pointer to optional array of data to write before + * buffer. + * @param prefix_len Number of bytes from prefix buffer to write + * @return Always returns true because there's no way to test success of SPI + * writes + */ +bool Adafruit_SPIDevice::write(uint8_t *buffer, size_t len, + uint8_t *prefix_buffer, size_t prefix_len) { + if (_spi) { + _spi->beginTransaction(*_spiSetting); + } + + digitalWrite(_cs, LOW); + // do the writing + for (size_t i = 0; i < prefix_len; i++) { + transfer(prefix_buffer[i]); + } + for (size_t i = 0; i < len; i++) { + transfer(buffer[i]); + } + digitalWrite(_cs, HIGH); + + if (_spi) { + _spi->endTransaction(); + } + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tSPIDevice Wrote: ")); + if ((prefix_len != 0) && (prefix_buffer != NULL)) { + for (uint16_t i = 0; i < prefix_len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(prefix_buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + } + } + for (uint16_t i = 0; i < len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (i % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif + + return true; +} + +/*! + * @brief Read from SPI into a buffer from the SPI device. + * @param buffer Pointer to buffer of data to read into + * @param len Number of bytes from buffer to read. + * @param sendvalue The 8-bits of data to write when doing the data read, + * defaults to 0xFF + * @return Always returns true because there's no way to test success of SPI + * writes + */ +bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) { + memset(buffer, sendvalue, len); // clear out existing buffer + if (_spi) { + _spi->beginTransaction(*_spiSetting); + } + digitalWrite(_cs, LOW); + transfer(buffer, len); + digitalWrite(_cs, HIGH); + + if (_spi) { + _spi->endTransaction(); + } + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tSPIDevice Read: ")); + for (uint16_t i = 0; i < len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (len % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif + + return true; +} + +/*! + * @brief Write some data, then read some data from SPI into another buffer. + * The buffers can point to same/overlapping locations. This does not + * transmit-receive at the same time! + * @param write_buffer Pointer to buffer of data to write from + * @param write_len Number of bytes from buffer to write. + * @param read_buffer Pointer to buffer of data to read into. + * @param read_len Number of bytes from buffer to read. + * @param sendvalue The 8-bits of data to write when doing the data read, + * defaults to 0xFF + * @return Always returns true because there's no way to test success of SPI + * writes + */ +bool Adafruit_SPIDevice::write_then_read(uint8_t *write_buffer, + size_t write_len, uint8_t *read_buffer, + size_t read_len, uint8_t sendvalue) { + if (_spi) { + _spi->beginTransaction(*_spiSetting); + } + + digitalWrite(_cs, LOW); + // do the writing + for (size_t i = 0; i < write_len; i++) { + transfer(write_buffer[i]); + } + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tSPIDevice Wrote: ")); + for (uint16_t i = 0; i < write_len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(write_buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (write_len % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif + + // do the reading + for (size_t i = 0; i < read_len; i++) { + read_buffer[i] = transfer(sendvalue); + } + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print(F("\tSPIDevice Read: ")); + for (uint16_t i = 0; i < read_len; i++) { + DEBUG_SERIAL.print(F("0x")); + DEBUG_SERIAL.print(read_buffer[i], HEX); + DEBUG_SERIAL.print(F(", ")); + if (read_len % 32 == 31) { + DEBUG_SERIAL.println(); + } + } + DEBUG_SERIAL.println(); +#endif + + digitalWrite(_cs, HIGH); + + if (_spi) { + _spi->endTransaction(); + } + + return true; +} diff --git a/libraries/Adafruit_BusIO/Adafruit_SPIDevice.h b/libraries/Adafruit_BusIO/Adafruit_SPIDevice.h new file mode 100644 index 0000000..b96f86b --- /dev/null +++ b/libraries/Adafruit_BusIO/Adafruit_SPIDevice.h @@ -0,0 +1,99 @@ +#include + +#ifndef Adafruit_SPIDevice_h +#define Adafruit_SPIDevice_h + +// some modern SPI definitions don't have BitOrder enum +#if (defined(__AVR__) && !defined(ARDUINO_ARCH_MEGAAVR)) || \ + defined(ESP8266) || defined(TEENSYDUINO) || defined(SPARK) || \ + defined(ARDUINO_ARCH_SPRESENSE) || defined(MEGATINYCORE) || \ + defined(DXCORE) || defined(ARDUINO_AVR_ATmega4809) || \ + defined(ARDUINO_AVR_ATmega4808) || defined(ARDUINO_AVR_ATmega3209) || \ + defined(ARDUINO_AVR_ATmega3208) || defined(ARDUINO_AVR_ATmega1609) || \ + defined(ARDUINO_AVR_ATmega1608) || defined(ARDUINO_AVR_ATmega809) || \ + defined(ARDUINO_AVR_ATmega808) +typedef enum _BitOrder { + SPI_BITORDER_MSBFIRST = MSBFIRST, + SPI_BITORDER_LSBFIRST = LSBFIRST, +} BitOrder; + +#elif defined(ESP32) || defined(__ASR6501__) + +// some modern SPI definitions don't have BitOrder enum and have different SPI +// mode defines +typedef enum _BitOrder { + SPI_BITORDER_MSBFIRST = SPI_MSBFIRST, + SPI_BITORDER_LSBFIRST = SPI_LSBFIRST, +} BitOrder; + +#else +// Some platforms have a BitOrder enum but its named MSBFIRST/LSBFIRST +#define SPI_BITORDER_MSBFIRST MSBFIRST +#define SPI_BITORDER_LSBFIRST LSBFIRST +#endif + +#if defined(__AVR__) || defined(TEENSYDUINO) +typedef volatile uint8_t BusIO_PortReg; +typedef uint8_t BusIO_PortMask; +#define BUSIO_USE_FAST_PINIO + +#elif defined(ESP8266) || defined(ESP32) || defined(__SAM3X8E__) || \ + defined(ARDUINO_ARCH_SAMD) +typedef volatile uint32_t BusIO_PortReg; +typedef uint32_t BusIO_PortMask; +#define BUSIO_USE_FAST_PINIO + +#elif (defined(__arm__) || defined(ARDUINO_FEATHER52)) && \ + !defined(ARDUINO_ARCH_MBED) +typedef volatile uint32_t BusIO_PortReg; +typedef uint32_t BusIO_PortMask; +#if not defined(__ASR6501__) +#define BUSIO_USE_FAST_PINIO +#endif + +#else +#undef BUSIO_USE_FAST_PINIO +#endif + +/**! The class which defines how we will talk to this device over SPI **/ +class Adafruit_SPIDevice { +public: + Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000, + BitOrder dataOrder = SPI_BITORDER_MSBFIRST, + uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = &SPI); + + Adafruit_SPIDevice(int8_t cspin, int8_t sck, int8_t miso, int8_t mosi, + uint32_t freq = 1000000, + BitOrder dataOrder = SPI_BITORDER_MSBFIRST, + uint8_t dataMode = SPI_MODE0); + ~Adafruit_SPIDevice(); + + bool begin(void); + bool read(uint8_t *buffer, size_t len, uint8_t sendvalue = 0xFF); + bool write(uint8_t *buffer, size_t len, uint8_t *prefix_buffer = NULL, + size_t prefix_len = 0); + bool write_then_read(uint8_t *write_buffer, size_t write_len, + uint8_t *read_buffer, size_t read_len, + uint8_t sendvalue = 0xFF); + + uint8_t transfer(uint8_t send); + void transfer(uint8_t *buffer, size_t len); + void beginTransaction(void); + void endTransaction(void); + +private: + SPIClass *_spi; + SPISettings *_spiSetting; + uint32_t _freq; + BitOrder _dataOrder; + uint8_t _dataMode; + + int8_t _cs, _sck, _mosi, _miso; +#ifdef BUSIO_USE_FAST_PINIO + BusIO_PortReg *mosiPort, *clkPort, *misoPort, *csPort; + BusIO_PortMask mosiPinMask, misoPinMask, clkPinMask, csPinMask; +#endif + bool _begun; +}; + +#endif // Adafruit_SPIDevice_h diff --git a/libraries/Adafruit_BusIO/LICENSE b/libraries/Adafruit_BusIO/LICENSE new file mode 100644 index 0000000..860e3e2 --- /dev/null +++ b/libraries/Adafruit_BusIO/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Adafruit Industries + +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. \ No newline at end of file diff --git a/libraries/Adafruit_BusIO/README.md b/libraries/Adafruit_BusIO/README.md new file mode 100644 index 0000000..771cd13 --- /dev/null +++ b/libraries/Adafruit_BusIO/README.md @@ -0,0 +1,8 @@ +# Adafruit Bus IO Library [![Build Status](https://github.com/adafruit/Adafruit_BusIO/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_BusIO/actions) + + +This is a helper libary to abstract away I2C & SPI transactions and registers + +Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! + +MIT license, all text above must be included in any redistribution diff --git a/libraries/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino b/libraries/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino new file mode 100644 index 0000000..b150525 --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino @@ -0,0 +1,21 @@ +#include + +Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(0x10); + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("I2C address detection test"); + + if (!i2c_dev.begin()) { + Serial.print("Did not find device at 0x"); + Serial.println(i2c_dev.address(), HEX); + while (1); + } + Serial.print("Device found on address 0x"); + Serial.println(i2c_dev.address(), HEX); +} + +void loop() { + +} diff --git a/libraries/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino b/libraries/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino new file mode 100644 index 0000000..909cf31 --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino @@ -0,0 +1,41 @@ +#include + +#define I2C_ADDRESS 0x60 +Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS); + + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("I2C device read and write test"); + + if (!i2c_dev.begin()) { + Serial.print("Did not find device at 0x"); + Serial.println(i2c_dev.address(), HEX); + while (1); + } + Serial.print("Device found on address 0x"); + Serial.println(i2c_dev.address(), HEX); + + uint8_t buffer[32]; + // Try to read 32 bytes + i2c_dev.read(buffer, 32); + Serial.print("Read: "); + for (uint8_t i=0; i<32; i++) { + Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); + } + Serial.println(); + + // read a register by writing first, then reading + buffer[0] = 0x0C; // we'll reuse the same buffer + i2c_dev.write_then_read(buffer, 1, buffer, 2, false); + Serial.print("Write then Read: "); + for (uint8_t i=0; i<2; i++) { + Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); + } + Serial.println(); +} + +void loop() { + +} diff --git a/libraries/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino b/libraries/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino new file mode 100644 index 0000000..41a3043 --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino @@ -0,0 +1,38 @@ +#include +#include + +#define I2C_ADDRESS 0x60 +Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS); + + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("I2C device register test"); + + if (!i2c_dev.begin()) { + Serial.print("Did not find device at 0x"); + Serial.println(i2c_dev.address(), HEX); + while (1); + } + Serial.print("Device found on address 0x"); + Serial.println(i2c_dev.address(), HEX); + + Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(&i2c_dev, 0x0C, 2, LSBFIRST); + uint16_t id; + id_reg.read(&id); + Serial.print("ID register = 0x"); Serial.println(id, HEX); + + Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(&i2c_dev, 0x01, 2, LSBFIRST); + uint16_t thresh; + thresh_reg.read(&thresh); + Serial.print("Initial threshold register = 0x"); Serial.println(thresh, HEX); + + thresh_reg.write(~thresh); + + Serial.print("Post threshold register = 0x"); Serial.println(thresh_reg.read(), HEX); +} + +void loop() { + +} \ No newline at end of file diff --git a/libraries/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino b/libraries/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino new file mode 100644 index 0000000..555cf3b --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino @@ -0,0 +1,38 @@ +#include + +// Define which interface to use by setting the unused interface to NULL! + +#define SPIDEVICE_CS 10 +Adafruit_SPIDevice *spi_dev = NULL; // new Adafruit_SPIDevice(SPIDEVICE_CS); + +#define I2C_ADDRESS 0x5D +Adafruit_I2CDevice *i2c_dev = new Adafruit_I2CDevice(I2C_ADDRESS); + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("I2C or SPI device register test"); + + if (spi_dev && !spi_dev->begin()) { + Serial.println("Could not initialize SPI device"); + } + + if (i2c_dev) { + if (i2c_dev->begin()) { + Serial.print("Device found on I2C address 0x"); + Serial.println(i2c_dev->address(), HEX); + } else { + Serial.print("Did not find I2C device at 0x"); + Serial.println(i2c_dev->address(), HEX); + } + } + + Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(i2c_dev, spi_dev, ADDRBIT8_HIGH_TOREAD, 0x0F); + uint8_t id; + id_reg.read(&id); + Serial.print("ID register = 0x"); Serial.println(id, HEX); +} + +void loop() { + +} \ No newline at end of file diff --git a/libraries/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino b/libraries/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino new file mode 100644 index 0000000..10168c5 --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino @@ -0,0 +1,29 @@ +#include + +#define SPIDEVICE_CS 10 +Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1); +//Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 13, 12, 11, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1); + + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("SPI device mode test"); + + if (!spi_dev.begin()) { + Serial.println("Could not initialize SPI device"); + while (1); + } +} + +void loop() { + Serial.println("\n\nTransfer test"); + for (uint16_t x=0; x<=0xFF; x++) { + uint8_t i = x; + Serial.print("0x"); Serial.print(i, HEX); + spi_dev.read(&i, 1, i); + Serial.print("/"); Serial.print(i, HEX); + Serial.print(", "); + delay(25); + } +} \ No newline at end of file diff --git a/libraries/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino b/libraries/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino new file mode 100644 index 0000000..6f2c063 --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino @@ -0,0 +1,39 @@ +#include + +#define SPIDEVICE_CS 10 +Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS); + + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("SPI device read and write test"); + + if (!spi_dev.begin()) { + Serial.println("Could not initialize SPI device"); + while (1); + } + + uint8_t buffer[32]; + + // Try to read 32 bytes + spi_dev.read(buffer, 32); + Serial.print("Read: "); + for (uint8_t i=0; i<32; i++) { + Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); + } + Serial.println(); + + // read a register by writing first, then reading + buffer[0] = 0x8F; // we'll reuse the same buffer + spi_dev.write_then_read(buffer, 1, buffer, 2, false); + Serial.print("Write then Read: "); + for (uint8_t i=0; i<2; i++) { + Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); + } + Serial.println(); +} + +void loop() { + +} diff --git a/libraries/Adafruit_BusIO/examples/spi_register_bits/spi_register_bits.ino b/libraries/Adafruit_BusIO/examples/spi_register_bits/spi_register_bits.ino new file mode 100644 index 0000000..ff21fb7 --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/spi_register_bits/spi_register_bits.ino @@ -0,0 +1,192 @@ +/*************************************************** + + This is an example for how to use Adafruit_BusIO_RegisterBits from Adafruit_BusIO library. + + Designed specifically to work with the Adafruit RTD Sensor + ----> https://www.adafruit.com/products/3328 + uisng a MAX31865 RTD-to-Digital Converter + ----> https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf + + This sensor uses SPI to communicate, 4 pins are required to + interface. + A fifth pin helps to detect when a new conversion is ready. + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Example written (2020/3) by Andreas Hardtung/AnHard. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#include +#include + +#define MAX31865_SPI_SPEED (5000000) +#define MAX31865_SPI_BITORDER (SPI_BITORDER_MSBFIRST) +#define MAX31865_SPI_MODE (SPI_MODE1) + +#define MAX31865_SPI_CS (10) +#define MAX31865_READY_PIN (2) + + +Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE, &SPI); // Hardware SPI +// Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, 13, 12, 11, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE); // Software SPI + +// MAX31865 chip related ********************************************************************************************* +Adafruit_BusIO_Register config_reg = Adafruit_BusIO_Register(&spi_dev, 0x00, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST); +Adafruit_BusIO_RegisterBits bias_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 7); +Adafruit_BusIO_RegisterBits auto_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 6); +Adafruit_BusIO_RegisterBits oneS_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 5); +Adafruit_BusIO_RegisterBits wire_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 4); +Adafruit_BusIO_RegisterBits faultT_bits = Adafruit_BusIO_RegisterBits(&config_reg, 2, 2); +Adafruit_BusIO_RegisterBits faultR_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 1); +Adafruit_BusIO_RegisterBits fi50hz_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 0); + +Adafruit_BusIO_Register rRatio_reg = Adafruit_BusIO_Register(&spi_dev, 0x01, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST); +Adafruit_BusIO_RegisterBits rRatio_bits = Adafruit_BusIO_RegisterBits(&rRatio_reg, 15, 1); +Adafruit_BusIO_RegisterBits fault_bit = Adafruit_BusIO_RegisterBits(&rRatio_reg, 1, 0); + +Adafruit_BusIO_Register maxRratio_reg = Adafruit_BusIO_Register(&spi_dev, 0x03, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST); +Adafruit_BusIO_RegisterBits maxRratio_bits = Adafruit_BusIO_RegisterBits(&maxRratio_reg, 15, 1); + +Adafruit_BusIO_Register minRratio_reg = Adafruit_BusIO_Register(&spi_dev, 0x05, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST); +Adafruit_BusIO_RegisterBits minRratio_bits = Adafruit_BusIO_RegisterBits(&minRratio_reg, 15, 1); + +Adafruit_BusIO_Register fault_reg = Adafruit_BusIO_Register(&spi_dev, 0x07, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST); +Adafruit_BusIO_RegisterBits range_high_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 7); +Adafruit_BusIO_RegisterBits range_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 6); +Adafruit_BusIO_RegisterBits refin_high_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 5); +Adafruit_BusIO_RegisterBits refin_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 4); +Adafruit_BusIO_RegisterBits rtdin_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 3); +Adafruit_BusIO_RegisterBits voltage_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 2); + +// Print the details of the configuration register. +void printConfig( void ) { + Serial.print("BIAS: "); if (bias_bit.read() ) Serial.print("ON"); else Serial.print("OFF"); + Serial.print(", AUTO: "); if (auto_bit.read() ) Serial.print("ON"); else Serial.print("OFF"); + Serial.print(", ONES: "); if (oneS_bit.read() ) Serial.print("ON"); else Serial.print("OFF"); + Serial.print(", WIRE: "); if (wire_bit.read() ) Serial.print("3"); else Serial.print("2/4"); + Serial.print(", FAULTCLEAR: "); if (faultR_bit.read() ) Serial.print("ON"); else Serial.print("OFF"); + Serial.print(", "); if (fi50hz_bit.read() ) Serial.print("50HZ"); else Serial.print("60HZ"); + Serial.println(); +} + +// Check and print faults. Then clear them. +void checkFaults( void ) { + if (fault_bit.read()) { + Serial.print("MAX: "); Serial.println(maxRratio_bits.read()); + Serial.print("VAL: "); Serial.println( rRatio_bits.read()); + Serial.print("MIN: "); Serial.println(minRratio_bits.read()); + + if (range_high_fault_bit.read() ) Serial.println("Range high fault"); + if ( range_low_fault_bit.read() ) Serial.println("Range low fault"); + if (refin_high_fault_bit.read() ) Serial.println("REFIN high fault"); + if ( refin_low_fault_bit.read() ) Serial.println("REFIN low fault"); + if ( rtdin_low_fault_bit.read() ) Serial.println("RTDIN low fault"); + if ( voltage_fault_bit.read() ) Serial.println("Voltage fault"); + + faultR_bit.write(1); // clear fault + } +} + +void setup() { + #if (MAX31865_1_READY_PIN != -1) + pinMode(MAX31865_READY_PIN ,INPUT_PULLUP); + #endif + + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("SPI Adafruit_BusIO_RegisterBits test on MAX31865"); + + if (!spi_dev.begin()) { + Serial.println("Could not initialize SPI device"); + while (1); + } + + // Set up for automode 50Hz. We don't care about selfheating. We want the highest possible sampling rate. + auto_bit.write(0); // Don't switch filtermode while auto_mode is on. + fi50hz_bit.write(1); // Set filter to 50Hz mode. + faultR_bit.write(1); // Clear faults. + bias_bit.write(1); // In automode we want to have the bias current always on. + delay(5); // Wait until bias current settles down. + // 10.5 time constants of the input RC network is required. + // 10ms worst case for 10kω reference resistor and a 0.1µF capacitor across the RTD inputs. + // Adafruit Module has 0.1µF and only 430/4300ω So here 0.43/4.3ms + auto_bit.write(1); // Now we can set automode. Automatically starting first conversion. + + // Test the READY_PIN + #if (defined( MAX31865_READY_PIN ) && (MAX31865_READY_PIN != -1)) + int i = 0; + while (digitalRead(MAX31865_READY_PIN) && i++ <= 100) { delay(1); } + if (i >= 100) { + Serial.print("ERROR: Max31865 Pin detection does not work. PIN:"); + Serial.println(MAX31865_READY_PIN); + } + #else + delay(100); + #endif + + // Set ratio range. + // Setting the temperatures would need some more calculation - not related to Adafruit_BusIO_RegisterBits. + uint16_t ratio = rRatio_bits.read(); + maxRratio_bits.write( (ratio < 0x8fffu-1000u) ? ratio + 1000u : 0x8fffu ); + minRratio_bits.write( (ratio > 1000u) ? ratio - 1000u : 0u ); + + printConfig(); + checkFaults(); +} + +void loop() { + #if (defined( MAX31865_READY_PIN ) && (MAX31865_1_READY_PIN != -1)) + // Is converstion ready? + if (!digitalRead(MAX31865_READY_PIN)) + #else + // Warant conversion is ready. + delay(21); // 21ms for 50Hz-mode. 19ms in 60Hz-mode. + #endif + { + // Read ratio, calculate temperature, scale, filter and print. + Serial.println( rRatio2C( rRatio_bits.read() ) * 100.0f, 0); // Temperature scaled by 100 + // Check, print, clear faults. + checkFaults(); + } + + // Do something else. + //delay(15000); +} + + +// Module/Sensor related. Here Adafruit PT100 module with a 2_Wire PT100 Class C ***************************** +float rRatio2C(uint16_t ratio) { + // A simple linear conversion. + const float R0 = 100.0f; + const float Rref = 430.0f; + const float alphaPT = 0.003850f; + const float ADCmax = (1u << 15) - 1.0f; + const float rscale = Rref / ADCmax; + // Measured temperature in boiling water 101.08°C with factor a = 1 and b = 0. Rref and MAX at about 22±2°C. + // Measured temperature in ice/water bath 0.76°C with factor a = 1 and b = 0. Rref and MAX at about 22±2°C. + //const float a = 1.0f / (alphaPT * R0); + const float a = (100.0f/101.08f) / (alphaPT * R0); + //const float b = 0.0f; // 101.08 + const float b = -0.76f; // 100.32 > 101.08 + + return filterRing( ((ratio * rscale) - R0) * a + b ); +} + +// General purpose ********************************************************************************************* +#define RINGLENGTH 250 +float filterRing( float newVal ) { + static float ring[RINGLENGTH] = { 0.0 }; + static uint8_t ringIndex = 0; + static bool ringFull = false; + + if ( ringIndex == RINGLENGTH ) { ringFull = true; ringIndex = 0; } + ring[ringIndex] = newVal; + uint8_t loopEnd = (ringFull) ? RINGLENGTH : ringIndex + 1; + float ringSum = 0.0f; + for (uint8_t i = 0; i < loopEnd; i++) ringSum += ring[i]; + ringIndex++; + return ringSum / loopEnd; +} diff --git a/libraries/Adafruit_BusIO/examples/spi_registers/spi_registers.ino b/libraries/Adafruit_BusIO/examples/spi_registers/spi_registers.ino new file mode 100644 index 0000000..e24f1aa --- /dev/null +++ b/libraries/Adafruit_BusIO/examples/spi_registers/spi_registers.ino @@ -0,0 +1,34 @@ +#include +#include + +#define SPIDEVICE_CS 10 +Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS); + +void setup() { + while (!Serial) { delay(10); } + Serial.begin(115200); + Serial.println("SPI device register test"); + + if (!spi_dev.begin()) { + Serial.println("Could not initialize SPI device"); + while (1); + } + + Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(&spi_dev, 0x0F, ADDRBIT8_HIGH_TOREAD); + uint8_t id; + id_reg.read(&id); + Serial.print("ID register = 0x"); Serial.println(id, HEX); + + Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(&spi_dev, 0x0C, ADDRBIT8_HIGH_TOREAD, 2, LSBFIRST); + uint16_t thresh; + thresh_reg.read(&thresh); + Serial.print("Initial threshold register = 0x"); Serial.println(thresh, HEX); + + thresh_reg.write(~thresh); + + Serial.print("Post threshold register = 0x"); Serial.println(thresh_reg.read(), HEX); +} + +void loop() { + +} \ No newline at end of file diff --git a/libraries/Adafruit_BusIO/library.properties b/libraries/Adafruit_BusIO/library.properties new file mode 100644 index 0000000..0a93ee1 --- /dev/null +++ b/libraries/Adafruit_BusIO/library.properties @@ -0,0 +1,9 @@ +name=Adafruit BusIO +version=1.7.2 +author=Adafruit +maintainer=Adafruit +sentence=This is a library for abstracting away UART, I2C and SPI interfacing +paragraph=This is a library for abstracting away UART, I2C and SPI interfacing +category=Signal Input/Output +url=https://github.com/adafruit/Adafruit_BusIO +architectures=* diff --git a/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp b/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp new file mode 100644 index 0000000..bae0858 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp @@ -0,0 +1,2670 @@ +/* +This is the core graphics library for all our displays, providing a common +set of graphics primitives (points, lines, circles, etc.). It needs to be +paired with a hardware-specific library for each display device we carry +(to handle the lower-level functions). + +Adafruit invests time and resources providing this open source code, please +support Adafruit & open-source hardware by purchasing products from Adafruit! + +Copyright (c) 2013 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Adafruit_GFX.h" +#include "glcdfont.c" +#ifdef __AVR__ +#include +#elif defined(ESP8266) || defined(ESP32) +#include +#endif + +// Many (but maybe not all) non-AVR board installs define macros +// for compatibility with existing PROGMEM-reading AVR code. +// Do our own checks and defines here for good measure... + +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif +#ifndef pgm_read_word +#define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +#ifndef pgm_read_dword +#define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#endif + +// Pointers are a peculiar case...typically 16-bit on AVR boards, +// 32 bits elsewhere. Try to accommodate both... + +#if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) +#define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) +#else +#define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) +#endif + +inline GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) { +#ifdef __AVR__ + return &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); +#else + // expression in __AVR__ section may generate "dereferencing type-punned + // pointer will break strict-aliasing rules" warning In fact, on other + // platforms (such as STM32) there is no need to do this pointer magic as + // program memory may be read in a usual way So expression may be simplified + return gfxFont->glyph + c; +#endif //__AVR__ +} + +inline uint8_t *pgm_read_bitmap_ptr(const GFXfont *gfxFont) { +#ifdef __AVR__ + return (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); +#else + // expression in __AVR__ section generates "dereferencing type-punned pointer + // will break strict-aliasing rules" warning In fact, on other platforms (such + // as STM32) there is no need to do this pointer magic as program memory may + // be read in a usual way So expression may be simplified + return gfxFont->bitmap; +#endif //__AVR__ +} + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) \ + { \ + int16_t t = a; \ + a = b; \ + b = t; \ + } +#endif + +/**************************************************************************/ +/*! + @brief Instatiate a GFX context for graphics! Can only be done by a + superclass + @param w Display width, in pixels + @param h Display height, in pixels +*/ +/**************************************************************************/ +Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) { + _width = WIDTH; + _height = HEIGHT; + rotation = 0; + cursor_y = cursor_x = 0; + textsize_x = textsize_y = 1; + textcolor = textbgcolor = 0xFFFF; + wrap = true; + _cp437 = false; + gfxFont = NULL; +} + +/**************************************************************************/ +/*! + @brief Write a line. Bresenham's algorithm - thx wikpedia + @param x0 Start point x coordinate + @param y0 Start point y coordinate + @param x1 End point x coordinate + @param y1 End point y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color) { +#if defined(ESP8266) + yield(); +#endif + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0 <= x1; x0++) { + if (steep) { + writePixel(y0, x0, color); + } else { + writePixel(x0, y0, color); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +/**************************************************************************/ +/*! + @brief Start a display-writing routine, overwrite in subclasses. +*/ +/**************************************************************************/ +void Adafruit_GFX::startWrite() {} + +/**************************************************************************/ +/*! + @brief Write a pixel, overwrite in subclasses if startWrite is defined! + @param x x coordinate + @param y y coordinate + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color) { + drawPixel(x, y, color); +} + +/**************************************************************************/ +/*! + @brief Write a perfectly vertical line, overwrite in subclasses if + startWrite is defined! + @param x Top-most x coordinate + @param y Top-most y coordinate + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + // Overwrite in subclasses if startWrite is defined! + // Can be just writeLine(x, y, x, y+h-1, color); + // or writeFillRect(x, y, 1, h, color); + drawFastVLine(x, y, h, color); +} + +/**************************************************************************/ +/*! + @brief Write a perfectly horizontal line, overwrite in subclasses if + startWrite is defined! + @param x Left-most x coordinate + @param y Left-most y coordinate + @param w Width in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + // Overwrite in subclasses if startWrite is defined! + // Example: writeLine(x, y, x+w-1, y, color); + // or writeFillRect(x, y, w, 1, color); + drawFastHLine(x, y, w, color); +} + +/**************************************************************************/ +/*! + @brief Write a rectangle completely with one color, overwrite in + subclasses if startWrite is defined! + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + // Overwrite in subclasses if desired! + fillRect(x, y, w, h, color); +} + +/**************************************************************************/ +/*! + @brief End a display-writing routine, overwrite in subclasses if + startWrite is defined! +*/ +/**************************************************************************/ +void Adafruit_GFX::endWrite() {} + +/**************************************************************************/ +/*! + @brief Draw a perfectly vertical line (this is often optimized in a + subclass!) + @param x Top-most x coordinate + @param y Top-most y coordinate + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + startWrite(); + writeLine(x, y, x, y + h - 1, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a perfectly horizontal line (this is often optimized in a + subclass!) + @param x Left-most x coordinate + @param y Left-most y coordinate + @param w Width in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + startWrite(); + writeLine(x, y, x + w - 1, y, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Fill a rectangle completely with one color. Update in subclasses if + desired! + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + startWrite(); + for (int16_t i = x; i < x + w; i++) { + writeFastVLine(i, y, h, color); + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Fill the screen completely with one color. Update in subclasses if + desired! + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillScreen(uint16_t color) { + fillRect(0, 0, _width, _height, color); +} + +/**************************************************************************/ +/*! + @brief Draw a line + @param x0 Start point x coordinate + @param y0 Start point y coordinate + @param x1 End point x coordinate + @param y1 End point y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color) { + // Update in subclasses if desired! + if (x0 == x1) { + if (y0 > y1) + _swap_int16_t(y0, y1); + drawFastVLine(x0, y0, y1 - y0 + 1, color); + } else if (y0 == y1) { + if (x0 > x1) + _swap_int16_t(x0, x1); + drawFastHLine(x0, y0, x1 - x0 + 1, color); + } else { + startWrite(); + writeLine(x0, y0, x1, y1, color); + endWrite(); + } +} + +/**************************************************************************/ +/*! + @brief Draw a circle outline + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param r Radius of circle + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { +#if defined(ESP8266) + yield(); +#endif + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + startWrite(); + writePixel(x0, y0 + r, color); + writePixel(x0, y0 - r, color); + writePixel(x0 + r, y0, color); + writePixel(x0 - r, y0, color); + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + writePixel(x0 + x, y0 + y, color); + writePixel(x0 - x, y0 + y, color); + writePixel(x0 + x, y0 - y, color); + writePixel(x0 - x, y0 - y, color); + writePixel(x0 + y, y0 + x, color); + writePixel(x0 - y, y0 + x, color); + writePixel(x0 + y, y0 - x, color); + writePixel(x0 - y, y0 - x, color); + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Quarter-circle drawer, used to do circles and roundrects + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param r Radius of circle + @param cornername Mask bit #1 or bit #2 to indicate which quarters of + the circle we're doing + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t cornername, uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (cornername & 0x4) { + writePixel(x0 + x, y0 + y, color); + writePixel(x0 + y, y0 + x, color); + } + if (cornername & 0x2) { + writePixel(x0 + x, y0 - y, color); + writePixel(x0 + y, y0 - x, color); + } + if (cornername & 0x8) { + writePixel(x0 - y, y0 + x, color); + writePixel(x0 - x, y0 + y, color); + } + if (cornername & 0x1) { + writePixel(x0 - y, y0 - x, color); + writePixel(x0 - x, y0 - y, color); + } + } +} + +/**************************************************************************/ +/*! + @brief Draw a circle with filled color + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param r Radius of circle + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + startWrite(); + writeFastVLine(x0, y0 - r, 2 * r + 1, color); + fillCircleHelper(x0, y0, r, 3, 0, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Quarter-circle drawer with fill, used for circles and roundrects + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param r Radius of circle + @param corners Mask bits indicating which quarters we're doing + @param delta Offset from center-point, used for round-rects + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t corners, int16_t delta, + uint16_t color) { + + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + int16_t px = x; + int16_t py = y; + + delta++; // Avoid some +1's in the loop + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + // These checks avoid double-drawing certain lines, important + // for the SSD1306 library which has an INVERT drawing mode. + if (x < (y + 1)) { + if (corners & 1) + writeFastVLine(x0 + x, y0 - y, 2 * y + delta, color); + if (corners & 2) + writeFastVLine(x0 - x, y0 - y, 2 * y + delta, color); + } + if (y != py) { + if (corners & 1) + writeFastVLine(x0 + py, y0 - px, 2 * px + delta, color); + if (corners & 2) + writeFastVLine(x0 - py, y0 - px, 2 * px + delta, color); + py = y; + } + px = x; + } +} + +/**************************************************************************/ +/*! + @brief Draw a rectangle with no fill color + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + startWrite(); + writeFastHLine(x, y, w, color); + writeFastHLine(x, y + h - 1, w, color); + writeFastVLine(x, y, h, color); + writeFastVLine(x + w - 1, y, h, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a rounded rectangle with no fill color + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param r Radius of corner rounding + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, + int16_t r, uint16_t color) { + int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis + if (r > max_radius) + r = max_radius; + // smarter version + startWrite(); + writeFastHLine(x + r, y, w - 2 * r, color); // Top + writeFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom + writeFastVLine(x, y + r, h - 2 * r, color); // Left + writeFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right + // draw four corners + drawCircleHelper(x + r, y + r, r, 1, color); + drawCircleHelper(x + w - r - 1, y + r, r, 2, color); + drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); + drawCircleHelper(x + r, y + h - r - 1, r, 8, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a rounded rectangle with fill color + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param r Radius of corner rounding + @param color 16-bit 5-6-5 Color to draw/fill with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, + int16_t r, uint16_t color) { + int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis + if (r > max_radius) + r = max_radius; + // smarter version + startWrite(); + writeFillRect(x + r, y, w - 2 * r, h, color); + // draw four corners + fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); + fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a triangle with no fill color + @param x0 Vertex #0 x coordinate + @param y0 Vertex #0 y coordinate + @param x1 Vertex #1 x coordinate + @param y1 Vertex #1 y coordinate + @param x2 Vertex #2 x coordinate + @param y2 Vertex #2 y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color) { + drawLine(x0, y0, x1, y1, color); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x0, y0, color); +} + +/**************************************************************************/ +/*! + @brief Draw a triangle with color-fill + @param x0 Vertex #0 x coordinate + @param y0 Vertex #0 y coordinate + @param x1 Vertex #1 x coordinate + @param y1 Vertex #1 y coordinate + @param x2 Vertex #2 x coordinate + @param y2 Vertex #2 y coordinate + @param color 16-bit 5-6-5 Color to fill/draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color) { + + int16_t a, b, y, last; + + // Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + if (y1 > y2) { + _swap_int16_t(y2, y1); + _swap_int16_t(x2, x1); + } + if (y0 > y1) { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + + startWrite(); + if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if (x1 < a) + a = x1; + else if (x1 > b) + b = x1; + if (x2 < a) + a = x2; + else if (x2 > b) + b = x2; + writeFastHLine(a, y0, b - a + 1, color); + endWrite(); + return; + } + + int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, + dx12 = x2 - x1, dy12 = y2 - y1; + int32_t sa = 0, sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if (y1 == y2) + last = y1; // Include y1 scanline + else + last = y1 - 1; // Skip it + + for (y = y0; y <= last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if (a > b) + _swap_int16_t(a, b); + writeFastHLine(a, y, b - a + 1, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = (int32_t)dx12 * (y - y1); + sb = (int32_t)dx02 * (y - y0); + for (; y <= y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if (a > b) + _swap_int16_t(a, b); + writeFastHLine(a, y, b - a + 1, color); + } + endWrite(); +} + +// BITMAP / XBITMAP / GRAYSCALE / RGB BITMAP FUNCTIONS --------------------- + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 1-bit image at the specified (x,y) + position, using the specified foreground color (unset bits are transparent). + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], + int16_t w, int16_t h, uint16_t color) { + + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + if (byte & 0x80) + writePixel(x + i, y, color); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 1-bit image at the specified (x,y) + position, using the specified foreground (for set bits) and background (unset + bits) colors. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw pixels with + @param bg 16-bit 5-6-5 Color to draw background with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], + int16_t w, int16_t h, uint16_t color, + uint16_t bg) { + + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + writePixel(x + i, y, (byte & 0x80) ? color : bg); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 1-bit image at the specified (x,y) position, + using the specified foreground color (unset bits are transparent). + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, + int16_t h, uint16_t color) { + + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = bitmap[j * byteWidth + i / 8]; + if (byte & 0x80) + writePixel(x + i, y, color); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 1-bit image at the specified (x,y) position, + using the specified foreground (for set bits) and background (unset bits) + colors. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw pixels with + @param bg 16-bit 5-6-5 Color to draw background with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, + int16_t h, uint16_t color, uint16_t bg) { + + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = bitmap[j * byteWidth + i / 8]; + writePixel(x + i, y, (byte & 0x80) ? color : bg); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP. + Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. + C Array can be directly used with this function. + There is no RAM-resident version of this function; if generating bitmaps + in RAM, use the format defined by drawBitmap() and call that instead. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw pixels with +*/ +/**************************************************************************/ +void Adafruit_GFX::drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], + int16_t w, int16_t h, uint16_t color) { + + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte >>= 1; + else + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + // Nearly identical to drawBitmap(), only the bit order + // is reversed here (left-to-right = LSB to MSB): + if (byte & 0x01) + writePixel(x + i, y, color); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 8-bit image (grayscale) at the specified + (x,y) pos. Specifically for 8-bit display devices such as IS31FL3731; no + color reduction/expansion is performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], int16_t w, + int16_t h) { + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + writePixel(x + i, y, (uint8_t)pgm_read_byte(&bitmap[j * w + i])); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 8-bit image (grayscale) at the specified (x,y) + pos. Specifically for 8-bit display devices such as IS31FL3731; no color + reduction/expansion is performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, + int16_t w, int16_t h) { + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + writePixel(x + i, y, bitmap[j * w + i]); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 8-bit image (grayscale) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (grayscale and mask) must be PROGMEM-resident. + Specifically for 8-bit display devices such as IS31FL3731; no color + reduction/expansion is performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param mask byte array with mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], + const uint8_t mask[], int16_t w, + int16_t h) { + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = pgm_read_byte(&mask[j * bw + i / 8]); + if (byte & 0x80) { + writePixel(x + i, y, (uint8_t)pgm_read_byte(&bitmap[j * w + i])); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 8-bit image (grayscale) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (grayscale and mask) must be RAM-residentt, no mix-and-match + Specifically for 8-bit display devices such as IS31FL3731; no color + reduction/expansion is performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param mask byte array with mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, + uint8_t *mask, int16_t w, int16_t h) { + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = mask[j * bw + i / 8]; + if (byte & 0x80) { + writePixel(x + i, y, bitmap[j * w + i]); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) at the specified + (x,y) position. For 16-bit display devices; no color reduction performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], + int16_t w, int16_t h) { + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + writePixel(x + i, y, pgm_read_word(&bitmap[j * w + i])); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) + position. For 16-bit display devices; no color reduction performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, + int16_t w, int16_t h) { + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + writePixel(x + i, y, bitmap[j * w + i]); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. BOTH + buffers (color and mask) must be PROGMEM-resident. For 16-bit display + devices; no color reduction performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param mask byte array with monochrome mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], + const uint8_t mask[], int16_t w, int16_t h) { + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = pgm_read_byte(&mask[j * bw + i / 8]); + if (byte & 0x80) { + writePixel(x + i, y, pgm_read_word(&bitmap[j * w + i])); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask (set + bits = opaque, unset bits = clear) at the specified (x,y) position. BOTH + buffers (color and mask) must be RAM-resident. For 16-bit display devices; no + color reduction performed. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param mask byte array with monochrome mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, + uint8_t *mask, int16_t w, int16_t h) { + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte <<= 1; + else + byte = mask[j * bw + i / 8]; + if (byte & 0x80) { + writePixel(x + i, y, bitmap[j * w + i]); + } + } + } + endWrite(); +} + +// TEXT- AND CHARACTER-HANDLING FUNCTIONS ---------------------------------- + +// Draw a character +/**************************************************************************/ +/*! + @brief Draw a single character + @param x Bottom left corner x coordinate + @param y Bottom left corner y coordinate + @param c The 8-bit font-indexed character (likely ascii) + @param color 16-bit 5-6-5 Color to draw chraracter with + @param bg 16-bit 5-6-5 Color to fill background with (if same as color, + no background) + @param size Font magnification level, 1 is 'original' size +*/ +/**************************************************************************/ +void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size) { + drawChar(x, y, c, color, bg, size, size); +} + +// Draw a character +/**************************************************************************/ +/*! + @brief Draw a single character + @param x Bottom left corner x coordinate + @param y Bottom left corner y coordinate + @param c The 8-bit font-indexed character (likely ascii) + @param color 16-bit 5-6-5 Color to draw chraracter with + @param bg 16-bit 5-6-5 Color to fill background with (if same as color, + no background) + @param size_x Font magnification level in X-axis, 1 is 'original' size + @param size_y Font magnification level in Y-axis, 1 is 'original' size +*/ +/**************************************************************************/ +void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size_x, + uint8_t size_y) { + + if (!gfxFont) { // 'Classic' built-in font + + if ((x >= _width) || // Clip right + (y >= _height) || // Clip bottom + ((x + 6 * size_x - 1) < 0) || // Clip left + ((y + 8 * size_y - 1) < 0)) // Clip top + return; + + if (!_cp437 && (c >= 176)) + c++; // Handle 'classic' charset behavior + + startWrite(); + for (int8_t i = 0; i < 5; i++) { // Char bitmap = 5 columns + uint8_t line = pgm_read_byte(&font[c * 5 + i]); + for (int8_t j = 0; j < 8; j++, line >>= 1) { + if (line & 1) { + if (size_x == 1 && size_y == 1) + writePixel(x + i, y + j, color); + else + writeFillRect(x + i * size_x, y + j * size_y, size_x, size_y, + color); + } else if (bg != color) { + if (size_x == 1 && size_y == 1) + writePixel(x + i, y + j, bg); + else + writeFillRect(x + i * size_x, y + j * size_y, size_x, size_y, bg); + } + } + } + if (bg != color) { // If opaque, draw vertical line for last column + if (size_x == 1 && size_y == 1) + writeFastVLine(x + 5, y, 8, bg); + else + writeFillRect(x + 5 * size_x, y, size_x, 8 * size_y, bg); + } + endWrite(); + + } else { // Custom font + + // Character is assumed previously filtered by write() to eliminate + // newlines, returns, non-printable characters, etc. Calling + // drawChar() directly with 'bad' characters of font may cause mayhem! + + c -= (uint8_t)pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c); + uint8_t *bitmap = pgm_read_bitmap_ptr(gfxFont); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits = 0, bit = 0; + int16_t xo16 = 0, yo16 = 0; + + if (size_x > 1 || size_y > 1) { + xo16 = xo; + yo16 = yo; + } + + // Todo: Add character clipping here + + // NOTE: THERE IS NO 'BACKGROUND' COLOR OPTION ON CUSTOM FONTS. + // THIS IS ON PURPOSE AND BY DESIGN. The background color feature + // has typically been used with the 'classic' font to overwrite old + // screen contents with new data. This ONLY works because the + // characters are a uniform size; it's not a sensible thing to do with + // proportionally-spaced fonts with glyphs of varying sizes (and that + // may overlap). To replace previously-drawn text when using a custom + // font, use the getTextBounds() function to determine the smallest + // rectangle encompassing a string, erase the area with fillRect(), + // then draw new text. This WILL infortunately 'blink' the text, but + // is unavoidable. Drawing 'background' pixels will NOT fix this, + // only creates a new set of problems. Have an idea to work around + // this (a canvas object type for MCUs that can afford the RAM and + // displays supporting setAddrWindow() and pushColors()), but haven't + // implemented this yet. + + startWrite(); + for (yy = 0; yy < h; yy++) { + for (xx = 0; xx < w; xx++) { + if (!(bit++ & 7)) { + bits = pgm_read_byte(&bitmap[bo++]); + } + if (bits & 0x80) { + if (size_x == 1 && size_y == 1) { + writePixel(x + xo + xx, y + yo + yy, color); + } else { + writeFillRect(x + (xo16 + xx) * size_x, y + (yo16 + yy) * size_y, + size_x, size_y, color); + } + } + bits <<= 1; + } + } + endWrite(); + + } // End classic vs custom font +} +/**************************************************************************/ +/*! + @brief Print one byte/character of data, used to support print() + @param c The 8-bit ascii character to write +*/ +/**************************************************************************/ +size_t Adafruit_GFX::write(uint8_t c) { + if (!gfxFont) { // 'Classic' built-in font + + if (c == '\n') { // Newline? + cursor_x = 0; // Reset x to zero, + cursor_y += textsize_y * 8; // advance y one line + } else if (c != '\r') { // Ignore carriage returns + if (wrap && ((cursor_x + textsize_x * 6) > _width)) { // Off right? + cursor_x = 0; // Reset x to zero, + cursor_y += textsize_y * 8; // advance y one line + } + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, + textsize_y); + cursor_x += textsize_x * 6; // Advance x one char + } + + } else { // Custom font + + if (c == '\n') { + cursor_x = 0; + cursor_y += + (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else if (c != '\r') { + uint8_t first = pgm_read_byte(&gfxFont->first); + if ((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if ((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic + if (wrap && ((cursor_x + textsize_x * (xo + w)) > _width)) { + cursor_x = 0; + cursor_y += (int16_t)textsize_y * + (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, + textsize_y); + } + cursor_x += + (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize_x; + } + } + } + return 1; +} + +/**************************************************************************/ +/*! + @brief Set text 'magnification' size. Each increase in s makes 1 pixel + that much bigger. + @param s Desired text size. 1 is default 6x8, 2 is 12x16, 3 is 18x24, etc +*/ +/**************************************************************************/ +void Adafruit_GFX::setTextSize(uint8_t s) { setTextSize(s, s); } + +/**************************************************************************/ +/*! + @brief Set text 'magnification' size. Each increase in s makes 1 pixel + that much bigger. + @param s_x Desired text width magnification level in X-axis. 1 is default + @param s_y Desired text width magnification level in Y-axis. 1 is default +*/ +/**************************************************************************/ +void Adafruit_GFX::setTextSize(uint8_t s_x, uint8_t s_y) { + textsize_x = (s_x > 0) ? s_x : 1; + textsize_y = (s_y > 0) ? s_y : 1; +} + +/**************************************************************************/ +/*! + @brief Set rotation setting for display + @param x 0 thru 3 corresponding to 4 cardinal rotations +*/ +/**************************************************************************/ +void Adafruit_GFX::setRotation(uint8_t x) { + rotation = (x & 3); + switch (rotation) { + case 0: + case 2: + _width = WIDTH; + _height = HEIGHT; + break; + case 1: + case 3: + _width = HEIGHT; + _height = WIDTH; + break; + } +} + +/**************************************************************************/ +/*! + @brief Set the font to display when print()ing, either custom or default + @param f The GFXfont object, if NULL use built in 6x8 font +*/ +/**************************************************************************/ +void Adafruit_GFX::setFont(const GFXfont *f) { + if (f) { // Font struct pointer passed in? + if (!gfxFont) { // And no current font struct? + // Switching from classic to new font behavior. + // Move cursor pos down 6 pixels so it's on baseline. + cursor_y += 6; + } + } else if (gfxFont) { // NULL passed. Current font struct defined? + // Switching from new to classic font behavior. + // Move cursor pos up 6 pixels so it's at top-left of char. + cursor_y -= 6; + } + gfxFont = (GFXfont *)f; +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a character with current font/size. + Broke this out as it's used by both the PROGMEM- and RAM-resident + getTextBounds() functions. + @param c The ASCII character in question + @param x Pointer to x location of character. Value is modified by + this function to advance to next character. + @param y Pointer to y location of character. Value is modified by + this function to advance to next character. + @param minx Pointer to minimum X coordinate, passed in to AND returned + by this function -- this is used to incrementally build a + bounding rectangle for a string. + @param miny Pointer to minimum Y coord, passed in AND returned. + @param maxx Pointer to maximum X coord, passed in AND returned. + @param maxy Pointer to maximum Y coord, passed in AND returned. +*/ +/**************************************************************************/ +void Adafruit_GFX::charBounds(unsigned char c, int16_t *x, int16_t *y, + int16_t *minx, int16_t *miny, int16_t *maxx, + int16_t *maxy) { + + if (gfxFont) { + + if (c == '\n') { // Newline? + *x = 0; // Reset x to zero, advance y by one line + *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else if (c != '\r') { // Not a carriage return; is normal char + uint8_t first = pgm_read_byte(&gfxFont->first), + last = pgm_read_byte(&gfxFont->last); + if ((c >= first) && (c <= last)) { // Char present in this font? + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); + uint8_t gw = pgm_read_byte(&glyph->width), + gh = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + if (wrap && ((*x + (((int16_t)xo + gw) * textsize_x)) > _width)) { + *x = 0; // Reset x to zero, advance y by one line + *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + int16_t tsx = (int16_t)textsize_x, tsy = (int16_t)textsize_y, + x1 = *x + xo * tsx, y1 = *y + yo * tsy, x2 = x1 + gw * tsx - 1, + y2 = y1 + gh * tsy - 1; + if (x1 < *minx) + *minx = x1; + if (y1 < *miny) + *miny = y1; + if (x2 > *maxx) + *maxx = x2; + if (y2 > *maxy) + *maxy = y2; + *x += xa * tsx; + } + } + + } else { // Default font + + if (c == '\n') { // Newline? + *x = 0; // Reset x to zero, + *y += textsize_y * 8; // advance y one line + // min/max x/y unchaged -- that waits for next 'normal' character + } else if (c != '\r') { // Normal char; ignore carriage returns + if (wrap && ((*x + textsize_x * 6) > _width)) { // Off right? + *x = 0; // Reset x to zero, + *y += textsize_y * 8; // advance y one line + } + int x2 = *x + textsize_x * 6 - 1, // Lower-right pixel of char + y2 = *y + textsize_y * 8 - 1; + if (x2 > *maxx) + *maxx = x2; // Track max x, y + if (y2 > *maxy) + *maxy = y2; + if (*x < *minx) + *minx = *x; // Track min x, y + if (*y < *miny) + *miny = *y; + *x += textsize_x * 6; // Advance x one char + } + } +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a string with current font/size. + Pass string and a cursor position, returns UL corner and W,H. + @param str The ASCII string to measure + @param x The current cursor X + @param y The current cursor Y + @param x1 The boundary X coordinate, returned by function + @param y1 The boundary Y coordinate, returned by function + @param w The boundary width, returned by function + @param h The boundary height, returned by function +*/ +/**************************************************************************/ +void Adafruit_GFX::getTextBounds(const char *str, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, + uint16_t *h) { + + uint8_t c; // Current character + int16_t minx = 0x7FFF, miny = 0x7FFF, maxx = -1, maxy = -1; // Bound rect + // Bound rect is intentionally initialized inverted, so 1st char sets it + + *x1 = x; // Initial position is value passed in + *y1 = y; + *w = *h = 0; // Initial size is zero + + while ((c = *str++)) { + // charBounds() modifies x/y to advance for each character, + // and min/max x/y are updated to incrementally build bounding rect. + charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); + } + + if (maxx >= minx) { // If legit string bounds were found... + *x1 = minx; // Update x1 to least X coord, + *w = maxx - minx + 1; // And w to bound rect width + } + if (maxy >= miny) { // Same for height + *y1 = miny; + *h = maxy - miny + 1; + } +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a string with current font/size. Pass + string and a cursor position, returns UL corner and W,H. + @param str The ascii string to measure (as an arduino String() class) + @param x The current cursor X + @param y The current cursor Y + @param x1 The boundary X coordinate, set by function + @param y1 The boundary Y coordinate, set by function + @param w The boundary width, set by function + @param h The boundary height, set by function +*/ +/**************************************************************************/ +void Adafruit_GFX::getTextBounds(const String &str, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, + uint16_t *h) { + if (str.length() != 0) { + getTextBounds(const_cast(str.c_str()), x, y, x1, y1, w, h); + } +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a PROGMEM string with current + font/size. Pass string and a cursor position, returns UL corner and W,H. + @param str The flash-memory ascii string to measure + @param x The current cursor X + @param y The current cursor Y + @param x1 The boundary X coordinate, set by function + @param y1 The boundary Y coordinate, set by function + @param w The boundary width, set by function + @param h The boundary height, set by function +*/ +/**************************************************************************/ +void Adafruit_GFX::getTextBounds(const __FlashStringHelper *str, int16_t x, + int16_t y, int16_t *x1, int16_t *y1, + uint16_t *w, uint16_t *h) { + uint8_t *s = (uint8_t *)str, c; + + *x1 = x; + *y1 = y; + *w = *h = 0; + + int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; + + while ((c = pgm_read_byte(s++))) + charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); + + if (maxx >= minx) { + *x1 = minx; + *w = maxx - minx + 1; + } + if (maxy >= miny) { + *y1 = miny; + *h = maxy - miny + 1; + } +} + +/**************************************************************************/ +/*! + @brief Invert the display (ideally using built-in hardware command) + @param i True if you want to invert, false to make 'normal' +*/ +/**************************************************************************/ +void Adafruit_GFX::invertDisplay(bool i) { + // Do nothing, must be subclassed if supported by hardware + (void)i; // disable -Wunused-parameter warning +} + +/***************************************************************************/ + +/**************************************************************************/ +/*! + @brief Create a simple drawn button UI element +*/ +/**************************************************************************/ +Adafruit_GFX_Button::Adafruit_GFX_Button(void) { _gfx = 0; } + +/**************************************************************************/ +/*! + @brief Initialize button with our desired color/size/settings + @param gfx Pointer to our display so we can draw to it! + @param x The X coordinate of the center of the button + @param y The Y coordinate of the center of the button + @param w Width of the buttton + @param h Height of the buttton + @param outline Color of the outline (16-bit 5-6-5 standard) + @param fill Color of the button fill (16-bit 5-6-5 standard) + @param textcolor Color of the button label (16-bit 5-6-5 standard) + @param label Ascii string of the text inside the button + @param textsize The font magnification of the label text +*/ +/**************************************************************************/ +// Classic initButton() function: pass center & size +void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, + uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) { + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor, + label, textsize); +} + +/**************************************************************************/ +/*! + @brief Initialize button with our desired color/size/settings + @param gfx Pointer to our display so we can draw to it! + @param x The X coordinate of the center of the button + @param y The Y coordinate of the center of the button + @param w Width of the buttton + @param h Height of the buttton + @param outline Color of the outline (16-bit 5-6-5 standard) + @param fill Color of the button fill (16-bit 5-6-5 standard) + @param textcolor Color of the button label (16-bit 5-6-5 standard) + @param label Ascii string of the text inside the button + @param textsize_x The font magnification in X-axis of the label text + @param textsize_y The font magnification in Y-axis of the label text +*/ +/**************************************************************************/ +// Classic initButton() function: pass center & size +void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, + uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize_x, + uint8_t textsize_y) { + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor, + label, textsize_x, textsize_y); +} + +/**************************************************************************/ +/*! + @brief Initialize button with our desired color/size/settings, with + upper-left coordinates + @param gfx Pointer to our display so we can draw to it! + @param x1 The X coordinate of the Upper-Left corner of the button + @param y1 The Y coordinate of the Upper-Left corner of the button + @param w Width of the buttton + @param h Height of the buttton + @param outline Color of the outline (16-bit 5-6-5 standard) + @param fill Color of the button fill (16-bit 5-6-5 standard) + @param textcolor Color of the button label (16-bit 5-6-5 standard) + @param label Ascii string of the text inside the button + @param textsize The font magnification of the label text +*/ +/**************************************************************************/ +void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1, + int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, + uint8_t textsize) { + initButtonUL(gfx, x1, y1, w, h, outline, fill, textcolor, label, textsize, + textsize); +} + +/**************************************************************************/ +/*! + @brief Initialize button with our desired color/size/settings, with + upper-left coordinates + @param gfx Pointer to our display so we can draw to it! + @param x1 The X coordinate of the Upper-Left corner of the button + @param y1 The Y coordinate of the Upper-Left corner of the button + @param w Width of the buttton + @param h Height of the buttton + @param outline Color of the outline (16-bit 5-6-5 standard) + @param fill Color of the button fill (16-bit 5-6-5 standard) + @param textcolor Color of the button label (16-bit 5-6-5 standard) + @param label Ascii string of the text inside the button + @param textsize_x The font magnification in X-axis of the label text + @param textsize_y The font magnification in Y-axis of the label text +*/ +/**************************************************************************/ +void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1, + int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, + uint8_t textsize_x, uint8_t textsize_y) { + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _fillcolor = fill; + _textcolor = textcolor; + _textsize_x = textsize_x; + _textsize_y = textsize_y; + _gfx = gfx; + strncpy(_label, label, 9); +} + +/**************************************************************************/ +/*! + @brief Draw the button on the screen + @param inverted Whether to draw with fill/text swapped to indicate + 'pressed' +*/ +/**************************************************************************/ +void Adafruit_GFX_Button::drawButton(bool inverted) { + uint16_t fill, outline, text; + + if (!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + _gfx->setCursor(_x1 + (_w / 2) - (strlen(_label) * 3 * _textsize_x), + _y1 + (_h / 2) - (4 * _textsize_y)); + _gfx->setTextColor(text); + _gfx->setTextSize(_textsize_x, _textsize_y); + _gfx->print(_label); +} + +/**************************************************************************/ +/*! + @brief Helper to let us know if a coordinate is within the bounds of the + button + @param x The X coordinate to check + @param y The Y coordinate to check + @returns True if within button graphics outline +*/ +/**************************************************************************/ +bool Adafruit_GFX_Button::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (int16_t)(_x1 + _w)) && (y >= _y1) && + (y < (int16_t)(_y1 + _h))); +} + +/**************************************************************************/ +/*! + @brief Query whether the button was pressed since we last checked state + @returns True if was not-pressed before, now is. +*/ +/**************************************************************************/ +bool Adafruit_GFX_Button::justPressed() { return (currstate && !laststate); } + +/**************************************************************************/ +/*! + @brief Query whether the button was released since we last checked state + @returns True if was pressed before, now is not. +*/ +/**************************************************************************/ +bool Adafruit_GFX_Button::justReleased() { return (!currstate && laststate); } + +// ------------------------------------------------------------------------- + +// GFXcanvas1, GFXcanvas8 and GFXcanvas16 (currently a WIP, don't get too +// comfy with the implementation) provide 1-, 8- and 16-bit offscreen +// canvases, the address of which can be passed to drawBitmap() or +// pushColors() (the latter appears only in a couple of GFX-subclassed TFT +// libraries at this time). This is here mostly to help with the recently- +// added proportionally-spaced fonts; adds a way to refresh a section of the +// screen without a massive flickering clear-and-redraw...but maybe you'll +// find other uses too. VERY RAM-intensive, since the buffer is in MCU +// memory and not the display driver...GXFcanvas1 might be minimally useful +// on an Uno-class board, but this and the others are much more likely to +// require at least a Mega or various recent ARM-type boards (recommended, +// as the text+bitmap draw can be pokey). GFXcanvas1 requires 1 bit per +// pixel (rounded up to nearest byte per scanline), GFXcanvas8 is 1 byte +// per pixel (no scanline pad), and GFXcanvas16 uses 2 bytes per pixel (no +// scanline pad). +// NOT EXTENSIVELY TESTED YET. MAY CONTAIN WORST BUGS KNOWN TO HUMANKIND. + +#ifdef __AVR__ +// Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR +const uint8_t PROGMEM GFXcanvas1::GFXsetBit[] = {0x80, 0x40, 0x20, 0x10, + 0x08, 0x04, 0x02, 0x01}; +const uint8_t PROGMEM GFXcanvas1::GFXclrBit[] = {0x7F, 0xBF, 0xDF, 0xEF, + 0xF7, 0xFB, 0xFD, 0xFE}; +#endif + +/**************************************************************************/ +/*! + @brief Instatiate a GFX 1-bit canvas context for graphics + @param w Display width, in pixels + @param h Display height, in pixels +*/ +/**************************************************************************/ +GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { + uint16_t bytes = ((w + 7) / 8) * h; + if ((buffer = (uint8_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } +} + +/**************************************************************************/ +/*! + @brief Delete the canvas, free memory +*/ +/**************************************************************************/ +GFXcanvas1::~GFXcanvas1(void) { + if (buffer) + free(buffer); +} + +/**************************************************************************/ +/*! + @brief Draw a pixel to the canvas framebuffer + @param x x coordinate + @param y y coordinate + @param color Binary (on or off) color to fill with +*/ +/**************************************************************************/ +void GFXcanvas1::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (buffer) { + if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) + return; + + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } + + uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)]; +#ifdef __AVR__ + if (color) + *ptr |= pgm_read_byte(&GFXsetBit[x & 7]); + else + *ptr &= pgm_read_byte(&GFXclrBit[x & 7]); +#else + if (color) + *ptr |= 0x80 >> (x & 7); + else + *ptr &= ~(0x80 >> (x & 7)); +#endif + } +} + +/**********************************************************************/ +/*! + @brief Get the pixel color value at a given coordinate + @param x x coordinate + @param y y coordinate + @returns The desired pixel's binary color value, either 0x1 (on) or 0x0 + (off) +*/ +/**********************************************************************/ +bool GFXcanvas1::getPixel(int16_t x, int16_t y) const { + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } + return getRawPixel(x, y); +} + +/**********************************************************************/ +/*! + @brief Get the pixel color value at a given, unrotated coordinate. + This method is intended for hardware drivers to get pixel value + in physical coordinates. + @param x x coordinate + @param y y coordinate + @returns The desired pixel's binary color value, either 0x1 (on) or 0x0 + (off) +*/ +/**********************************************************************/ +bool GFXcanvas1::getRawPixel(int16_t x, int16_t y) const { + if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT)) + return 0; + if (this->getBuffer()) { + uint8_t *buffer = this->getBuffer(); + uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)]; + +#ifdef __AVR__ + return ((*ptr) & pgm_read_byte(&GFXsetBit[x & 7])) != 0; +#else + return ((*ptr) & (0x80 >> (x & 7))) != 0; +#endif + } + return 0; +} + +/**************************************************************************/ +/*! + @brief Fill the framebuffer completely with one color + @param color Binary (on or off) color to fill with +*/ +/**************************************************************************/ +void GFXcanvas1::fillScreen(uint16_t color) { + if (buffer) { + uint16_t bytes = ((WIDTH + 7) / 8) * HEIGHT; + memset(buffer, color ? 0xFF : 0x00, bytes); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized vertical line drawing + @param x Line horizontal start point + @param y Line vertical start point + @param h Length of vertical line to be drawn, including first point + @param color Color to fill with +*/ +/**************************************************************************/ +void GFXcanvas1::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + + if (h < 0) { // Convert negative heights to positive equivalent + h *= -1; + y -= h - 1; + if (y < 0) { + h += y; + y = 0; + } + } + + // Edge rejection (no-draw if totally off canvas) + if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) { + return; + } + + if (y < 0) { // Clip top + h += y; + y = 0; + } + if (y + h > height()) { // Clip bottom + h = height() - y; + } + + if (getRotation() == 0) { + drawFastRawVLine(x, y, h, color); + } else if (getRotation() == 1) { + int16_t t = x; + x = WIDTH - 1 - y; + y = t; + x -= h - 1; + drawFastRawHLine(x, y, h, color); + } else if (getRotation() == 2) { + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + + y -= h - 1; + drawFastRawVLine(x, y, h, color); + } else if (getRotation() == 3) { + int16_t t = x; + x = y; + y = HEIGHT - 1 - t; + drawFastRawHLine(x, y, h, color); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized horizontal line drawing + @param x Line horizontal start point + @param y Line vertical start point + @param w Length of horizontal line to be drawn, including first point + @param color Color to fill with +*/ +/**************************************************************************/ +void GFXcanvas1::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if (w < 0) { // Convert negative widths to positive equivalent + w *= -1; + x -= w - 1; + if (x < 0) { + w += x; + x = 0; + } + } + + // Edge rejection (no-draw if totally off canvas) + if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) { + return; + } + + if (x < 0) { // Clip left + w += x; + x = 0; + } + if (x + w >= width()) { // Clip right + w = width() - x; + } + + if (getRotation() == 0) { + drawFastRawHLine(x, y, w, color); + } else if (getRotation() == 1) { + int16_t t = x; + x = WIDTH - 1 - y; + y = t; + drawFastRawVLine(x, y, w, color); + } else if (getRotation() == 2) { + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + + x -= w - 1; + drawFastRawHLine(x, y, w, color); + } else if (getRotation() == 3) { + int16_t t = x; + x = y; + y = HEIGHT - 1 - t; + y -= w - 1; + drawFastRawVLine(x, y, w, color); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized vertical line drawing into the raw canvas buffer + @param x Line horizontal start point + @param y Line vertical start point + @param h length of vertical line to be drawn, including first point + @param color Binary (on or off) color to fill with +*/ +/**************************************************************************/ +void GFXcanvas1::drawFastRawVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + // x & y already in raw (rotation 0) coordinates, no need to transform. + int16_t row_bytes = ((WIDTH + 7) / 8); + uint8_t *buffer = this->getBuffer(); + uint8_t *ptr = &buffer[(x / 8) + y * row_bytes]; + + if (color > 0) { +#ifdef __AVR__ + uint8_t bit_mask = pgm_read_byte(&GFXsetBit[x & 7]); +#else + uint8_t bit_mask = (0x80 >> (x & 7)); +#endif + for (int16_t i = 0; i < h; i++) { + *ptr |= bit_mask; + ptr += row_bytes; + } + } else { +#ifdef __AVR__ + uint8_t bit_mask = pgm_read_byte(&GFXclrBit[x & 7]); +#else + uint8_t bit_mask = ~(0x80 >> (x & 7)); +#endif + for (int16_t i = 0; i < h; i++) { + *ptr &= bit_mask; + ptr += row_bytes; + } + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized horizontal line drawing into the raw canvas buffer + @param x Line horizontal start point + @param y Line vertical start point + @param w length of horizontal line to be drawn, including first point + @param color Binary (on or off) color to fill with +*/ +/**************************************************************************/ +void GFXcanvas1::drawFastRawHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + // x & y already in raw (rotation 0) coordinates, no need to transform. + int16_t rowBytes = ((WIDTH + 7) / 8); + uint8_t *buffer = this->getBuffer(); + uint8_t *ptr = &buffer[(x / 8) + y * rowBytes]; + size_t remainingWidthBits = w; + + // check to see if first byte needs to be partially filled + if ((x & 7) > 0) { + // create bit mask for first byte + uint8_t startByteBitMask = 0x00; + for (int8_t i = (x & 7); ((i < 8) && (remainingWidthBits > 0)); i++) { +#ifdef __AVR__ + startByteBitMask |= pgm_read_byte(&GFXsetBit[i]); +#else + startByteBitMask |= (0x80 >> i); +#endif + remainingWidthBits--; + } + if (color > 0) { + *ptr |= startByteBitMask; + } else { + *ptr &= ~startByteBitMask; + } + + ptr++; + } + + // do the next remainingWidthBits bits + if (remainingWidthBits > 0) { + size_t remainingWholeBytes = remainingWidthBits / 8; + size_t lastByteBits = remainingWidthBits % 8; + uint8_t wholeByteColor = color > 0 ? 0xFF : 0x00; + + memset(ptr, wholeByteColor, remainingWholeBytes); + + if (lastByteBits > 0) { + uint8_t lastByteBitMask = 0x00; + for (size_t i = 0; i < lastByteBits; i++) { +#ifdef __AVR__ + lastByteBitMask |= pgm_read_byte(&GFXsetBit[i]); +#else + lastByteBitMask |= (0x80 >> i); +#endif + } + ptr += remainingWholeBytes; + + if (color > 0) { + *ptr |= lastByteBitMask; + } else { + *ptr &= ~lastByteBitMask; + } + } + } +} + +/**************************************************************************/ +/*! + @brief Instatiate a GFX 8-bit canvas context for graphics + @param w Display width, in pixels + @param h Display height, in pixels +*/ +/**************************************************************************/ +GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { + uint32_t bytes = w * h; + if ((buffer = (uint8_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } +} + +/**************************************************************************/ +/*! + @brief Delete the canvas, free memory +*/ +/**************************************************************************/ +GFXcanvas8::~GFXcanvas8(void) { + if (buffer) + free(buffer); +} + +/**************************************************************************/ +/*! + @brief Draw a pixel to the canvas framebuffer + @param x x coordinate + @param y y coordinate + @param color 8-bit Color to fill with. Only lower byte of uint16_t is used. +*/ +/**************************************************************************/ +void GFXcanvas8::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (buffer) { + if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) + return; + + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } + + buffer[x + y * WIDTH] = color; + } +} + +/**********************************************************************/ +/*! + @brief Get the pixel color value at a given coordinate + @param x x coordinate + @param y y coordinate + @returns The desired pixel's 8-bit color value +*/ +/**********************************************************************/ +uint8_t GFXcanvas8::getPixel(int16_t x, int16_t y) const { + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } + return getRawPixel(x, y); +} + +/**********************************************************************/ +/*! + @brief Get the pixel color value at a given, unrotated coordinate. + This method is intended for hardware drivers to get pixel value + in physical coordinates. + @param x x coordinate + @param y y coordinate + @returns The desired pixel's 8-bit color value +*/ +/**********************************************************************/ +uint8_t GFXcanvas8::getRawPixel(int16_t x, int16_t y) const { + if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT)) + return 0; + if (buffer) { + return buffer[x + y * WIDTH]; + } + return 0; +} + +/**************************************************************************/ +/*! + @brief Fill the framebuffer completely with one color + @param color 8-bit Color to fill with. Only lower byte of uint16_t is used. +*/ +/**************************************************************************/ +void GFXcanvas8::fillScreen(uint16_t color) { + if (buffer) { + memset(buffer, color, WIDTH * HEIGHT); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized vertical line drawing + @param x Line horizontal start point + @param y Line vertical start point + @param h Length of vertical line to be drawn, including first point + @param color 8-bit Color to fill with. Only lower byte of uint16_t is + used. +*/ +/**************************************************************************/ +void GFXcanvas8::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if (h < 0) { // Convert negative heights to positive equivalent + h *= -1; + y -= h - 1; + if (y < 0) { + h += y; + y = 0; + } + } + + // Edge rejection (no-draw if totally off canvas) + if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) { + return; + } + + if (y < 0) { // Clip top + h += y; + y = 0; + } + if (y + h > height()) { // Clip bottom + h = height() - y; + } + + if (getRotation() == 0) { + drawFastRawVLine(x, y, h, color); + } else if (getRotation() == 1) { + int16_t t = x; + x = WIDTH - 1 - y; + y = t; + x -= h - 1; + drawFastRawHLine(x, y, h, color); + } else if (getRotation() == 2) { + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + + y -= h - 1; + drawFastRawVLine(x, y, h, color); + } else if (getRotation() == 3) { + int16_t t = x; + x = y; + y = HEIGHT - 1 - t; + drawFastRawHLine(x, y, h, color); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized horizontal line drawing + @param x Line horizontal start point + @param y Line vertical start point + @param w Length of horizontal line to be drawn, including 1st point + @param color 8-bit Color to fill with. Only lower byte of uint16_t is + used. +*/ +/**************************************************************************/ +void GFXcanvas8::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + + if (w < 0) { // Convert negative widths to positive equivalent + w *= -1; + x -= w - 1; + if (x < 0) { + w += x; + x = 0; + } + } + + // Edge rejection (no-draw if totally off canvas) + if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) { + return; + } + + if (x < 0) { // Clip left + w += x; + x = 0; + } + if (x + w >= width()) { // Clip right + w = width() - x; + } + + if (getRotation() == 0) { + drawFastRawHLine(x, y, w, color); + } else if (getRotation() == 1) { + int16_t t = x; + x = WIDTH - 1 - y; + y = t; + drawFastRawVLine(x, y, w, color); + } else if (getRotation() == 2) { + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + + x -= w - 1; + drawFastRawHLine(x, y, w, color); + } else if (getRotation() == 3) { + int16_t t = x; + x = y; + y = HEIGHT - 1 - t; + y -= w - 1; + drawFastRawVLine(x, y, w, color); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized vertical line drawing into the raw canvas buffer + @param x Line horizontal start point + @param y Line vertical start point + @param h length of vertical line to be drawn, including first point + @param color 8-bit Color to fill with. Only lower byte of uint16_t is + used. +*/ +/**************************************************************************/ +void GFXcanvas8::drawFastRawVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + // x & y already in raw (rotation 0) coordinates, no need to transform. + uint8_t *buffer_ptr = buffer + y * WIDTH + x; + for (int16_t i = 0; i < h; i++) { + (*buffer_ptr) = color; + buffer_ptr += WIDTH; + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized horizontal line drawing into the raw canvas buffer + @param x Line horizontal start point + @param y Line vertical start point + @param w length of horizontal line to be drawn, including first point + @param color 8-bit Color to fill with. Only lower byte of uint16_t is + used. +*/ +/**************************************************************************/ +void GFXcanvas8::drawFastRawHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + // x & y already in raw (rotation 0) coordinates, no need to transform. + memset(buffer + y * WIDTH + x, color, w); +} + +/**************************************************************************/ +/*! + @brief Instatiate a GFX 16-bit canvas context for graphics + @param w Display width, in pixels + @param h Display height, in pixels +*/ +/**************************************************************************/ +GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { + uint32_t bytes = w * h * 2; + if ((buffer = (uint16_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } +} + +/**************************************************************************/ +/*! + @brief Delete the canvas, free memory +*/ +/**************************************************************************/ +GFXcanvas16::~GFXcanvas16(void) { + if (buffer) + free(buffer); +} + +/**************************************************************************/ +/*! + @brief Draw a pixel to the canvas framebuffer + @param x x coordinate + @param y y coordinate + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void GFXcanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (buffer) { + if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) + return; + + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } + + buffer[x + y * WIDTH] = color; + } +} + +/**********************************************************************/ +/*! + @brief Get the pixel color value at a given coordinate + @param x x coordinate + @param y y coordinate + @returns The desired pixel's 16-bit 5-6-5 color value +*/ +/**********************************************************************/ +uint16_t GFXcanvas16::getPixel(int16_t x, int16_t y) const { + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } + return getRawPixel(x, y); +} + +/**********************************************************************/ +/*! + @brief Get the pixel color value at a given, unrotated coordinate. + This method is intended for hardware drivers to get pixel value + in physical coordinates. + @param x x coordinate + @param y y coordinate + @returns The desired pixel's 16-bit 5-6-5 color value +*/ +/**********************************************************************/ +uint16_t GFXcanvas16::getRawPixel(int16_t x, int16_t y) const { + if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT)) + return 0; + if (buffer) { + return buffer[x + y * WIDTH]; + } + return 0; +} + +/**************************************************************************/ +/*! + @brief Fill the framebuffer completely with one color + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void GFXcanvas16::fillScreen(uint16_t color) { + if (buffer) { + uint8_t hi = color >> 8, lo = color & 0xFF; + if (hi == lo) { + memset(buffer, lo, WIDTH * HEIGHT * 2); + } else { + uint32_t i, pixels = WIDTH * HEIGHT; + for (i = 0; i < pixels; i++) + buffer[i] = color; + } + } +} + +/**************************************************************************/ +/*! + @brief Reverses the "endian-ness" of each 16-bit pixel within the + canvas; little-endian to big-endian, or big-endian to little. + Most microcontrollers (such as SAMD) are little-endian, while + most displays tend toward big-endianness. All the drawing + functions (including RGB bitmap drawing) take care of this + automatically, but some specialized code (usually involving + DMA) can benefit from having pixel data already in the + display-native order. Note that this does NOT convert to a + SPECIFIC endian-ness, it just flips the bytes within each word. +*/ +/**************************************************************************/ +void GFXcanvas16::byteSwap(void) { + if (buffer) { + uint32_t i, pixels = WIDTH * HEIGHT; + for (i = 0; i < pixels; i++) + buffer[i] = __builtin_bswap16(buffer[i]); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized vertical line drawing + @param x Line horizontal start point + @param y Line vertical start point + @param h length of vertical line to be drawn, including first point + @param color color 16-bit 5-6-5 Color to draw line with +*/ +/**************************************************************************/ +void GFXcanvas16::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if (h < 0) { // Convert negative heights to positive equivalent + h *= -1; + y -= h - 1; + if (y < 0) { + h += y; + y = 0; + } + } + + // Edge rejection (no-draw if totally off canvas) + if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) { + return; + } + + if (y < 0) { // Clip top + h += y; + y = 0; + } + if (y + h > height()) { // Clip bottom + h = height() - y; + } + + if (getRotation() == 0) { + drawFastRawVLine(x, y, h, color); + } else if (getRotation() == 1) { + int16_t t = x; + x = WIDTH - 1 - y; + y = t; + x -= h - 1; + drawFastRawHLine(x, y, h, color); + } else if (getRotation() == 2) { + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + + y -= h - 1; + drawFastRawVLine(x, y, h, color); + } else if (getRotation() == 3) { + int16_t t = x; + x = y; + y = HEIGHT - 1 - t; + drawFastRawHLine(x, y, h, color); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized horizontal line drawing + @param x Line horizontal start point + @param y Line vertical start point + @param w Length of horizontal line to be drawn, including 1st point + @param color Color 16-bit 5-6-5 Color to draw line with +*/ +/**************************************************************************/ +void GFXcanvas16::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if (w < 0) { // Convert negative widths to positive equivalent + w *= -1; + x -= w - 1; + if (x < 0) { + w += x; + x = 0; + } + } + + // Edge rejection (no-draw if totally off canvas) + if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) { + return; + } + + if (x < 0) { // Clip left + w += x; + x = 0; + } + if (x + w >= width()) { // Clip right + w = width() - x; + } + + if (getRotation() == 0) { + drawFastRawHLine(x, y, w, color); + } else if (getRotation() == 1) { + int16_t t = x; + x = WIDTH - 1 - y; + y = t; + drawFastRawVLine(x, y, w, color); + } else if (getRotation() == 2) { + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + + x -= w - 1; + drawFastRawHLine(x, y, w, color); + } else if (getRotation() == 3) { + int16_t t = x; + x = y; + y = HEIGHT - 1 - t; + y -= w - 1; + drawFastRawVLine(x, y, w, color); + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized vertical line drawing into the raw canvas buffer + @param x Line horizontal start point + @param y Line vertical start point + @param h length of vertical line to be drawn, including first point + @param color color 16-bit 5-6-5 Color to draw line with +*/ +/**************************************************************************/ +void GFXcanvas16::drawFastRawVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + // x & y already in raw (rotation 0) coordinates, no need to transform. + uint16_t *buffer_ptr = buffer + y * WIDTH + x; + for (int16_t i = 0; i < h; i++) { + (*buffer_ptr) = color; + buffer_ptr += WIDTH; + } +} + +/**************************************************************************/ +/*! + @brief Speed optimized horizontal line drawing into the raw canvas buffer + @param x Line horizontal start point + @param y Line vertical start point + @param w length of horizontal line to be drawn, including first point + @param color color 16-bit 5-6-5 Color to draw line with +*/ +/**************************************************************************/ +void GFXcanvas16::drawFastRawHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + // x & y already in raw (rotation 0) coordinates, no need to transform. + uint32_t buffer_index = y * WIDTH + x; + for (uint32_t i = buffer_index; i < buffer_index + w; i++) { + buffer[i] = color; + } +} diff --git a/libraries/Adafruit_GFX_Library/Adafruit_GFX.h b/libraries/Adafruit_GFX_Library/Adafruit_GFX.h new file mode 100644 index 0000000..b9d63c6 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_GFX.h @@ -0,0 +1,393 @@ +#ifndef _ADAFRUIT_GFX_H +#define _ADAFRUIT_GFX_H + +#if ARDUINO >= 100 +#include "Arduino.h" +#include "Print.h" +#else +#include "WProgram.h" +#endif +#include "gfxfont.h" + +/// A generic graphics superclass that can handle all sorts of drawing. At a +/// minimum you can subclass and provide drawPixel(). At a maximum you can do a +/// ton of overriding to optimize. Used for any/all Adafruit displays! +class Adafruit_GFX : public Print { + +public: + Adafruit_GFX(int16_t w, int16_t h); // Constructor + + /**********************************************************************/ + /*! + @brief Draw to the screen/framebuffer/etc. + Must be overridden in subclass. + @param x X coordinate in pixels + @param y Y coordinate in pixels + @param color 16-bit pixel color. + */ + /**********************************************************************/ + virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0; + + // TRANSACTION API / CORE DRAW API + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + virtual void startWrite(void); + virtual void writePixel(int16_t x, int16_t y, uint16_t color); + virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color); + virtual void endWrite(void); + + // CONTROL API + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + virtual void setRotation(uint8_t r); + virtual void invertDisplay(bool i); + + // BASIC DRAW API + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + + // It's good to implement those, even if using transaction API + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + virtual void fillScreen(uint16_t color); + // Optional and probably not necessary to change + virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color); + virtual void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + + // These exist only with Adafruit_GFX (no subclass overrides) + void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); + void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, + uint16_t color); + void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); + void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, + int16_t delta, uint16_t color); + void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, + int16_t y2, uint16_t color); + void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, + int16_t y2, uint16_t color); + void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color); + void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color); + void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, + int16_t h, uint16_t color); + void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, + int16_t h, uint16_t color, uint16_t bg); + void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color); + void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color, uint16_t bg); + void drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, + int16_t h, uint16_t color); + void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], + int16_t w, int16_t h); + void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, + int16_t h); + void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], + const uint8_t mask[], int16_t w, int16_t h); + void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint8_t *mask, + int16_t w, int16_t h); + void drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w, + int16_t h); + void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, + int16_t h); + void drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], + const uint8_t mask[], int16_t w, int16_t h); + void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint8_t *mask, + int16_t w, int16_t h); + void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, + uint16_t bg, uint8_t size); + void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, + uint16_t bg, uint8_t size_x, uint8_t size_y); + void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, + int16_t *y1, uint16_t *w, uint16_t *h); + void getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, + int16_t *y1, uint16_t *w, uint16_t *h); + void setTextSize(uint8_t s); + void setTextSize(uint8_t sx, uint8_t sy); + void setFont(const GFXfont *f = NULL); + + /**********************************************************************/ + /*! + @brief Set text cursor location + @param x X coordinate in pixels + @param y Y coordinate in pixels + */ + /**********************************************************************/ + void setCursor(int16_t x, int16_t y) { + cursor_x = x; + cursor_y = y; + } + + /**********************************************************************/ + /*! + @brief Set text font color with transparant background + @param c 16-bit 5-6-5 Color to draw text with + @note For 'transparent' background, background and foreground + are set to same color rather than using a separate flag. + */ + /**********************************************************************/ + void setTextColor(uint16_t c) { textcolor = textbgcolor = c; } + + /**********************************************************************/ + /*! + @brief Set text font color with custom background color + @param c 16-bit 5-6-5 Color to draw text with + @param bg 16-bit 5-6-5 Color to draw background/fill with + */ + /**********************************************************************/ + void setTextColor(uint16_t c, uint16_t bg) { + textcolor = c; + textbgcolor = bg; + } + + /**********************************************************************/ + /*! + @brief Set whether text that is too long for the screen width should + automatically wrap around to the next line (else clip right). + @param w true for wrapping, false for clipping + */ + /**********************************************************************/ + void setTextWrap(bool w) { wrap = w; } + + /**********************************************************************/ + /*! + @brief Enable (or disable) Code Page 437-compatible charset. + There was an error in glcdfont.c for the longest time -- one + character (#176, the 'light shade' block) was missing -- this + threw off the index of every character that followed it. + But a TON of code has been written with the erroneous + character indices. By default, the library uses the original + 'wrong' behavior and old sketches will still work. Pass + 'true' to this function to use correct CP437 character values + in your code. + @param x true = enable (new behavior), false = disable (old behavior) + */ + /**********************************************************************/ + void cp437(bool x = true) { _cp437 = x; } + + using Print::write; +#if ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + + /************************************************************************/ + /*! + @brief Get width of the display, accounting for current rotation + @returns Width in pixels + */ + /************************************************************************/ + int16_t width(void) const { return _width; }; + + /************************************************************************/ + /*! + @brief Get height of the display, accounting for current rotation + @returns Height in pixels + */ + /************************************************************************/ + int16_t height(void) const { return _height; } + + /************************************************************************/ + /*! + @brief Get rotation setting for display + @returns 0 thru 3 corresponding to 4 cardinal rotations + */ + /************************************************************************/ + uint8_t getRotation(void) const { return rotation; } + + // get current cursor position (get rotation safe maximum values, + // using: width() for x, height() for y) + /************************************************************************/ + /*! + @brief Get text cursor X location + @returns X coordinate in pixels + */ + /************************************************************************/ + int16_t getCursorX(void) const { return cursor_x; } + + /************************************************************************/ + /*! + @brief Get text cursor Y location + @returns Y coordinate in pixels + */ + /************************************************************************/ + int16_t getCursorY(void) const { return cursor_y; }; + +protected: + void charBounds(unsigned char c, int16_t *x, int16_t *y, int16_t *minx, + int16_t *miny, int16_t *maxx, int16_t *maxy); + int16_t WIDTH; ///< This is the 'raw' display width - never changes + int16_t HEIGHT; ///< This is the 'raw' display height - never changes + int16_t _width; ///< Display width as modified by current rotation + int16_t _height; ///< Display height as modified by current rotation + int16_t cursor_x; ///< x location to start print()ing text + int16_t cursor_y; ///< y location to start print()ing text + uint16_t textcolor; ///< 16-bit background color for print() + uint16_t textbgcolor; ///< 16-bit text color for print() + uint8_t textsize_x; ///< Desired magnification in X-axis of text to print() + uint8_t textsize_y; ///< Desired magnification in Y-axis of text to print() + uint8_t rotation; ///< Display rotation (0 thru 3) + bool wrap; ///< If set, 'wrap' text at right edge of display + bool _cp437; ///< If set, use correct CP437 charset (default is off) + GFXfont *gfxFont; ///< Pointer to special font +}; + +/// A simple drawn button UI element +class Adafruit_GFX_Button { + +public: + Adafruit_GFX_Button(void); + // "Classic" initButton() uses center & size + void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w, + uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w, + uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize_x, + uint8_t textsize_y); + // New/alt initButton() uses upper-left corner & size + void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w, + uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w, + uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize_x, + uint8_t textsize_y); + void drawButton(bool inverted = false); + bool contains(int16_t x, int16_t y); + + /**********************************************************************/ + /*! + @brief Sets button state, should be done by some touch function + @param p True for pressed, false for not. + */ + /**********************************************************************/ + void press(bool p) { + laststate = currstate; + currstate = p; + } + + bool justPressed(); + bool justReleased(); + + /**********************************************************************/ + /*! + @brief Query whether the button is currently pressed + @returns True if pressed + */ + /**********************************************************************/ + bool isPressed(void) { return currstate; }; + +private: + Adafruit_GFX *_gfx; + int16_t _x1, _y1; // Coordinates of top-left corner + uint16_t _w, _h; + uint8_t _textsize_x; + uint8_t _textsize_y; + uint16_t _outlinecolor, _fillcolor, _textcolor; + char _label[10]; + + bool currstate, laststate; +}; + +/// A GFX 1-bit canvas context for graphics +class GFXcanvas1 : public Adafruit_GFX { +public: + GFXcanvas1(uint16_t w, uint16_t h); + ~GFXcanvas1(void); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void fillScreen(uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + bool getPixel(int16_t x, int16_t y) const; + /**********************************************************************/ + /*! + @brief Get a pointer to the internal buffer memory + @returns A pointer to the allocated buffer + */ + /**********************************************************************/ + uint8_t *getBuffer(void) const { return buffer; } + +protected: + bool getRawPixel(int16_t x, int16_t y) const; + void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + +private: + uint8_t *buffer; + +#ifdef __AVR__ + // Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR + static const uint8_t PROGMEM GFXsetBit[], GFXclrBit[]; +#endif +}; + +/// A GFX 8-bit canvas context for graphics +class GFXcanvas8 : public Adafruit_GFX { +public: + GFXcanvas8(uint16_t w, uint16_t h); + ~GFXcanvas8(void); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void fillScreen(uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + uint8_t getPixel(int16_t x, int16_t y) const; + /**********************************************************************/ + /*! + @brief Get a pointer to the internal buffer memory + @returns A pointer to the allocated buffer + */ + /**********************************************************************/ + uint8_t *getBuffer(void) const { return buffer; } + +protected: + uint8_t getRawPixel(int16_t x, int16_t y) const; + void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + +private: + uint8_t *buffer; +}; + +/// A GFX 16-bit canvas context for graphics +class GFXcanvas16 : public Adafruit_GFX { +public: + GFXcanvas16(uint16_t w, uint16_t h); + ~GFXcanvas16(void); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void fillScreen(uint16_t color); + void byteSwap(void); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + uint16_t getPixel(int16_t x, int16_t y) const; + /**********************************************************************/ + /*! + @brief Get a pointer to the internal buffer memory + @returns A pointer to the allocated buffer + */ + /**********************************************************************/ + uint16_t *getBuffer(void) const { return buffer; } + +protected: + uint16_t getRawPixel(int16_t x, int16_t y) const; + void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + +private: + uint16_t *buffer; +}; + +#endif // _ADAFRUIT_GFX_H diff --git a/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.cpp b/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.cpp new file mode 100644 index 0000000..f487861 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.cpp @@ -0,0 +1,421 @@ +/*! + * @file Adafruit_GrayOLED.cpp + * + * This is documentation for Adafruit's generic library for grayscale + * OLED displays: http://www.adafruit.com/category/63_98 + * + * These displays use I2C or SPI to communicate. I2C requires 2 pins + * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK, + * select, data/command) and optionally a reset pin. Hardware SPI or + * 'bitbang' software SPI are both supported. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + */ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all + +#include "Adafruit_GrayOLED.h" +#include + +// SOME DEFINES AND STATIC VARIABLES USED INTERNALLY ----------------------- + +#define grayoled_swap(a, b) \ + (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation + +// CONSTRUCTORS, DESTRUCTOR ------------------------------------------------ + +/*! + @brief Constructor for I2C-interfaced OLED displays. + @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray + @param w + Display width in pixels + @param h + Display height in pixels + @param twi + Pointer to an existing TwoWire instance (e.g. &Wire, the + microcontroller's primary I2C bus). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param clkDuring + Speed (in Hz) for Wire transmissions in library calls. + Defaults to 400000 (400 KHz), a known 'safe' value for most + microcontrollers, and meets the OLED datasheet spec. + Some systems can operate I2C faster (800 KHz for ESP32, 1 MHz + for many other 32-bit MCUs), and some (perhaps not all) + Many OLED's can work with this -- so it's optionally be specified + here and is not a default behavior. (Ignored if using pre-1.5.7 + Arduino software, which operates I2C at a fixed 100 KHz.) + @param clkAfter + Speed (in Hz) for Wire transmissions following library + calls. Defaults to 100000 (100 KHz), the default Arduino Wire + speed. This is done rather than leaving it at the 'during' speed + because other devices on the I2C bus might not be compatible + with the faster rate. (Ignored if using pre-1.5.7 Arduino + software, which operates I2C at a fixed 100 KHz.) + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, + TwoWire *twi, int8_t rst_pin, + uint32_t clkDuring, uint32_t clkAfter) + : Adafruit_GFX(w, h), i2c_preclk(clkDuring), i2c_postclk(clkAfter), + buffer(NULL), dcPin(-1), csPin(-1), rstPin(rst_pin), _bpp(bpp) { + i2c_dev = NULL; + _theWire = twi; +} + +/*! + @brief Constructor for SPI GrayOLED displays, using software (bitbang) + SPI. + @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray + @param w + Display width in pixels + @param h + Display height in pixels + @param mosi_pin + MOSI (master out, slave in) pin (using Arduino pin numbering). + This transfers serial data from microcontroller to display. + @param sclk_pin + SCLK (serial clock) pin (using Arduino pin numbering). + This clocks each bit from MOSI. + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, + int8_t mosi_pin, int8_t sclk_pin, + int8_t dc_pin, int8_t rst_pin, + int8_t cs_pin) + : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin), + _bpp(bpp) { + + spi_dev = new Adafruit_SPIDevice(cs_pin, sclk_pin, -1, mosi_pin, 1000000); +} + +/*! + @brief Constructor for SPI GrayOLED displays, using native hardware SPI. + @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray + @param w + Display width in pixels + @param h + Display height in pixels + @param spi + Pointer to an existing SPIClass instance (e.g. &SPI, the + microcontroller's primary SPI bus). + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @param bitrate + SPI clock rate for transfers to this display. Default if + unspecified is 8000000UL (8 MHz). + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, + SPIClass *spi, int8_t dc_pin, + int8_t rst_pin, int8_t cs_pin, + uint32_t bitrate) + : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin), + _bpp(bpp) { + + spi_dev = new Adafruit_SPIDevice(cs_pin, bitrate, SPI_BITORDER_MSBFIRST, + SPI_MODE0, spi); +} + +/*! + @brief Destructor for Adafruit_GrayOLED object. +*/ +Adafruit_GrayOLED::~Adafruit_GrayOLED(void) { + if (buffer) { + free(buffer); + buffer = NULL; + } + if (spi_dev) + delete spi_dev; + if (i2c_dev) + delete i2c_dev; +} + +// LOW-LEVEL UTILS --------------------------------------------------------- + +/*! + @brief Issue single command byte to OLED, using I2C or hard/soft SPI as + needed. + @param c The single byte command +*/ +void Adafruit_GrayOLED::oled_command(uint8_t c) { + if (i2c_dev) { // I2C + uint8_t buf[2] = {0x00, c}; // Co = 0, D/C = 0 + i2c_dev->write(buf, 2); + } else { // SPI (hw or soft) -- transaction started in calling function + digitalWrite(dcPin, LOW); + spi_dev->write(&c, 1); + } +} + +// Issue list of commands to GrayOLED +/*! + @brief Issue multiple bytes of commands OLED, using I2C or hard/soft SPI as + needed. + @param c Pointer to the command array + @param n The number of bytes in the command array + @returns True for success on ability to write the data in I2C. +*/ + +bool Adafruit_GrayOLED::oled_commandList(const uint8_t *c, uint8_t n) { + if (i2c_dev) { // I2C + uint8_t dc_byte = 0x00; // Co = 0, D/C = 0 + if (!i2c_dev->write((uint8_t *)c, n, true, &dc_byte, 1)) { + return false; + } + } else { // SPI -- transaction started in calling function + digitalWrite(dcPin, LOW); + if (!spi_dev->write((uint8_t *)c, n)) { + return false; + } + } + return true; +} + +// ALLOCATE & INIT DISPLAY ------------------------------------------------- + +/*! + @brief Allocate RAM for image buffer, initialize peripherals and pins. + Note that subclasses must call this before other begin() init + @param addr + I2C address of corresponding oled display. + SPI displays (hardware or software) do not use addresses, but + this argument is still required. Default if unspecified is 0x3C. + @param reset + If true, and if the reset pin passed to the constructor is + valid, a hard reset will be performed before initializing the + display. If using multiple oled displays on the same bus, and + if they all share the same reset pin, you should only pass true + on the first display being initialized, false on all others, + else the already-initialized displays would be reset. Default if + unspecified is true. + @return true on successful allocation/init, false otherwise. + Well-behaved code should check the return value before + proceeding. + @note MUST call this function before any drawing or updates! +*/ +bool Adafruit_GrayOLED::_init(uint8_t addr, bool reset) { + + // attempt to malloc the bitmap framebuffer + if ((!buffer) && + !(buffer = (uint8_t *)malloc(_bpp * WIDTH * ((HEIGHT + 7) / 8)))) { + return false; + } + + // Reset OLED if requested and reset pin specified in constructor + if (reset && (rstPin >= 0)) { + pinMode(rstPin, OUTPUT); + digitalWrite(rstPin, HIGH); + delay(10); // VDD goes high at start, pause + digitalWrite(rstPin, LOW); // Bring reset low + delay(10); // Wait 10 ms + digitalWrite(rstPin, HIGH); // Bring out of reset + delay(10); + } + + // Setup pin directions + if (_theWire) { // using I2C + i2c_dev = new Adafruit_I2CDevice(addr, _theWire); + // look for i2c address: + if (!i2c_dev || !i2c_dev->begin()) { + return false; + } + } else { // Using one of the SPI modes, either soft or hardware + if (!spi_dev || !spi_dev->begin()) { + return false; + } + pinMode(dcPin, OUTPUT); // Set data/command pin as output + } + + clearDisplay(); + + // set max dirty window + window_x1 = 0; + window_y1 = 0; + window_x2 = WIDTH - 1; + window_y2 = HEIGHT - 1; + + return true; // Success +} + +// DRAWING FUNCTIONS ------------------------------------------------------- + +/*! + @brief Set/clear/invert a single pixel. This is also invoked by the + Adafruit_GFX library in generating many higher-level graphics + primitives. + @param x + Column of display -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @param color + Pixel color, one of: MONOOLED_BLACK, MONOOLED_WHITE or + MONOOLED_INVERT. + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Adafruit_GrayOLED::drawPixel(int16_t x, int16_t y, uint16_t color) { + if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { + // Pixel is in-bounds. Rotate coordinates if needed. + switch (getRotation()) { + case 1: + grayoled_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + grayoled_swap(x, y); + y = HEIGHT - y - 1; + break; + } + + // adjust dirty window + window_x1 = min(window_x1, x); + window_y1 = min(window_y1, y); + window_x2 = max(window_x2, x); + window_y2 = max(window_y2, y); + + if (_bpp == 1) { + switch (color) { + case MONOOLED_WHITE: + buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7)); + break; + case MONOOLED_BLACK: + buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7)); + break; + case MONOOLED_INVERSE: + buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7)); + break; + } + } + if (_bpp == 4) { + uint8_t *pixelptr = &buffer[x / 2 + (y * WIDTH / 2)]; + // Serial.printf("(%d, %d) -> offset %d\n", x, y, x/2 + (y * WIDTH / 2)); + if (x % 2 == 0) { // even, left nibble + uint8_t t = pixelptr[0] & 0x0F; + t |= (color & 0xF) << 4; + pixelptr[0] = t; + } else { // odd, right lower nibble + uint8_t t = pixelptr[0] & 0xF0; + t |= color & 0xF; + pixelptr[0] = t; + } + } + } +} + +/*! + @brief Clear contents of display buffer (set all pixels to off). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Adafruit_GrayOLED::clearDisplay(void) { + memset(buffer, 0, _bpp * WIDTH * ((HEIGHT + 7) / 8)); + // set max dirty window + window_x1 = 0; + window_y1 = 0; + window_x2 = WIDTH - 1; + window_y2 = HEIGHT - 1; +} + +/*! + @brief Return color of a single pixel in display buffer. + @param x + Column of display -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @return true if pixel is set (usually MONOOLED_WHITE, unless display invert + mode is enabled), false if clear (MONOOLED_BLACK). + @note Reads from buffer contents; may not reflect current contents of + screen if display() has not been called. +*/ +bool Adafruit_GrayOLED::getPixel(int16_t x, int16_t y) { + if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { + // Pixel is in-bounds. Rotate coordinates if needed. + switch (getRotation()) { + case 1: + grayoled_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + grayoled_swap(x, y); + y = HEIGHT - y - 1; + break; + } + return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7))); + } + return false; // Pixel out of bounds +} + +/*! + @brief Get base address of display buffer for direct reading or writing. + @return Pointer to an unsigned 8-bit array, column-major, columns padded + to full byte boundary if needed. +*/ +uint8_t *Adafruit_GrayOLED::getBuffer(void) { return buffer; } + +// OTHER HARDWARE SETTINGS ------------------------------------------------- + +/*! + @brief Enable or disable display invert mode (white-on-black vs + black-on-white). Handy for testing! + @param i + If true, switch to invert mode (black-on-white), else normal + mode (white-on-black). + @note This has an immediate effect on the display, no need to call the + display() function -- buffer contents are not changed, rather a + different pixel mode of the display hardware is used. When + enabled, drawing MONOOLED_BLACK (value 0) pixels will actually draw + white, MONOOLED_WHITE (value 1) will draw black. +*/ +void Adafruit_GrayOLED::invertDisplay(bool i) { + oled_command(i ? GRAYOLED_INVERTDISPLAY : GRAYOLED_NORMALDISPLAY); +} + +/*! + @brief Adjust the display contrast. + @param level The contrast level from 0 to 0x7F + @note This has an immediate effect on the display, no need to call the + display() function -- buffer contents are not changed. +*/ +void Adafruit_GrayOLED::setContrast(uint8_t level) { + uint8_t cmd[] = {GRAYOLED_SETCONTRAST, level}; + oled_commandList(cmd, 2); +} + +#endif /* ATTIN85 not supported */ diff --git a/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.h b/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.h new file mode 100644 index 0000000..8ec7b23 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.h @@ -0,0 +1,100 @@ +/*! + * @file Adafruit_GrayOLED.h + * + * This is part of for Adafruit's GFX library, supplying generic support + * for grayscale OLED displays: http://www.adafruit.com/category/63_98 + * + * These displays use I2C or SPI to communicate. I2C requires 2 pins + * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK, + * select, data/command) and optionally a reset pin. Hardware SPI or + * 'bitbang' software SPI are both supported. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Limor Fried/Ladyada for Adafruit Industries, with + * contributions from the open source community. + * + * BSD license, all text above, and the splash screen header file, + * must be included in any redistribution. + * + */ + +#ifndef _Adafruit_GRAYOLED_H_ +#define _Adafruit_GRAYOLED_H_ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all + +#include +#include +#include +#include +#include + +#define GRAYOLED_SETCONTRAST 0x81 ///< Generic contrast for almost all OLEDs +#define GRAYOLED_NORMALDISPLAY 0xA6 ///< Generic non-invert for almost all OLEDs +#define GRAYOLED_INVERTDISPLAY 0xA7 ///< Generic invert for almost all OLEDs + +#define MONOOLED_BLACK 0 ///< Default black 'color' for monochrome OLEDS +#define MONOOLED_WHITE 1 ///< Default white 'color' for monochrome OLEDS +#define MONOOLED_INVERSE 2 ///< Default inversion command for monochrome OLEDS + +/*! + @brief Class that stores state and functions for interacting with + generic grayscale OLED displays. +*/ +class Adafruit_GrayOLED : public Adafruit_GFX { +public: + Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, TwoWire *twi = &Wire, + int8_t rst_pin = -1, uint32_t preclk = 400000, + uint32_t postclk = 100000); + Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, int8_t mosi_pin, + int8_t sclk_pin, int8_t dc_pin, int8_t rst_pin, + int8_t cs_pin); + Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, SPIClass *spi, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, + uint32_t bitrate = 8000000UL); + + ~Adafruit_GrayOLED(void); + + /** + @brief The function that sub-classes define that writes out the buffer to + the display over I2C or SPI + **/ + virtual void display(void) = 0; + void clearDisplay(void); + void invertDisplay(bool i); + void setContrast(uint8_t contrastlevel); + void drawPixel(int16_t x, int16_t y, uint16_t color); + bool getPixel(int16_t x, int16_t y); + uint8_t *getBuffer(void); + + void oled_command(uint8_t c); + bool oled_commandList(const uint8_t *c, uint8_t n); + +protected: + bool _init(uint8_t i2caddr = 0x3C, bool reset = true); + + Adafruit_SPIDevice *spi_dev = NULL; ///< The SPI interface BusIO device + Adafruit_I2CDevice *i2c_dev = NULL; ///< The I2C interface BusIO device + int32_t i2c_preclk = 400000, ///< Configurable 'high speed' I2C rate + i2c_postclk = 100000; ///< Configurable 'low speed' I2C rate + uint8_t *buffer = NULL; ///< Internal 1:1 framebuffer of display mem + + int16_t window_x1, ///< Dirty tracking window minimum x + window_y1, ///< Dirty tracking window minimum y + window_x2, ///< Dirty tracking window maximum x + window_y2; ///< Dirty tracking window maximum y + + int dcPin, ///< The Arduino pin connected to D/C (for SPI) + csPin, ///< The Arduino pin connected to CS (for SPI) + rstPin; ///< The Arduino pin connected to reset (-1 if unused) + + uint8_t _bpp = 1; ///< Bits per pixel color for this display +private: + TwoWire *_theWire = NULL; ///< The underlying hardware I2C +}; + +#endif // end __AVR_ATtiny85__ +#endif // _Adafruit_GrayOLED_H_ diff --git a/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp b/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp new file mode 100644 index 0000000..2c65610 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp @@ -0,0 +1,2515 @@ +/*! + * @file Adafruit_SPITFT.cpp + * + * @mainpage Adafruit SPI TFT Displays (and some others) + * + * @section intro_sec Introduction + * + * Part of Adafruit's GFX graphics library. Originally this class was + * written to handle a range of color TFT displays connected via SPI, + * but over time this library and some display-specific subclasses have + * mutated to include some color OLEDs as well as parallel-interfaced + * displays. The name's been kept for the sake of older code. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + + * @section dependencies Dependencies + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * @section author Author + * + * Written by Limor "ladyada" Fried for Adafruit Industries, + * with contributions from the open source community. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + */ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all + +#include "Adafruit_SPITFT.h" + +#if defined(__AVR__) +#if defined(__AVR_XMEGA__) // only tested with __AVR_ATmega4809__ +#define AVR_WRITESPI(x) \ + for (SPI0_DATA = (x); (!(SPI0_INTFLAGS & _BV(SPI_IF_bp)));) +#else +#define AVR_WRITESPI(x) for (SPDR = (x); (!(SPSR & _BV(SPIF)));) +#endif +#endif + +#if defined(PORT_IOBUS) +// On SAMD21, redefine digitalPinToPort() to use the slightly-faster +// PORT_IOBUS rather than PORT (not needed on SAMD51). +#undef digitalPinToPort +#define digitalPinToPort(P) (&(PORT_IOBUS->Group[g_APinDescription[P].ulPort])) +#endif // end PORT_IOBUS + +#if defined(USE_SPI_DMA) && (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) +// #pragma message ("GFX DMA IS ENABLED. HIGHLY EXPERIMENTAL.") +#include "wiring_private.h" // pinPeripheral() function +#include +#include // memalign() function +#define tcNum 2 // Timer/Counter for parallel write strobe PWM +#define wrPeripheral PIO_CCL // Use CCL to invert write strobe + +// DMA transfer-in-progress indicator and callback +static volatile bool dma_busy = false; +static void dma_callback(Adafruit_ZeroDMA *dma) { dma_busy = false; } + +#if defined(__SAMD51__) +// Timer/counter info by index # +static const struct { + Tc *tc; // -> Timer/Counter base address + int gclk; // GCLK ID + int evu; // EVSYS user ID +} tcList[] = {{TC0, TC0_GCLK_ID, EVSYS_ID_USER_TC0_EVU}, + {TC1, TC1_GCLK_ID, EVSYS_ID_USER_TC1_EVU}, + {TC2, TC2_GCLK_ID, EVSYS_ID_USER_TC2_EVU}, + {TC3, TC3_GCLK_ID, EVSYS_ID_USER_TC3_EVU}, +#if defined(TC4) + {TC4, TC4_GCLK_ID, EVSYS_ID_USER_TC4_EVU}, +#endif +#if defined(TC5) + {TC5, TC5_GCLK_ID, EVSYS_ID_USER_TC5_EVU}, +#endif +#if defined(TC6) + {TC6, TC6_GCLK_ID, EVSYS_ID_USER_TC6_EVU}, +#endif +#if defined(TC7) + {TC7, TC7_GCLK_ID, EVSYS_ID_USER_TC7_EVU} +#endif +}; +#define NUM_TIMERS (sizeof tcList / sizeof tcList[0]) ///< # timer/counters +#endif // end __SAMD51__ + +#endif // end USE_SPI_DMA + +// Possible values for Adafruit_SPITFT.connection: +#define TFT_HARD_SPI 0 ///< Display interface = hardware SPI +#define TFT_SOFT_SPI 1 ///< Display interface = software SPI +#define TFT_PARALLEL 2 ///< Display interface = 8- or 16-bit parallel + +// CONSTRUCTORS ------------------------------------------------------------ + +/*! + @brief Adafruit_SPITFT constructor for software (bitbang) SPI. + @param w Display width in pixels at default rotation setting (0). + @param h Display height in pixels at default rotation setting (0). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param mosi Arduino pin # for bitbang SPI MOSI signal (required). + @param sck Arduino pin # for bitbang SPI SCK signal (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @param miso Arduino pin # for bitbang SPI MISO signal (optional, + -1 default, many displays don't support SPI read). + @note Output pins are not initialized; application typically will + need to call subclass' begin() function, which in turn calls + this library's initSPI() function to initialize pins. +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, int8_t dc, + int8_t mosi, int8_t sck, int8_t rst, + int8_t miso) + : Adafruit_GFX(w, h), connection(TFT_SOFT_SPI), _rst(rst), _cs(cs), + _dc(dc) { + swspi._sck = sck; + swspi._mosi = mosi; + swspi._miso = miso; +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(CORE_TEENSY) +#if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(dc); + swspi.sckPinMask = digitalPinToBitMask(sck); + swspi.mosiPinMask = digitalPinToBitMask(mosi); +#endif + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + swspi.sckPortSet = portSetRegister(sck); + swspi.sckPortClr = portClearRegister(sck); + swspi.mosiPortSet = portSetRegister(mosi); + swspi.mosiPortClr = portClearRegister(mosi); + if (cs >= 0) { +#if !defined(KINETISK) + csPinMask = digitalPinToBitMask(cs); +#endif + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { +#if !defined(KINETISK) + csPinMask = 0; +#endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } + if (miso >= 0) { + swspi.misoPort = portInputRegister(miso); +#if !defined(KINETISK) + swspi.misoPinMask = digitalPinToBitMask(miso); +#endif + } else { + swspi.misoPort = portInputRegister(dc); + } +#else // !CORE_TEENSY + dcPinMask = digitalPinToBitMask(dc); + swspi.sckPinMask = digitalPinToBitMask(sck); + swspi.mosiPinMask = digitalPinToBitMask(mosi); + dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + swspi.sckPortSet = &(PORT->Group[g_APinDescription[sck].ulPort].OUTSET.reg); + swspi.sckPortClr = &(PORT->Group[g_APinDescription[sck].ulPort].OUTCLR.reg); + swspi.mosiPortSet = &(PORT->Group[g_APinDescription[mosi].ulPort].OUTSET.reg); + swspi.mosiPortClr = &(PORT->Group[g_APinDescription[mosi].ulPort].OUTCLR.reg); + if (cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + if (miso >= 0) { + swspi.misoPinMask = digitalPinToBitMask(miso); + swspi.misoPort = (PORTreg_t)portInputRegister(digitalPinToPort(miso)); + } else { + swspi.misoPinMask = 0; + swspi.misoPort = (PORTreg_t)portInputRegister(digitalPinToPort(dc)); + } +#endif // end !CORE_TEENSY +#else // !HAS_PORT_SET_CLR + dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet = digitalPinToBitMask(dc); + swspi.sckPort = (PORTreg_t)portOutputRegister(digitalPinToPort(sck)); + swspi.sckPinMaskSet = digitalPinToBitMask(sck); + swspi.mosiPort = (PORTreg_t)portOutputRegister(digitalPinToPort(mosi)); + swspi.mosiPinMaskSet = digitalPinToBitMask(mosi); + if (cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + if (miso >= 0) { + swspi.misoPort = (PORTreg_t)portInputRegister(digitalPinToPort(miso)); + swspi.misoPinMask = digitalPinToBitMask(miso); + } else { + swspi.misoPort = (PORTreg_t)portInputRegister(digitalPinToPort(dc)); + swspi.misoPinMask = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + swspi.sckPinMaskClr = ~swspi.sckPinMaskSet; + swspi.mosiPinMaskClr = ~swspi.mosiPinMaskSet; +#endif // !end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} + +/*! + @brief Adafruit_SPITFT constructor for hardware SPI using the board's + default SPI peripheral. + @param w Display width in pixels at default rotation setting (0). + @param h Display height in pixels at default rotation setting (0). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @note Output pins are not initialized; application typically will + need to call subclass' begin() function, which in turn calls + this library's initSPI() function to initialize pins. +*/ +#if defined(ESP8266) // See notes below +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, int8_t dc, + int8_t rst) + : Adafruit_GFX(w, h), connection(TFT_HARD_SPI), _rst(rst), _cs(cs), + _dc(dc) { + hwspi._spi = &SPI; +} +#else // !ESP8266 +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, int8_t dc, + int8_t rst) + : Adafruit_SPITFT(w, h, &SPI, cs, dc, rst) { + // This just invokes the hardware SPI constructor below, + // passing the default SPI device (&SPI). +} +#endif // end !ESP8266 + +#if !defined(ESP8266) +// ESP8266 compiler freaks out at this constructor -- it can't disambiguate +// beteween the SPIClass pointer (argument #3) and a regular integer. +// Solution here it to just not offer this variant on the ESP8266. You can +// use the default hardware SPI peripheral, or you can use software SPI, +// but if there's any library out there that creates a 'virtual' SPIClass +// peripheral and drives it with software bitbanging, that's not supported. +/*! + @brief Adafruit_SPITFT constructor for hardware SPI using a specific + SPI peripheral. + @param w Display width in pixels at default rotation (0). + @param h Display height in pixels at default rotation (0). + @param spiClass Pointer to SPIClass type (e.g. &SPI or &SPI1). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @note Output pins are not initialized in constructor; application + typically will need to call subclass' begin() function, which + in turn calls this library's initSPI() function to initialize + pins. EXCEPT...if you have built your own SERCOM SPI peripheral + (calling the SPIClass constructor) rather than one of the + built-in SPI devices (e.g. &SPI, &SPI1 and so forth), you will + need to call the begin() function for your object as well as + pinPeripheral() for the MOSI, MISO and SCK pins to configure + GPIO manually. Do this BEFORE calling the display-specific + begin or init function. Unfortunate but unavoidable. +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, + int8_t cs, int8_t dc, int8_t rst) + : Adafruit_GFX(w, h), connection(TFT_HARD_SPI), _rst(rst), _cs(cs), + _dc(dc) { + hwspi._spi = spiClass; +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(CORE_TEENSY) +#if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(dc); +#endif + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + if (cs >= 0) { +#if !defined(KINETISK) + csPinMask = digitalPinToBitMask(cs); +#endif + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { // see comments below +#if !defined(KINETISK) + csPinMask = 0; +#endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } +#else // !CORE_TEENSY + dcPinMask = digitalPinToBitMask(dc); + dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + if (cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } +#endif // end !CORE_TEENSY +#else // !HAS_PORT_SET_CLR + dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet = digitalPinToBitMask(dc); + if (cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} +#endif // end !ESP8266 + +/*! + @brief Adafruit_SPITFT constructor for parallel display connection. + @param w Display width in pixels at default rotation (0). + @param h Display height in pixels at default rotation (0). + @param busWidth If tft16 (enumeration in header file), is a 16-bit + parallel connection, else 8-bit. + 16-bit isn't fully implemented or tested yet so + applications should pass "tft8bitbus" for now...needed to + stick a required enum argument in there to + disambiguate this constructor from the soft-SPI case. + Argument is ignored on 8-bit architectures (no 'wide' + support there since PORTs are 8 bits anyway). + @param d0 Arduino pin # for data bit 0 (1+ are extrapolated). + The 8 (or 16) data bits MUST be contiguous and byte- + aligned (or word-aligned for wide interface) within + the same PORT register (might not correspond to + Arduino pin sequence). + @param wr Arduino pin # for write strobe (required). + @param dc Arduino pin # for data/command select (required). + @param cs Arduino pin # for chip-select (optional, -1 if unused, + tie CS low). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @param rd Arduino pin # for read strobe (optional, -1 if unused). + @note Output pins are not initialized; application typically will need + to call subclass' begin() function, which in turn calls this + library's initSPI() function to initialize pins. + Yes, the name is a misnomer...this library originally handled + only SPI displays, parallel being a recent addition (but not + wanting to break existing code). +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth, + int8_t d0, int8_t wr, int8_t dc, int8_t cs, + int8_t rst, int8_t rd) + : Adafruit_GFX(w, h), connection(TFT_PARALLEL), _rst(rst), _cs(cs), + _dc(dc) { + tft8._d0 = d0; + tft8._wr = wr; + tft8._rd = rd; + tft8.wide = (busWidth == tft16bitbus); +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(CORE_TEENSY) + tft8.wrPortSet = portSetRegister(wr); + tft8.wrPortClr = portClearRegister(wr); +#if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(dc); +#endif + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + if (cs >= 0) { +#if !defined(KINETISK) + csPinMask = digitalPinToBitMask(cs); +#endif + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { // see comments below +#if !defined(KINETISK) + csPinMask = 0; +#endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } + if (rd >= 0) { // if read-strobe pin specified... +#if defined(KINETISK) + tft8.rdPinMask = 1; +#else // !KINETISK + tft8.rdPinMask = digitalPinToBitMask(rd); +#endif + tft8.rdPortSet = portSetRegister(rd); + tft8.rdPortClr = portClearRegister(rd); + } else { + tft8.rdPinMask = 0; + tft8.rdPortSet = dcPortSet; + tft8.rdPortClr = dcPortClr; + } + // These are all uint8_t* pointers -- elsewhere they're recast + // as necessary if a 'wide' 16-bit interface is in use. + tft8.writePort = portOutputRegister(d0); + tft8.readPort = portInputRegister(d0); + tft8.dirSet = portModeRegister(d0); + tft8.dirClr = portModeRegister(d0); +#else // !CORE_TEENSY + tft8.wrPinMask = digitalPinToBitMask(wr); + tft8.wrPortSet = &(PORT->Group[g_APinDescription[wr].ulPort].OUTSET.reg); + tft8.wrPortClr = &(PORT->Group[g_APinDescription[wr].ulPort].OUTCLR.reg); + dcPinMask = digitalPinToBitMask(dc); + dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + if (cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + if (rd >= 0) { // if read-strobe pin specified... + tft8.rdPinMask = digitalPinToBitMask(rd); + tft8.rdPortSet = &(PORT->Group[g_APinDescription[rd].ulPort].OUTSET.reg); + tft8.rdPortClr = &(PORT->Group[g_APinDescription[rd].ulPort].OUTCLR.reg); + } else { + tft8.rdPinMask = 0; + tft8.rdPortSet = dcPortSet; + tft8.rdPortClr = dcPortClr; + } + // Get pointers to PORT write/read/dir bytes within 32-bit PORT + uint8_t dBit = g_APinDescription[d0].ulPin; // d0 bit # in PORT + PortGroup *p = (&(PORT->Group[g_APinDescription[d0].ulPort])); + uint8_t offset = dBit / 8; // d[7:0] byte # within PORT + if (tft8.wide) + offset &= ~1; // d[15:8] byte # within PORT + // These are all uint8_t* pointers -- elsewhere they're recast + // as necessary if a 'wide' 16-bit interface is in use. + tft8.writePort = (volatile uint8_t *)&(p->OUT.reg) + offset; + tft8.readPort = (volatile uint8_t *)&(p->IN.reg) + offset; + tft8.dirSet = (volatile uint8_t *)&(p->DIRSET.reg) + offset; + tft8.dirClr = (volatile uint8_t *)&(p->DIRCLR.reg) + offset; +#endif // end !CORE_TEENSY +#else // !HAS_PORT_SET_CLR + tft8.wrPort = (PORTreg_t)portOutputRegister(digitalPinToPort(wr)); + tft8.wrPinMaskSet = digitalPinToBitMask(wr); + dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet = digitalPinToBitMask(dc); + if (cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + if (rd >= 0) { // if read-strobe pin specified... + tft8.rdPort = (PORTreg_t)portOutputRegister(digitalPinToPort(rd)); + tft8.rdPinMaskSet = digitalPinToBitMask(rd); + } else { + tft8.rdPort = dcPort; + tft8.rdPinMaskSet = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + tft8.wrPinMaskClr = ~tft8.wrPinMaskSet; + tft8.rdPinMaskClr = ~tft8.rdPinMaskSet; + tft8.writePort = (PORTreg_t)portOutputRegister(digitalPinToPort(d0)); + tft8.readPort = (PORTreg_t)portInputRegister(digitalPinToPort(d0)); + tft8.portDir = (PORTreg_t)portModeRegister(digitalPinToPort(d0)); +#endif // end !HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} + +// end constructors ------- + +// CLASS MEMBER FUNCTIONS -------------------------------------------------- + +// begin() and setAddrWindow() MUST be declared by any subclass. + +/*! + @brief Configure microcontroller pins for TFT interfacing. Typically + called by a subclass' begin() function. + @param freq SPI frequency when using hardware SPI. If default (0) + is passed, will fall back on a device-specific value. + Value is ignored when using software SPI or parallel + connection. + @param spiMode SPI mode when using hardware SPI. MUST be one of the + values SPI_MODE0, SPI_MODE1, SPI_MODE2 or SPI_MODE3 + defined in SPI.h. Do NOT attempt to pass '0' for + SPI_MODE0 and so forth...the values are NOT the same! + Use ONLY the defines! (Pity it's not an enum.) + @note Another anachronistically-named function; this is called even + when the display connection is parallel (not SPI). Also, this + could probably be made private...quite a few class functions + were generously put in the public section. +*/ +void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) { + + if (!freq) + freq = DEFAULT_SPI_FREQ; // If no freq specified, use default + + // Init basic control pins common to all connection types + if (_cs >= 0) { + pinMode(_cs, OUTPUT); + digitalWrite(_cs, HIGH); // Deselect + } + pinMode(_dc, OUTPUT); + digitalWrite(_dc, HIGH); // Data mode + + if (connection == TFT_HARD_SPI) { + +#if defined(SPI_HAS_TRANSACTION) + hwspi.settings = SPISettings(freq, MSBFIRST, spiMode); +#else + hwspi._freq = freq; // Save freq value for later +#endif + hwspi._mode = spiMode; // Save spiMode value for later + // Call hwspi._spi->begin() ONLY if this is among the 'established' + // SPI interfaces in variant.h. For DIY roll-your-own SERCOM SPIs, + // begin() and pinPeripheral() calls MUST be made in one's calling + // code, BEFORE the screen-specific begin/init function is called. + // Reason for this is that SPI::begin() makes its own calls to + // pinPeripheral() based on g_APinDescription[n].ulPinType, which + // on non-established SPI interface pins will always be PIO_DIGITAL + // or similar, while we need PIO_SERCOM or PIO_SERCOM_ALT...it's + // highly unique between devices and variants for each pin or + // SERCOM so we can't make those calls ourselves here. And the SPI + // device needs to be set up before calling this because it's + // immediately followed with initialization commands. Blargh. + if ( +#if !defined(SPI_INTERFACES_COUNT) + 1 +#else +#if SPI_INTERFACES_COUNT > 0 + (hwspi._spi == &SPI) +#endif +#if SPI_INTERFACES_COUNT > 1 + || (hwspi._spi == &SPI1) +#endif +#if SPI_INTERFACES_COUNT > 2 + || (hwspi._spi == &SPI2) +#endif +#if SPI_INTERFACES_COUNT > 3 + || (hwspi._spi == &SPI3) +#endif +#if SPI_INTERFACES_COUNT > 4 + || (hwspi._spi == &SPI4) +#endif +#if SPI_INTERFACES_COUNT > 5 + || (hwspi._spi == &SPI5) +#endif +#endif // end SPI_INTERFACES_COUNT + ) { + hwspi._spi->begin(); + } + } else if (connection == TFT_SOFT_SPI) { + + pinMode(swspi._mosi, OUTPUT); + digitalWrite(swspi._mosi, LOW); + pinMode(swspi._sck, OUTPUT); + digitalWrite(swspi._sck, LOW); + if (swspi._miso >= 0) { + pinMode(swspi._miso, INPUT); + } + + } else { // TFT_PARALLEL + // Initialize data pins. We were only passed d0, so scan + // the pin description list looking for the other pins. + // They'll be on the same PORT, and within the next 7 (or 15) bits + // (because we need to write to a contiguous PORT byte or word). +#if defined(__AVR__) + // PORT registers are 8 bits wide, so just need a register match... + for (uint8_t i = 0; i < NUM_DIGITAL_PINS; i++) { + if ((PORTreg_t)portOutputRegister(digitalPinToPort(i)) == + tft8.writePort) { + pinMode(i, OUTPUT); + digitalWrite(i, LOW); + } + } +#elif defined(USE_FAST_PINIO) +#if defined(CORE_TEENSY) + if (!tft8.wide) { + *tft8.dirSet = 0xFF; // Set port to output + *tft8.writePort = 0x00; // Write all 0s + } else { + *(volatile uint16_t *)tft8.dirSet = 0xFFFF; + *(volatile uint16_t *)tft8.writePort = 0x0000; + } +#else // !CORE_TEENSY + uint8_t portNum = g_APinDescription[tft8._d0].ulPort, // d0 PORT # + dBit = g_APinDescription[tft8._d0].ulPin, // d0 bit in PORT + lastBit = dBit + (tft8.wide ? 15 : 7); + for (uint8_t i = 0; i < PINS_COUNT; i++) { + if ((g_APinDescription[i].ulPort == portNum) && + (g_APinDescription[i].ulPin >= dBit) && + (g_APinDescription[i].ulPin <= (uint32_t)lastBit)) { + pinMode(i, OUTPUT); + digitalWrite(i, LOW); + } + } +#endif // end !CORE_TEENSY +#endif + pinMode(tft8._wr, OUTPUT); + digitalWrite(tft8._wr, HIGH); + if (tft8._rd >= 0) { + pinMode(tft8._rd, OUTPUT); + digitalWrite(tft8._rd, HIGH); + } + } + + if (_rst >= 0) { + // Toggle _rst low to reset + pinMode(_rst, OUTPUT); + digitalWrite(_rst, HIGH); + delay(100); + digitalWrite(_rst, LOW); + delay(100); + digitalWrite(_rst, HIGH); + delay(200); + } + +#if defined(USE_SPI_DMA) && (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) + if (((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) && + (dma.allocate() == DMA_STATUS_OK)) { // Allocate channel + // The DMA library needs to alloc at least one valid descriptor, + // so we do that here. It's not used in the usual sense though, + // just before a transfer we copy descriptor[0] to this address. + if (dptr = dma.addDescriptor(NULL, NULL, 42, DMA_BEAT_SIZE_BYTE, false, + false)) { + // Alloc 2 scanlines worth of pixels on display's major axis, + // whichever that is, rounding each up to 2-pixel boundary. + int major = (WIDTH > HEIGHT) ? WIDTH : HEIGHT; + major += (major & 1); // -> next 2-pixel bound, if needed. + maxFillLen = major * 2; // 2 scanlines + // Note to future self: if you decide to make the pixel buffer + // much larger, remember that DMA transfer descriptors can't + // exceed 65,535 bytes (not 65,536), meaning 32,767 pixels max. + // Not that we have that kind of RAM to throw around right now. + if ((pixelBuf[0] = (uint16_t *)malloc(maxFillLen * sizeof(uint16_t)))) { + // Alloc OK. Get pointer to start of second scanline. + pixelBuf[1] = &pixelBuf[0][major]; + // Determine number of DMA descriptors needed to cover + // entire screen when entire 2-line pixelBuf is used + // (round up for fractional last descriptor). + int numDescriptors = (WIDTH * HEIGHT + (maxFillLen - 1)) / maxFillLen; + // DMA descriptors MUST be 128-bit (16 byte) aligned. + // memalign() is considered obsolete but it's replacements + // (aligned_alloc() or posix_memalign()) are not currently + // available in the version of ARM GCC in use, but this + // is, so here we are. + if ((descriptor = (DmacDescriptor *)memalign( + 16, numDescriptors * sizeof(DmacDescriptor)))) { + int dmac_id; + volatile uint32_t *data_reg; + + if (connection == TFT_HARD_SPI) { + // THIS IS AN AFFRONT TO NATURE, but I don't know + // any "clean" way to get the sercom number from the + // the SPIClass pointer (e.g. &SPI or &SPI1), which + // is all we have to work with. SPIClass does contain + // a SERCOM pointer but it is a PRIVATE member! + // Doing an UNSPEAKABLY HORRIBLE THING here, directly + // accessing the first 32-bit value in the SPIClass + // structure, knowing that's (currently) where the + // SERCOM pointer lives, but this ENTIRELY DEPENDS on + // that structure not changing nor the compiler + // rearranging things. Oh the humanity! + + if (*(SERCOM **)hwspi._spi == &sercom0) { + dmac_id = SERCOM0_DMAC_ID_TX; + data_reg = &SERCOM0->SPI.DATA.reg; +#if defined SERCOM1 + } else if (*(SERCOM **)hwspi._spi == &sercom1) { + dmac_id = SERCOM1_DMAC_ID_TX; + data_reg = &SERCOM1->SPI.DATA.reg; +#endif +#if defined SERCOM2 + } else if (*(SERCOM **)hwspi._spi == &sercom2) { + dmac_id = SERCOM2_DMAC_ID_TX; + data_reg = &SERCOM2->SPI.DATA.reg; +#endif +#if defined SERCOM3 + } else if (*(SERCOM **)hwspi._spi == &sercom3) { + dmac_id = SERCOM3_DMAC_ID_TX; + data_reg = &SERCOM3->SPI.DATA.reg; +#endif +#if defined SERCOM4 + } else if (*(SERCOM **)hwspi._spi == &sercom4) { + dmac_id = SERCOM4_DMAC_ID_TX; + data_reg = &SERCOM4->SPI.DATA.reg; +#endif +#if defined SERCOM5 + } else if (*(SERCOM **)hwspi._spi == &sercom5) { + dmac_id = SERCOM5_DMAC_ID_TX; + data_reg = &SERCOM5->SPI.DATA.reg; +#endif +#if defined SERCOM6 + } else if (*(SERCOM **)hwspi._spi == &sercom6) { + dmac_id = SERCOM6_DMAC_ID_TX; + data_reg = &SERCOM6->SPI.DATA.reg; +#endif +#if defined SERCOM7 + } else if (*(SERCOM **)hwspi._spi == &sercom7) { + dmac_id = SERCOM7_DMAC_ID_TX; + data_reg = &SERCOM7->SPI.DATA.reg; +#endif + } + dma.setPriority(DMA_PRIORITY_3); + dma.setTrigger(dmac_id); + dma.setAction(DMA_TRIGGER_ACTON_BEAT); + + // Initialize descriptor list. + for (int d = 0; d < numDescriptors; d++) { + // No need to set SRCADDR, DESCADDR or BTCNT -- + // those are done in the pixel-writing functions. + descriptor[d].BTCTRL.bit.VALID = true; + descriptor[d].BTCTRL.bit.EVOSEL = DMA_EVENT_OUTPUT_DISABLE; + descriptor[d].BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_NOACT; + descriptor[d].BTCTRL.bit.BEATSIZE = DMA_BEAT_SIZE_BYTE; + descriptor[d].BTCTRL.bit.DSTINC = 0; + descriptor[d].BTCTRL.bit.STEPSEL = DMA_STEPSEL_SRC; + descriptor[d].BTCTRL.bit.STEPSIZE = + DMA_ADDRESS_INCREMENT_STEP_SIZE_1; + descriptor[d].DSTADDR.reg = (uint32_t)data_reg; + } + + } else { // Parallel connection + +#if defined(__SAMD51__) + int dmaChannel = dma.getChannel(); + // Enable event output, use EVOSEL output + DMAC->Channel[dmaChannel].CHEVCTRL.bit.EVOE = 1; + DMAC->Channel[dmaChannel].CHEVCTRL.bit.EVOMODE = 0; + + // CONFIGURE TIMER/COUNTER (for write strobe) + + Tc *timer = tcList[tcNum].tc; // -> Timer struct + int id = tcList[tcNum].gclk; // Timer GCLK ID + GCLK_PCHCTRL_Type pchctrl; + + // Set up timer clock source from GCLK + GCLK->PCHCTRL[id].bit.CHEN = 0; // Stop timer + while (GCLK->PCHCTRL[id].bit.CHEN) + ; // Wait for it + pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val; + pchctrl.bit.CHEN = 1; // Enable + GCLK->PCHCTRL[id].reg = pchctrl.reg; + while (!GCLK->PCHCTRL[id].bit.CHEN) + ; // Wait for it + + // Disable timer/counter before configuring it + timer->COUNT8.CTRLA.bit.ENABLE = 0; + while (timer->COUNT8.SYNCBUSY.bit.STATUS) + ; + + timer->COUNT8.WAVE.bit.WAVEGEN = 2; // NPWM + timer->COUNT8.CTRLA.bit.MODE = 1; // 8-bit + timer->COUNT8.CTRLA.bit.PRESCALER = 0; // 1:1 + while (timer->COUNT8.SYNCBUSY.bit.STATUS) + ; + + timer->COUNT8.CTRLBCLR.bit.DIR = 1; // Count UP + while (timer->COUNT8.SYNCBUSY.bit.CTRLB) + ; + timer->COUNT8.CTRLBSET.bit.ONESHOT = 1; // One-shot + while (timer->COUNT8.SYNCBUSY.bit.CTRLB) + ; + timer->COUNT8.PER.reg = 6; // PWM top + while (timer->COUNT8.SYNCBUSY.bit.PER) + ; + timer->COUNT8.CC[0].reg = 2; // Compare + while (timer->COUNT8.SYNCBUSY.bit.CC0) + ; + // Enable async input events, + // event action = restart. + timer->COUNT8.EVCTRL.bit.TCEI = 1; + timer->COUNT8.EVCTRL.bit.EVACT = 1; + + // Enable timer + timer->COUNT8.CTRLA.reg |= TC_CTRLA_ENABLE; + while (timer->COUNT8.SYNCBUSY.bit.STATUS) + ; + +#if (wrPeripheral == PIO_CCL) + // CONFIGURE CCL (inverts timer/counter output) + + MCLK->APBCMASK.bit.CCL_ = 1; // Enable CCL clock + CCL->CTRL.bit.ENABLE = 0; // Disable to config + CCL->CTRL.bit.SWRST = 1; // Reset CCL registers + CCL->LUTCTRL[tcNum].bit.ENABLE = 0; // Disable LUT + CCL->LUTCTRL[tcNum].bit.FILTSEL = 0; // No filter + CCL->LUTCTRL[tcNum].bit.INSEL0 = 6; // TC input + CCL->LUTCTRL[tcNum].bit.INSEL1 = 0; // MASK + CCL->LUTCTRL[tcNum].bit.INSEL2 = 0; // MASK + CCL->LUTCTRL[tcNum].bit.TRUTH = 1; // Invert in 0 + CCL->LUTCTRL[tcNum].bit.ENABLE = 1; // Enable LUT + CCL->CTRL.bit.ENABLE = 1; // Enable CCL +#endif + + // CONFIGURE EVENT SYSTEM + + // Set up event system clock source from GCLK... + // Disable EVSYS, wait for disable + GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN = 0; + while (GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN) + ; + pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val; + pchctrl.bit.CHEN = 1; // Re-enable + GCLK->PCHCTRL[EVSYS_GCLK_ID_0].reg = pchctrl.reg; + // Wait for it, then enable EVSYS clock + while (!GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN) + ; + MCLK->APBBMASK.bit.EVSYS_ = 1; + + // Connect Timer EVU to ch 0 + EVSYS->USER[tcList[tcNum].evu].reg = 1; + // Datasheet recommends single write operation; + // reg instead of bit. Also datasheet: PATH bits + // must be zero when using async! + EVSYS_CHANNEL_Type ev; + ev.reg = 0; + ev.bit.PATH = 2; // Asynchronous + ev.bit.EVGEN = 0x22 + dmaChannel; // DMA channel 0+ + EVSYS->Channel[0].CHANNEL.reg = ev.reg; + + // Initialize descriptor list. + for (int d = 0; d < numDescriptors; d++) { + // No need to set SRCADDR, DESCADDR or BTCNT -- + // those are done in the pixel-writing functions. + descriptor[d].BTCTRL.bit.VALID = true; + // Event strobe on beat xfer: + descriptor[d].BTCTRL.bit.EVOSEL = 0x3; + descriptor[d].BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_NOACT; + descriptor[d].BTCTRL.bit.BEATSIZE = + tft8.wide ? DMA_BEAT_SIZE_HWORD : DMA_BEAT_SIZE_BYTE; + descriptor[d].BTCTRL.bit.SRCINC = 1; + descriptor[d].BTCTRL.bit.DSTINC = 0; + descriptor[d].BTCTRL.bit.STEPSEL = DMA_STEPSEL_SRC; + descriptor[d].BTCTRL.bit.STEPSIZE = + DMA_ADDRESS_INCREMENT_STEP_SIZE_1; + descriptor[d].DSTADDR.reg = (uint32_t)tft8.writePort; + } +#endif // __SAMD51 + } // end parallel-specific DMA setup + + lastFillColor = 0x0000; + lastFillLen = 0; + dma.setCallback(dma_callback); + return; // Success! + // else clean up any partial allocation... + } // end descriptor memalign() + free(pixelBuf[0]); + pixelBuf[0] = pixelBuf[1] = NULL; + } // end pixelBuf malloc() + // Don't currently have a descriptor delete function in + // ZeroDMA lib, but if we did, it would be called here. + } // end addDescriptor() + dma.free(); // Deallocate DMA channel + } +#endif // end USE_SPI_DMA +} + +/*! + @brief Allow changing the SPI clock speed after initialization + @param freq Desired frequency of SPI clock, may not be the + end frequency you get based on what the chip can do! +*/ +void Adafruit_SPITFT::setSPISpeed(uint32_t freq) { +#if defined(SPI_HAS_TRANSACTION) + hwspi.settings = SPISettings(freq, MSBFIRST, hwspi._mode); +#else + hwspi._freq = freq; // Save freq value for later +#endif +} + +/*! + @brief Call before issuing command(s) or data to display. Performs + chip-select (if required) and starts an SPI transaction (if + using hardware SPI and transactions are supported). Required + for all display types; not an SPI-specific function. +*/ +void Adafruit_SPITFT::startWrite(void) { + SPI_BEGIN_TRANSACTION(); + if (_cs >= 0) + SPI_CS_LOW(); +} + +/*! + @brief Call after issuing command(s) or data to display. Performs + chip-deselect (if required) and ends an SPI transaction (if + using hardware SPI and transactions are supported). Required + for all display types; not an SPI-specific function. +*/ +void Adafruit_SPITFT::endWrite(void) { + if (_cs >= 0) + SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + +// ------------------------------------------------------------------------- +// Lower-level graphics operations. These functions require a chip-select +// and/or SPI transaction around them (via startWrite(), endWrite() above). +// Higher-level graphics primitives might start a single transaction and +// then make multiple calls to these functions (e.g. circle or text +// rendering might make repeated lines or rects) before ending the +// transaction. It's more efficient than starting a transaction every time. + +/*! + @brief Draw a single pixel to the display at requested coordinates. + Not self-contained; should follow a startWrite() call. + @param x Horizontal position (0 = left). + @param y Vertical position (0 = top). + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) { + if ((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + setAddrWindow(x, y, 1, 1); + SPI_WRITE16(color); + } +} + +/*! + @brief Issue a series of pixels from memory to the display. Not self- + contained; should follow startWrite() and setAddrWindow() calls. + @param colors Pointer to array of 16-bit pixel values in '565' RGB + format. + @param len Number of elements in 'colors' array. + @param block If true (default case if unspecified), function blocks + until DMA transfer is complete. This is simply IGNORED + if DMA is not enabled. If false, the function returns + immediately after the last DMA transfer is started, + and one should use the dmaWait() function before + doing ANY other display-related activities (or even + any SPI-related activities, if using an SPI display + that shares the bus with other devices). + @param bigEndian If using DMA, and if set true, bitmap in memory is in + big-endian order (most significant byte first). By + default this is false, as most microcontrollers seem + to be little-endian and 16-bit pixel values must be + byte-swapped before issuing to the display (which tend + to be big-endian when using SPI or 8-bit parallel). + If an application can optimize around this -- for + example, a bitmap in a uint16_t array having the byte + values already reordered big-endian, this can save + some processing time here, ESPECIALLY if using this + function's non-blocking DMA mode. Not all cases are + covered...this is really here only for SAMD DMA and + much forethought on the application side. +*/ +void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len, bool block, + bool bigEndian) { + + if (!len) + return; // Avoid 0-byte transfers + + // avoid paramater-not-used complaints + (void)block; + (void)bigEndian; + +#if defined(ESP32) // ESP32 has a special SPI pixel-writing function... + if (connection == TFT_HARD_SPI) { + hwspi._spi->writePixels(colors, len * 2); + return; + } +#elif defined(ARDUINO_NRF52_ADAFRUIT) && \ + defined(NRF52840_XXAA) // Adafruit nRF52 use SPIM3 DMA at 32Mhz + // TFT and SPI DMA endian is different we need to swap bytes + if (!bigEndian) { + for (uint32_t i = 0; i < len; i++) { + colors[i] = __builtin_bswap16(colors[i]); + } + } + + // use the separate tx, rx buf variant to prevent overwrite the buffer + hwspi._spi->transfer(colors, NULL, 2 * len); + + // swap back color buffer + if (!bigEndian) { + for (uint32_t i = 0; i < len; i++) { + colors[i] = __builtin_bswap16(colors[i]); + } + } + + return; +#elif defined(USE_SPI_DMA) && \ + (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) + if ((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) { + int maxSpan = maxFillLen / 2; // One scanline max + uint8_t pixelBufIdx = 0; // Active pixel buffer number +#if defined(__SAMD51__) + if (connection == TFT_PARALLEL) { + // Switch WR pin to PWM or CCL + pinPeripheral(tft8._wr, wrPeripheral); + } +#endif // end __SAMD51__ + if (!bigEndian) { // Normal little-endian situation... + while (len) { + int count = (len < maxSpan) ? len : maxSpan; + + // Because TFT and SAMD endianisms are different, must swap + // bytes from the 'colors' array passed into a DMA working + // buffer. This can take place while the prior DMA transfer + // is in progress, hence the need for two pixelBufs. + for (int i = 0; i < count; i++) { + pixelBuf[pixelBufIdx][i] = __builtin_bswap16(*colors++); + } + // The transfers themselves are relatively small, so we don't + // need a long descriptor list. We just alternate between the + // first two, sharing pixelBufIdx for that purpose. + descriptor[pixelBufIdx].SRCADDR.reg = + (uint32_t)pixelBuf[pixelBufIdx] + count * 2; + descriptor[pixelBufIdx].BTCTRL.bit.SRCINC = 1; + descriptor[pixelBufIdx].BTCNT.reg = count * 2; + descriptor[pixelBufIdx].DESCADDR.reg = 0; + + while (dma_busy) + ; // Wait for prior line to finish + + // Move new descriptor into place... + memcpy(dptr, &descriptor[pixelBufIdx], sizeof(DmacDescriptor)); + dma_busy = true; + dma.startJob(); // Trigger SPI DMA transfer + if (connection == TFT_PARALLEL) + dma.trigger(); + pixelBufIdx = 1 - pixelBufIdx; // Swap DMA pixel buffers + + len -= count; + } + } else { // bigEndian == true + // With big-endian pixel data, this can be handled as a single + // DMA transfer using chained descriptors. Even full screen, this + // needs only a relatively short descriptor list, each + // transferring a max of 32,767 (not 32,768) pixels. The list + // was allocated large enough to accommodate a full screen's + // worth of data, so this won't run past the end of the list. + int d, numDescriptors = (len + 32766) / 32767; + for (d = 0; d < numDescriptors; d++) { + int count = (len < 32767) ? len : 32767; + descriptor[d].SRCADDR.reg = (uint32_t)colors + count * 2; + descriptor[d].BTCTRL.bit.SRCINC = 1; + descriptor[d].BTCNT.reg = count * 2; + descriptor[d].DESCADDR.reg = (uint32_t)&descriptor[d + 1]; + len -= count; + colors += count; + } + descriptor[d - 1].DESCADDR.reg = 0; + + while (dma_busy) + ; // Wait for prior transfer (if any) to finish + + // Move first descriptor into place and start transfer... + memcpy(dptr, &descriptor[0], sizeof(DmacDescriptor)); + dma_busy = true; + dma.startJob(); // Trigger SPI DMA transfer + if (connection == TFT_PARALLEL) + dma.trigger(); + } // end bigEndian + + lastFillColor = 0x0000; // pixelBuf has been sullied + lastFillLen = 0; + if (block) { + while (dma_busy) + ; // Wait for last line to complete +#if defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO) + if (connection == TFT_HARD_SPI) { + // See SAMD51/21 note in writeColor() + hwspi._spi->setDataMode(hwspi._mode); + } else { + pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO + } +#endif // end __SAMD51__ || ARDUINO_SAMD_ZERO + } + return; + } +#endif // end USE_SPI_DMA + + // All other cases (bitbang SPI or non-DMA hard SPI or parallel), + // use a loop with the normal 16-bit data write function: + while (len--) { + SPI_WRITE16(*colors++); + } +} + +/*! + @brief Wait for the last DMA transfer in a prior non-blocking + writePixels() call to complete. This does nothing if DMA + is not enabled, and is not needed if blocking writePixels() + was used (as is the default case). +*/ +void Adafruit_SPITFT::dmaWait(void) { +#if defined(USE_SPI_DMA) && (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) + while (dma_busy) + ; +#if defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO) + if (connection == TFT_HARD_SPI) { + // See SAMD51/21 note in writeColor() + hwspi._spi->setDataMode(hwspi._mode); + } else { + pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO + } +#endif // end __SAMD51__ || ARDUINO_SAMD_ZERO +#endif +} + +/*! + @brief Issue a series of pixels, all the same color. Not self- + contained; should follow startWrite() and setAddrWindow() calls. + @param color 16-bit pixel color in '565' RGB format. + @param len Number of pixels to draw. +*/ +void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) { + + if (!len) + return; // Avoid 0-byte transfers + + uint8_t hi = color >> 8, lo = color; + +#if defined(ESP32) // ESP32 has a special SPI pixel-writing function... + if (connection == TFT_HARD_SPI) { +#define SPI_MAX_PIXELS_AT_ONCE 32 +#define TMPBUF_LONGWORDS (SPI_MAX_PIXELS_AT_ONCE + 1) / 2 +#define TMPBUF_PIXELS (TMPBUF_LONGWORDS * 2) + static uint32_t temp[TMPBUF_LONGWORDS]; + uint32_t c32 = color * 0x00010001; + uint16_t bufLen = (len < TMPBUF_PIXELS) ? len : TMPBUF_PIXELS, xferLen, + fillLen; + // Fill temp buffer 32 bits at a time + fillLen = (bufLen + 1) / 2; // Round up to next 32-bit boundary + for (uint32_t t = 0; t < fillLen; t++) { + temp[t] = c32; + } + // Issue pixels in blocks from temp buffer + while (len) { // While pixels remain + xferLen = (bufLen < len) ? bufLen : len; // How many this pass? + writePixels((uint16_t *)temp, xferLen); + len -= xferLen; + } + return; + } +#elif defined(ARDUINO_NRF52_ADAFRUIT) && \ + defined(NRF52840_XXAA) // Adafruit nRF52840 use SPIM3 DMA at 32Mhz + // at most 2 scan lines + uint32_t const pixbufcount = min(len, ((uint32_t)2 * width())); + uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); + + // use SPI3 DMA if we could allocate buffer, else fall back to writing each + // pixel loop below + if (pixbuf) { + uint16_t const swap_color = __builtin_bswap16(color); + + // fill buffer with color + for (uint32_t i = 0; i < pixbufcount; i++) { + pixbuf[i] = swap_color; + } + + while (len) { + uint32_t const count = min(len, pixbufcount); + writePixels(pixbuf, count, true, true); + len -= count; + } + + rtos_free(pixbuf); + return; + } +#else // !ESP32 +#if defined(USE_SPI_DMA) && (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) + if (((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) && + (len >= 16)) { // Don't bother with DMA on short pixel runs + int i, d, numDescriptors; + if (hi == lo) { // If high & low bytes are same... + onePixelBuf = color; + // Can do this with a relatively short descriptor list, + // each transferring a max of 32,767 (not 32,768) pixels. + // This won't run off the end of the allocated descriptor list, + // since we're using much larger chunks per descriptor here. + numDescriptors = (len + 32766) / 32767; + for (d = 0; d < numDescriptors; d++) { + int count = (len < 32767) ? len : 32767; + descriptor[d].SRCADDR.reg = (uint32_t)&onePixelBuf; + descriptor[d].BTCTRL.bit.SRCINC = 0; + descriptor[d].BTCNT.reg = count * 2; + descriptor[d].DESCADDR.reg = (uint32_t)&descriptor[d + 1]; + len -= count; + } + descriptor[d - 1].DESCADDR.reg = 0; + } else { + // If high and low bytes are distinct, it's necessary to fill + // a buffer with pixel data (swapping high and low bytes because + // TFT and SAMD are different endianisms) and create a longer + // descriptor list pointing repeatedly to this data. We can do + // this slightly faster working 2 pixels (32 bits) at a time. + uint32_t *pixelPtr = (uint32_t *)pixelBuf[0], + twoPixels = __builtin_bswap16(color) * 0x00010001; + // We can avoid some or all of the buffer-filling if the color + // is the same as last time... + if (color == lastFillColor) { + // If length is longer than prior instance, fill only the + // additional pixels in the buffer and update lastFillLen. + if (len > lastFillLen) { + int fillStart = lastFillLen / 2, + fillEnd = (((len < maxFillLen) ? len : maxFillLen) + 1) / 2; + for (i = fillStart; i < fillEnd; i++) + pixelPtr[i] = twoPixels; + lastFillLen = fillEnd * 2; + } // else do nothing, don't set pixels or change lastFillLen + } else { + int fillEnd = (((len < maxFillLen) ? len : maxFillLen) + 1) / 2; + for (i = 0; i < fillEnd; i++) + pixelPtr[i] = twoPixels; + lastFillLen = fillEnd * 2; + lastFillColor = color; + } + + numDescriptors = (len + maxFillLen - 1) / maxFillLen; + for (d = 0; d < numDescriptors; d++) { + int pixels = (len < maxFillLen) ? len : maxFillLen, bytes = pixels * 2; + descriptor[d].SRCADDR.reg = (uint32_t)pixelPtr + bytes; + descriptor[d].BTCTRL.bit.SRCINC = 1; + descriptor[d].BTCNT.reg = bytes; + descriptor[d].DESCADDR.reg = (uint32_t)&descriptor[d + 1]; + len -= pixels; + } + descriptor[d - 1].DESCADDR.reg = 0; + } + memcpy(dptr, &descriptor[0], sizeof(DmacDescriptor)); +#if defined(__SAMD51__) + if (connection == TFT_PARALLEL) { + // Switch WR pin to PWM or CCL + pinPeripheral(tft8._wr, wrPeripheral); + } +#endif // end __SAMD51__ + + dma_busy = true; + dma.startJob(); + if (connection == TFT_PARALLEL) + dma.trigger(); + while (dma_busy) + ; // Wait for completion + // Unfortunately blocking is necessary. An earlier version returned + // immediately and checked dma_busy on startWrite() instead, but it + // turns out to be MUCH slower on many graphics operations (as when + // drawing lines, pixel-by-pixel), perhaps because it's a volatile + // type and doesn't cache. Working on this. +#if defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO) + if (connection == TFT_HARD_SPI) { + // SAMD51: SPI DMA seems to leave the SPI peripheral in a freaky + // state on completion. Workaround is to explicitly set it back... + // (5/17/2019: apparently SAMD21 too, in certain cases, observed + // with ST7789 display.) + hwspi._spi->setDataMode(hwspi._mode); + } else { + pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO + } +#endif // end __SAMD51__ + return; + } +#endif // end USE_SPI_DMA +#endif // end !ESP32 + + // All other cases (non-DMA hard SPI, bitbang SPI, parallel)... + + if (connection == TFT_HARD_SPI) { +#if defined(ESP8266) + do { + uint32_t pixelsThisPass = len; + if (pixelsThisPass > 50000) + pixelsThisPass = 50000; + len -= pixelsThisPass; + yield(); // Periodic yield() on long fills + while (pixelsThisPass--) { + hwspi._spi->write(hi); + hwspi._spi->write(lo); + } + } while (len); +#else // !ESP8266 + while (len--) { +#if defined(__AVR__) + AVR_WRITESPI(hi); + AVR_WRITESPI(lo); +#elif defined(ESP32) + hwspi._spi->write(hi); + hwspi._spi->write(lo); +#else + hwspi._spi->transfer(hi); + hwspi._spi->transfer(lo); +#endif + } +#endif // end !ESP8266 + } else if (connection == TFT_SOFT_SPI) { +#if defined(ESP8266) + do { + uint32_t pixelsThisPass = len; + if (pixelsThisPass > 20000) + pixelsThisPass = 20000; + len -= pixelsThisPass; + yield(); // Periodic yield() on long fills + while (pixelsThisPass--) { + for (uint16_t bit = 0, x = color; bit < 16; bit++) { + if (x & 0x8000) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + x <<= 1; + } + } + } while (len); +#else // !ESP8266 + while (len--) { +#if defined(__AVR__) + for (uint8_t bit = 0, x = hi; bit < 8; bit++) { + if (x & 0x80) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + x <<= 1; + } + for (uint8_t bit = 0, x = lo; bit < 8; bit++) { + if (x & 0x80) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + x <<= 1; + } +#else // !__AVR__ + for (uint16_t bit = 0, x = color; bit < 16; bit++) { + if (x & 0x8000) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + x <<= 1; + SPI_SCK_LOW(); + } +#endif // end !__AVR__ + } +#endif // end !ESP8266 + } else { // PARALLEL + if (hi == lo) { +#if defined(__AVR__) + len *= 2; + *tft8.writePort = hi; + while (len--) { + TFT_WR_STROBE(); + } +#elif defined(USE_FAST_PINIO) + if (!tft8.wide) { + len *= 2; + *tft8.writePort = hi; + } else { + *(volatile uint16_t *)tft8.writePort = color; + } + while (len--) { + TFT_WR_STROBE(); + } +#endif + } else { + while (len--) { +#if defined(__AVR__) + *tft8.writePort = hi; + TFT_WR_STROBE(); + *tft8.writePort = lo; +#elif defined(USE_FAST_PINIO) + if (!tft8.wide) { + *tft8.writePort = hi; + TFT_WR_STROBE(); + *tft8.writePort = lo; + } else { + *(volatile uint16_t *)tft8.writePort = color; + } +#endif + TFT_WR_STROBE(); + } + } + } +} + +/*! + @brief Draw a filled rectangle to the display. Not self-contained; + should follow startWrite(). Typically used by higher-level + graphics primitives; user code shouldn't need to call this and + is likely to use the self-contained fillRect() instead. + writeFillRect() performs its own edge clipping and rejection; + see writeFillRectPreclipped() for a more 'raw' implementation. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note Written in this deep-nested way because C by definition will + optimize for the 'if' case, not the 'else' -- avoids branches + and rejects clipped rectangles at the least-work possibility. +*/ +void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + if (w && h) { // Nonzero width and height? + if (w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if (x < _width) { // Not off right + if (h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if (y < _height) { // Not off bottom + int16_t x2 = x + w - 1; + if (x2 >= 0) { // Not off left + int16_t y2 = y + h - 1; + if (y2 >= 0) { // Not off top + // Rectangle partly or fully overlaps screen + if (x < 0) { + x = 0; + w = x2 + 1; + } // Clip left + if (y < 0) { + y = 0; + h = y2 + 1; + } // Clip top + if (x2 >= _width) { + w = _width - x; + } // Clip right + if (y2 >= _height) { + h = _height - y; + } // Clip bottom + writeFillRectPreclipped(x, y, w, h, color); + } + } + } + } + } +} + +/*! + @brief Draw a horizontal line on the display. Performs edge clipping + and rejection. Not self-contained; should follow startWrite(). + Typically used by higher-level graphics primitives; user code + shouldn't need to call this and is likely to use the self- + contained drawFastHLine() instead. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line width in pixels (positive = right of first point, + negative = point of first corner). + @param color 16-bit line color in '565' RGB format. +*/ +void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if ((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width + if (w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if (x < _width) { // Not off right + int16_t x2 = x + w - 1; + if (x2 >= 0) { // Not off left + // Line partly or fully overlaps screen + if (x < 0) { + x = 0; + w = x2 + 1; + } // Clip left + if (x2 >= _width) { + w = _width - x; + } // Clip right + writeFillRectPreclipped(x, y, w, 1, color); + } + } + } +} + +/*! + @brief Draw a vertical line on the display. Performs edge clipping and + rejection. Not self-contained; should follow startWrite(). + Typically used by higher-level graphics primitives; user code + shouldn't need to call this and is likely to use the self- + contained drawFastVLine() instead. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param h Line height in pixels (positive = below first point, + negative = above first point). + @param color 16-bit line color in '565' RGB format. +*/ +void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if ((x >= 0) && (x < _width) && h) { // X on screen, nonzero height + if (h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if (y < _height) { // Not off bottom + int16_t y2 = y + h - 1; + if (y2 >= 0) { // Not off top + // Line partly or fully overlaps screen + if (y < 0) { + y = 0; + h = y2 + 1; + } // Clip top + if (y2 >= _height) { + h = _height - y; + } // Clip bottom + writeFillRectPreclipped(x, y, 1, h, color); + } + } + } +} + +/*! + @brief A lower-level version of writeFillRect(). This version requires + all inputs are in-bounds, that width and height are positive, + and no part extends offscreen. NO EDGE CLIPPING OR REJECTION IS + PERFORMED. If higher-level graphics primitives are written to + handle their own clipping earlier in the drawing process, this + can avoid unnecessary function calls and repeated clipping + operations in the lower-level functions. + @param x Horizontal position of first corner. MUST BE WITHIN + SCREEN BOUNDS. + @param y Vertical position of first corner. MUST BE WITHIN SCREEN + BOUNDS. + @param w Rectangle width in pixels. MUST BE POSITIVE AND NOT + EXTEND OFF SCREEN. + @param h Rectangle height in pixels. MUST BE POSITIVE AND NOT + EXTEND OFF SCREEN. + @param color 16-bit fill color in '565' RGB format. + @note This is a new function, no graphics primitives besides rects + and horizontal/vertical lines are written to best use this yet. +*/ +inline void Adafruit_SPITFT::writeFillRectPreclipped(int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t color) { + setAddrWindow(x, y, w, h); + writeColor(color, (uint32_t)w * h); +} + +// ------------------------------------------------------------------------- +// Ever-so-slightly higher-level graphics operations. Similar to the 'write' +// functions above, but these contain their own chip-select and SPI +// transactions as needed (via startWrite(), endWrite()). They're typically +// used solo -- as graphics primitives in themselves, not invoked by higher- +// level primitives (which should use the functions above for better +// performance). + +/*! + @brief Draw a single pixel to the display at requested coordinates. + Self-contained and provides its own transaction as needed + (see writePixel(x,y,color) for a lower-level variant). + Edge clipping is performed here. + @param x Horizontal position (0 = left). + @param y Vertical position (0 = top). + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color) { + // Clip first... + if ((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + // THEN set up transaction (if needed) and draw... + startWrite(); + setAddrWindow(x, y, 1, 1); + SPI_WRITE16(color); + endWrite(); + } +} + +/*! + @brief Draw a filled rectangle to the display. Self-contained and + provides its own transaction as needed (see writeFillRect() or + writeFillRectPreclipped() for lower-level variants). Edge + clipping and rejection is performed here. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note This repeats the writeFillRect() function almost in its entirety, + with the addition of a transaction start/end. It's done this way + (rather than starting the transaction and calling writeFillRect() + to handle clipping and so forth) so that the transaction isn't + performed at all if the rectangle is rejected. It's really not + that much code. +*/ +void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + if (w && h) { // Nonzero width and height? + if (w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if (x < _width) { // Not off right + if (h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if (y < _height) { // Not off bottom + int16_t x2 = x + w - 1; + if (x2 >= 0) { // Not off left + int16_t y2 = y + h - 1; + if (y2 >= 0) { // Not off top + // Rectangle partly or fully overlaps screen + if (x < 0) { + x = 0; + w = x2 + 1; + } // Clip left + if (y < 0) { + y = 0; + h = y2 + 1; + } // Clip top + if (x2 >= _width) { + w = _width - x; + } // Clip right + if (y2 >= _height) { + h = _height - y; + } // Clip bottom + startWrite(); + writeFillRectPreclipped(x, y, w, h, color); + endWrite(); + } + } + } + } + } +} + +/*! + @brief Draw a horizontal line on the display. Self-contained and + provides its own transaction as needed (see writeFastHLine() for + a lower-level variant). Edge clipping and rejection is performed + here. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line width in pixels (positive = right of first point, + negative = point of first corner). + @param color 16-bit line color in '565' RGB format. + @note This repeats the writeFastHLine() function almost in its + entirety, with the addition of a transaction start/end. It's + done this way (rather than starting the transaction and calling + writeFastHLine() to handle clipping and so forth) so that the + transaction isn't performed at all if the line is rejected. +*/ +void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if ((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width + if (w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if (x < _width) { // Not off right + int16_t x2 = x + w - 1; + if (x2 >= 0) { // Not off left + // Line partly or fully overlaps screen + if (x < 0) { + x = 0; + w = x2 + 1; + } // Clip left + if (x2 >= _width) { + w = _width - x; + } // Clip right + startWrite(); + writeFillRectPreclipped(x, y, w, 1, color); + endWrite(); + } + } + } +} + +/*! + @brief Draw a vertical line on the display. Self-contained and provides + its own transaction as needed (see writeFastHLine() for a lower- + level variant). Edge clipping and rejection is performed here. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param h Line height in pixels (positive = below first point, + negative = above first point). + @param color 16-bit line color in '565' RGB format. + @note This repeats the writeFastVLine() function almost in its + entirety, with the addition of a transaction start/end. It's + done this way (rather than starting the transaction and calling + writeFastVLine() to handle clipping and so forth) so that the + transaction isn't performed at all if the line is rejected. +*/ +void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if ((x >= 0) && (x < _width) && h) { // X on screen, nonzero height + if (h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if (y < _height) { // Not off bottom + int16_t y2 = y + h - 1; + if (y2 >= 0) { // Not off top + // Line partly or fully overlaps screen + if (y < 0) { + y = 0; + h = y2 + 1; + } // Clip top + if (y2 >= _height) { + h = _height - y; + } // Clip bottom + startWrite(); + writeFillRectPreclipped(x, y, 1, h, color); + endWrite(); + } + } + } +} + +/*! + @brief Essentially writePixel() with a transaction around it. I don't + think this is in use by any of our code anymore (believe it was + for some older BMP-reading examples), but is kept here in case + any user code relies on it. Consider it DEPRECATED. + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::pushColor(uint16_t color) { + startWrite(); + SPI_WRITE16(color); + endWrite(); +} + +/*! + @brief Draw a 16-bit image (565 RGB) at the specified (x,y) position. + For 16-bit display devices; no color reduction performed. + Adapted from https://github.com/PaulStoffregen/ILI9341_t3 + by Marc MERLIN. See examples/pictureEmbed to use this. + 5/6/2017: function name and arguments have changed for + compatibility with current GFX library and to avoid naming + problems in prior implementation. Formerly drawBitmap() with + arguments in different order. Handles its own transaction and + edge clipping/rejection. + @param x Top left corner horizontal coordinate. + @param y Top left corner vertical coordinate. + @param pcolors Pointer to 16-bit array of pixel values. + @param w Width of bitmap in pixels. + @param h Height of bitmap in pixels. +*/ +void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, uint16_t *pcolors, + int16_t w, int16_t h) { + + int16_t x2, y2; // Lower-right coord + if ((x >= _width) || // Off-edge right + (y >= _height) || // " top + ((x2 = (x + w - 1)) < 0) || // " left + ((y2 = (y + h - 1)) < 0)) + return; // " bottom + + int16_t bx1 = 0, by1 = 0, // Clipped top-left within bitmap + saveW = w; // Save original bitmap width value + if (x < 0) { // Clip left + w += x; + bx1 = -x; + x = 0; + } + if (y < 0) { // Clip top + h += y; + by1 = -y; + y = 0; + } + if (x2 >= _width) + w = _width - x; // Clip right + if (y2 >= _height) + h = _height - y; // Clip bottom + + pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left + startWrite(); + setAddrWindow(x, y, w, h); // Clipped area + while (h--) { // For each (clipped) scanline... + writePixels(pcolors, w); // Push one (clipped) row + pcolors += saveW; // Advance pointer by one full (unclipped) line + } + endWrite(); +} + +// ------------------------------------------------------------------------- +// Miscellaneous class member functions that don't draw anything. + +/*! + @brief Invert the colors of the display (if supported by hardware). + Self-contained, no transaction setup required. + @param i true = inverted display, false = normal display. +*/ +void Adafruit_SPITFT::invertDisplay(bool i) { + startWrite(); + writeCommand(i ? invertOnCommand : invertOffCommand); + endWrite(); +} + +/*! + @brief Given 8-bit red, green and blue values, return a 'packed' + 16-bit color value in '565' RGB format (5 bits red, 6 bits + green, 5 bits blue). This is just a mathematical operation, + no hardware is touched. + @param red 8-bit red brightnesss (0 = off, 255 = max). + @param green 8-bit green brightnesss (0 = off, 255 = max). + @param blue 8-bit blue brightnesss (0 = off, 255 = max). + @return 'Packed' 16-bit color value (565 format). +*/ +uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) { + return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3); +} + +/*! +@brief Adafruit_SPITFT Send Command handles complete sending of commands and +data +@param commandByte The Command Byte +@param dataBytes A pointer to the Data bytes to send +@param numDataBytes The number of bytes we should send +*/ +void Adafruit_SPITFT::sendCommand(uint8_t commandByte, uint8_t *dataBytes, + uint8_t numDataBytes) { + SPI_BEGIN_TRANSACTION(); + if (_cs >= 0) + SPI_CS_LOW(); + + SPI_DC_LOW(); // Command mode + spiWrite(commandByte); // Send the command byte + + SPI_DC_HIGH(); + for (int i = 0; i < numDataBytes; i++) { + if ((connection == TFT_PARALLEL) && tft8.wide) { + SPI_WRITE16(*(uint16_t *)dataBytes); + dataBytes += 2; + } else { + spiWrite(*dataBytes); // Send the data bytes + dataBytes++; + } + } + + if (_cs >= 0) + SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + +/*! + @brief Adafruit_SPITFT Send Command handles complete sending of commands and + data + @param commandByte The Command Byte + @param dataBytes A pointer to the Data bytes to send + @param numDataBytes The number of bytes we should send + */ +void Adafruit_SPITFT::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, + uint8_t numDataBytes) { + SPI_BEGIN_TRANSACTION(); + if (_cs >= 0) + SPI_CS_LOW(); + + SPI_DC_LOW(); // Command mode + spiWrite(commandByte); // Send the command byte + + SPI_DC_HIGH(); + for (int i = 0; i < numDataBytes; i++) { + if ((connection == TFT_PARALLEL) && tft8.wide) { + SPI_WRITE16(*(uint16_t *)dataBytes); + dataBytes += 2; + } else { + spiWrite(pgm_read_byte(dataBytes++)); + } + } + + if (_cs >= 0) + SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + +/*! + @brief Adafruit_SPITFT sendCommand16 handles complete sending of + commands and data for 16-bit parallel displays. Currently somewhat + rigged for the NT35510, which has the odd behavior of wanting + commands 16-bit, but subsequent data as 8-bit values, despite + the 16-bit bus (high byte is always 0). Also seems to require + issuing and incrementing address with each transfer. + @param commandWord The command word (16 bits) + @param dataBytes A pointer to the data bytes to send + @param numDataBytes The number of bytes we should send + */ +void Adafruit_SPITFT::sendCommand16(uint16_t commandWord, + const uint8_t *dataBytes, + uint8_t numDataBytes) { + SPI_BEGIN_TRANSACTION(); + if (_cs >= 0) + SPI_CS_LOW(); + + if (numDataBytes == 0) { + SPI_DC_LOW(); // Command mode + SPI_WRITE16(commandWord); // Send the command word + SPI_DC_HIGH(); // Data mode + } + for (int i = 0; i < numDataBytes; i++) { + SPI_DC_LOW(); // Command mode + SPI_WRITE16(commandWord); // Send the command word + SPI_DC_HIGH(); // Data mode + commandWord++; + SPI_WRITE16((uint16_t)pgm_read_byte(dataBytes++)); + } + + if (_cs >= 0) + SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + +/*! + @brief Read 8 bits of data from display configuration memory (not RAM). + This is highly undocumented/supported and should be avoided, + function is only included because some of the examples use it. + @param commandByte + The command register to read data from. + @param index + The byte index into the command to read from. + @return Unsigned 8-bit data read from display register. + */ +/**************************************************************************/ +uint8_t Adafruit_SPITFT::readcommand8(uint8_t commandByte, uint8_t index) { + uint8_t result; + startWrite(); + SPI_DC_LOW(); // Command mode + spiWrite(commandByte); + SPI_DC_HIGH(); // Data mode + do { + result = spiRead(); + } while (index--); // Discard bytes up to index'th + endWrite(); + return result; +} + +/*! + @brief Read 16 bits of data from display register. + For 16-bit parallel displays only. + @param addr Command/register to access. + @return Unsigned 16-bit data. + */ +uint16_t Adafruit_SPITFT::readcommand16(uint16_t addr) { +#if defined(USE_FAST_PINIO) // NOT SUPPORTED without USE_FAST_PINIO + uint16_t result = 0; + if ((connection == TFT_PARALLEL) && tft8.wide) { + startWrite(); + SPI_DC_LOW(); // Command mode + SPI_WRITE16(addr); + SPI_DC_HIGH(); // Data mode + TFT_RD_LOW(); // Read line LOW +#if defined(HAS_PORT_SET_CLR) + *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state + result = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state +#else // !HAS_PORT_SET_CLR + *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state + result = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state +#endif // end !HAS_PORT_SET_CLR + TFT_RD_HIGH(); // Read line HIGH + endWrite(); + } + return result; +#else + (void)addr; // disable -Wunused-parameter warning + return 0; +#endif // end !USE_FAST_PINIO +} + +// ------------------------------------------------------------------------- +// Lowest-level hardware-interfacing functions. Many of these are inline and +// compile to different things based on #defines -- typically just a few +// instructions. Others, not so much, those are not inlined. + +/*! + @brief Start an SPI transaction if using the hardware SPI interface to + the display. If using an earlier version of the Arduino platform + (before the addition of SPI transactions), this instead attempts + to set up the SPI clock and mode. No action is taken if the + connection is not hardware SPI-based. This does NOT include a + chip-select operation -- see startWrite() for a function that + encapsulated both actions. +*/ +inline void Adafruit_SPITFT::SPI_BEGIN_TRANSACTION(void) { + if (connection == TFT_HARD_SPI) { +#if defined(SPI_HAS_TRANSACTION) + hwspi._spi->beginTransaction(hwspi.settings); +#else // No transactions, configure SPI manually... +#if defined(__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) + hwspi._spi->setClockDivider(SPI_CLOCK_DIV2); +#elif defined(__arm__) + hwspi._spi->setClockDivider(11); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->setFrequency(hwspi._freq); +#elif defined(RASPI) || defined(ARDUINO_ARCH_STM32F1) + hwspi._spi->setClock(hwspi._freq); +#endif + hwspi._spi->setBitOrder(MSBFIRST); + hwspi._spi->setDataMode(hwspi._mode); +#endif // end !SPI_HAS_TRANSACTION + } +} + +/*! + @brief End an SPI transaction if using the hardware SPI interface to + the display. No action is taken if the connection is not + hardware SPI-based or if using an earlier version of the Arduino + platform (before the addition of SPI transactions). This does + NOT include a chip-deselect operation -- see endWrite() for a + function that encapsulated both actions. +*/ +inline void Adafruit_SPITFT::SPI_END_TRANSACTION(void) { +#if defined(SPI_HAS_TRANSACTION) + if (connection == TFT_HARD_SPI) { + hwspi._spi->endTransaction(); + } +#endif +} + +/*! + @brief Issue a single 8-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the byte. This is another of + those functions in the library with a now-not-accurate name + that's being maintained for compatibility with outside code. + This function is used even if display connection is parallel. + @param b 8-bit value to write. +*/ +void Adafruit_SPITFT::spiWrite(uint8_t b) { + if (connection == TFT_HARD_SPI) { +#if defined(__AVR__) + AVR_WRITESPI(b); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write(b); +#else + hwspi._spi->transfer(b); +#endif + } else if (connection == TFT_SOFT_SPI) { + for (uint8_t bit = 0; bit < 8; bit++) { + if (b & 0x80) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + b <<= 1; + SPI_SCK_LOW(); + } + } else { // TFT_PARALLEL +#if defined(__AVR__) + *tft8.writePort = b; +#elif defined(USE_FAST_PINIO) + if (!tft8.wide) + *tft8.writePort = b; + else + *(volatile uint16_t *)tft8.writePort = b; +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Write a single command byte to the display. Chip-select and + transaction must have been previously set -- this ONLY sets + the device to COMMAND mode, issues the byte and then restores + DATA mode. There is no corresponding explicit writeData() + function -- just use spiWrite(). + @param cmd 8-bit command to write. +*/ +void Adafruit_SPITFT::writeCommand(uint8_t cmd) { + SPI_DC_LOW(); + spiWrite(cmd); + SPI_DC_HIGH(); +} + +/*! + @brief Read a single 8-bit value from the display. Chip-select and + transaction must have been previously set -- this ONLY reads + the byte. This is another of those functions in the library + with a now-not-accurate name that's being maintained for + compatibility with outside code. This function is used even if + display connection is parallel. + @return Unsigned 8-bit value read (always zero if USE_FAST_PINIO is + not supported by the MCU architecture). +*/ +uint8_t Adafruit_SPITFT::spiRead(void) { + uint8_t b = 0; + uint16_t w = 0; + if (connection == TFT_HARD_SPI) { + return hwspi._spi->transfer((uint8_t)0); + } else if (connection == TFT_SOFT_SPI) { + if (swspi._miso >= 0) { + for (uint8_t i = 0; i < 8; i++) { + SPI_SCK_HIGH(); + b <<= 1; + if (SPI_MISO_READ()) + b++; + SPI_SCK_LOW(); + } + } + return b; + } else { // TFT_PARALLEL + if (tft8._rd >= 0) { +#if defined(USE_FAST_PINIO) + TFT_RD_LOW(); // Read line LOW +#if defined(__AVR__) + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.portDir = 0xFF; // Restore port to output +#else // !__AVR__ + if (!tft8.wide) { // 8-bit TFT connection +#if defined(HAS_PORT_SET_CLR) + *tft8.dirClr = 0xFF; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.dirSet = 0xFF; // Restore port to output +#else // !HAS_PORT_SET_CLR + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.portDir = 0xFF; // Restore port to output +#endif // end HAS_PORT_SET_CLR + } else { // 16-bit TFT connection +#if defined(HAS_PORT_SET_CLR) + *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state +#else // !HAS_PORT_SET_CLR + *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state +#endif // end !HAS_PORT_SET_CLR + } + TFT_RD_HIGH(); // Read line HIGH +#endif // end !__AVR__ +#else // !USE_FAST_PINIO + w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO +#endif // end !USE_FAST_PINIO + } + return w; + } +} + +/*! + @brief Issue a single 16-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the word. + Thus operates ONLY on 'wide' (16-bit) parallel displays! + @param w 16-bit value to write. +*/ +void Adafruit_SPITFT::write16(uint16_t w) { + if (connection == TFT_PARALLEL) { +#if defined(USE_FAST_PINIO) + if (tft8.wide) + *(volatile uint16_t *)tft8.writePort = w; +#else + (void)w; // disable -Wunused-parameter warning +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Write a single command word to the display. Chip-select and + transaction must have been previously set -- this ONLY sets + the device to COMMAND mode, issues the byte and then restores + DATA mode. This operates ONLY on 'wide' (16-bit) parallel + displays! + @param cmd 16-bit command to write. +*/ +void Adafruit_SPITFT::writeCommand16(uint16_t cmd) { + SPI_DC_LOW(); + write16(cmd); + SPI_DC_HIGH(); +} + +/*! + @brief Read a single 16-bit value from the display. Chip-select and + transaction must have been previously set -- this ONLY reads + the byte. This operates ONLY on 'wide' (16-bit) parallel + displays! + @return Unsigned 16-bit value read (always zero if USE_FAST_PINIO is + not supported by the MCU architecture). +*/ +uint16_t Adafruit_SPITFT::read16(void) { + uint16_t w = 0; + if (connection == TFT_PARALLEL) { + if (tft8._rd >= 0) { +#if defined(USE_FAST_PINIO) + TFT_RD_LOW(); // Read line LOW + if (tft8.wide) { // 16-bit TFT connection +#if defined(HAS_PORT_SET_CLR) + *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state +#else // !HAS_PORT_SET_CLR + *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state +#endif // end !HAS_PORT_SET_CLR + } + TFT_RD_HIGH(); // Read line HIGH +#else // !USE_FAST_PINIO + w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO +#endif // end !USE_FAST_PINIO + } + } + return w; +} + +/*! + @brief Set the software (bitbang) SPI MOSI line HIGH. +*/ +inline void Adafruit_SPITFT::SPI_MOSI_HIGH(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *swspi.mosiPortSet = 1; +#else // !KINETISK + *swspi.mosiPortSet = swspi.mosiPinMask; +#endif +#else // !HAS_PORT_SET_CLR + *swspi.mosiPort |= swspi.mosiPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._mosi, HIGH); +#if defined(ESP32) + for (volatile uint8_t i = 0; i < 1; i++) + ; +#endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI MOSI line LOW. +*/ +inline void Adafruit_SPITFT::SPI_MOSI_LOW(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *swspi.mosiPortClr = 1; +#else // !KINETISK + *swspi.mosiPortClr = swspi.mosiPinMask; +#endif +#else // !HAS_PORT_SET_CLR + *swspi.mosiPort &= swspi.mosiPinMaskClr; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._mosi, LOW); +#if defined(ESP32) + for (volatile uint8_t i = 0; i < 1; i++) + ; +#endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI SCK line HIGH. +*/ +inline void Adafruit_SPITFT::SPI_SCK_HIGH(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *swspi.sckPortSet = 1; +#else // !KINETISK + *swspi.sckPortSet = swspi.sckPinMask; +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + for (volatile uint8_t i = 0; i < 1; i++) + ; +#endif +#endif +#else // !HAS_PORT_SET_CLR + *swspi.sckPort |= swspi.sckPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._sck, HIGH); +#if defined(ESP32) + for (volatile uint8_t i = 0; i < 1; i++) + ; +#endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI SCK line LOW. +*/ +inline void Adafruit_SPITFT::SPI_SCK_LOW(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *swspi.sckPortClr = 1; +#else // !KINETISK + *swspi.sckPortClr = swspi.sckPinMask; +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + for (volatile uint8_t i = 0; i < 1; i++) + ; +#endif +#endif +#else // !HAS_PORT_SET_CLR + *swspi.sckPort &= swspi.sckPinMaskClr; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._sck, LOW); +#if defined(ESP32) + for (volatile uint8_t i = 0; i < 1; i++) + ; +#endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Read the state of the software (bitbang) SPI MISO line. + @return true if HIGH, false if LOW. +*/ +inline bool Adafruit_SPITFT::SPI_MISO_READ(void) { +#if defined(USE_FAST_PINIO) +#if defined(KINETISK) + return *swspi.misoPort; +#else // !KINETISK + return *swspi.misoPort & swspi.misoPinMask; +#endif // end !KINETISK +#else // !USE_FAST_PINIO + return digitalRead(swspi._miso); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Issue a single 16-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the word. Despite the name, + this function is used even if display connection is parallel; + name was maintaned for backward compatibility. Naming is also + not consistent with the 8-bit version, spiWrite(). Sorry about + that. Again, staying compatible with outside code. + @param w 16-bit value to write. +*/ +void Adafruit_SPITFT::SPI_WRITE16(uint16_t w) { + if (connection == TFT_HARD_SPI) { +#if defined(__AVR__) + AVR_WRITESPI(w >> 8); + AVR_WRITESPI(w); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write16(w); +#else + hwspi._spi->transfer(w >> 8); + hwspi._spi->transfer(w); +#endif + } else if (connection == TFT_SOFT_SPI) { + for (uint8_t bit = 0; bit < 16; bit++) { + if (w & 0x8000) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + w <<= 1; + } + } else { // TFT_PARALLEL +#if defined(__AVR__) + *tft8.writePort = w >> 8; + TFT_WR_STROBE(); + *tft8.writePort = w; +#elif defined(USE_FAST_PINIO) + if (!tft8.wide) { + *tft8.writePort = w >> 8; + TFT_WR_STROBE(); + *tft8.writePort = w; + } else { + *(volatile uint16_t *)tft8.writePort = w; + } +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Issue a single 32-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the longword. Despite the + name, this function is used even if display connection is + parallel; name was maintaned for backward compatibility. Naming + is also not consistent with the 8-bit version, spiWrite(). + Sorry about that. Again, staying compatible with outside code. + @param l 32-bit value to write. +*/ +void Adafruit_SPITFT::SPI_WRITE32(uint32_t l) { + if (connection == TFT_HARD_SPI) { +#if defined(__AVR__) + AVR_WRITESPI(l >> 24); + AVR_WRITESPI(l >> 16); + AVR_WRITESPI(l >> 8); + AVR_WRITESPI(l); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write32(l); +#else + hwspi._spi->transfer(l >> 24); + hwspi._spi->transfer(l >> 16); + hwspi._spi->transfer(l >> 8); + hwspi._spi->transfer(l); +#endif + } else if (connection == TFT_SOFT_SPI) { + for (uint8_t bit = 0; bit < 32; bit++) { + if (l & 0x80000000) + SPI_MOSI_HIGH(); + else + SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + l <<= 1; + } + } else { // TFT_PARALLEL +#if defined(__AVR__) + *tft8.writePort = l >> 24; + TFT_WR_STROBE(); + *tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *tft8.writePort = l >> 8; + TFT_WR_STROBE(); + *tft8.writePort = l; +#elif defined(USE_FAST_PINIO) + if (!tft8.wide) { + *tft8.writePort = l >> 24; + TFT_WR_STROBE(); + *tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *tft8.writePort = l >> 8; + TFT_WR_STROBE(); + *tft8.writePort = l; + } else { + *(volatile uint16_t *)tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *(volatile uint16_t *)tft8.writePort = l; + } +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Set the WR line LOW, then HIGH. Used for parallel-connected + interfaces when writing data. +*/ +inline void Adafruit_SPITFT::TFT_WR_STROBE(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *tft8.wrPortClr = 1; + *tft8.wrPortSet = 1; +#else // !KINETISK + *tft8.wrPortClr = tft8.wrPinMask; + *tft8.wrPortSet = tft8.wrPinMask; +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + *tft8.wrPort &= tft8.wrPinMaskClr; + *tft8.wrPort |= tft8.wrPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._wr, LOW); + digitalWrite(tft8._wr, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the RD line HIGH. Used for parallel-connected interfaces + when reading data. +*/ +inline void Adafruit_SPITFT::TFT_RD_HIGH(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + *tft8.rdPortSet = tft8.rdPinMask; +#else // !HAS_PORT_SET_CLR + *tft8.rdPort |= tft8.rdPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._rd, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the RD line LOW. Used for parallel-connected interfaces + when reading data. +*/ +inline void Adafruit_SPITFT::TFT_RD_LOW(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + *tft8.rdPortClr = tft8.rdPinMask; +#else // !HAS_PORT_SET_CLR + *tft8.rdPort &= tft8.rdPinMaskClr; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._rd, LOW); +#endif // end !USE_FAST_PINIO +} + +#endif // end __AVR_ATtiny85__ diff --git a/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h b/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h new file mode 100644 index 0000000..78e850c --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.h @@ -0,0 +1,525 @@ +/*! + * @file Adafruit_SPITFT.h + * + * Part of Adafruit's GFX graphics library. Originally this class was + * written to handle a range of color TFT displays connected via SPI, + * but over time this library and some display-specific subclasses have + * mutated to include some color OLEDs as well as parallel-interfaced + * displays. The name's been kept for the sake of older code. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Limor "ladyada" Fried for Adafruit Industries, + * with contributions from the open source community. + * + * BSD license, all text here must be included in any redistribution. + */ + +#ifndef _ADAFRUIT_SPITFT_H_ +#define _ADAFRUIT_SPITFT_H_ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all + +#include "Adafruit_GFX.h" +#include + +// HARDWARE CONFIG --------------------------------------------------------- + +#if defined(__AVR__) +typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit +#define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(ARDUINO_STM32_FEATHER) // WICED +typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED +typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +#elif defined(__arm__) +#if defined(ARDUINO_ARCH_SAMD) +// Adafruit M0, M4 +typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +#elif defined(CORE_TEENSY) +// PJRC Teensy 4.x +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x +typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit + // PJRC Teensy 3.x +#else +typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit +#endif +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +#else +// Arduino Due? +typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +// USE_FAST_PINIO not available here (yet)...Due has a totally different +// GPIO register set and will require some changes elsewhere (e.g. in +// constructors especially). +#endif +#else // !ARM +// Probably ESP8266 or ESP32. USE_FAST_PINIO is not available here (yet) +// but don't worry about it too much...the digitalWrite() implementation +// on these platforms is reasonably efficient and already RAM-resident, +// only gotcha then is no parallel connection support for now. +typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +#endif // end !ARM +typedef volatile ADAGFX_PORT_t *PORTreg_t; ///< PORT register type + +#if defined(__AVR__) +#define DEFAULT_SPI_FREQ 8000000L ///< Hardware SPI default speed +#else +#define DEFAULT_SPI_FREQ 16000000L ///< Hardware SPI default speed +#endif + +#if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || \ + defined(ADAFRUIT_PYGAMER_M4_EXPRESS) || \ + defined(ADAFRUIT_MONSTER_M4SK_EXPRESS) || defined(NRF52_SERIES) || \ + defined(ADAFRUIT_CIRCUITPLAYGROUND_M0) +#define USE_SPI_DMA ///< Auto DMA +#else + //#define USE_SPI_DMA ///< If set, + // use DMA if available +#endif +// Another "oops" name -- this now also handles parallel DMA. +// If DMA is enabled, Arduino sketch MUST #include +// Estimated RAM usage: +// 4 bytes/pixel on display major axis + 8 bytes/pixel on minor axis, +// e.g. 320x240 pixels = 320 * 4 + 240 * 8 = 3,200 bytes. + +#if defined(USE_SPI_DMA) && (defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)) +#include +#endif + +// This is kind of a kludge. Needed a way to disambiguate the software SPI +// and parallel constructors via their argument lists. Originally tried a +// bool as the first argument to the parallel constructor (specifying 8-bit +// vs 16-bit interface) but the compiler regards this as equivalent to an +// integer and thus still ambiguous. SO...the parallel constructor requires +// an enumerated type as the first argument: tft8 (for 8-bit parallel) or +// tft16 (for 16-bit)...even though 16-bit isn't fully implemented or tested +// and might never be, still needed that disambiguation from soft SPI. +/*! For first arg to parallel constructor */ +enum tftBusWidth { tft8bitbus, tft16bitbus }; + +// CLASS DEFINITION -------------------------------------------------------- + +/*! + @brief Adafruit_SPITFT is an intermediary class between Adafruit_GFX + and various hardware-specific subclasses for different displays. + It handles certain operations that are common to a range of + displays (address window, area fills, etc.). Originally these were + all color TFT displays interfaced via SPI, but it's since expanded + to include color OLEDs and parallel-interfaced TFTs. THE NAME HAS + BEEN KEPT TO AVOID BREAKING A LOT OF SUBCLASSES AND EXAMPLE CODE. + Many of the class member functions similarly live on with names + that don't necessarily accurately describe what they're doing, + again to avoid breaking a lot of other code. If in doubt, read + the comments. +*/ +class Adafruit_SPITFT : public Adafruit_GFX { + +public: + // CONSTRUCTORS -------------------------------------------------------- + + // Software SPI constructor: expects width & height (at default rotation + // setting 0), 4 signal pins (cs, dc, mosi, sclk), 2 optional pins + // (reset, miso). cs argument is required but can be -1 if unused -- + // rather than moving it to the optional arguments, it was done this way + // to avoid breaking existing code (-1 option was a later addition). + Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, int8_t dc, int8_t mosi, + int8_t sck, int8_t rst = -1, int8_t miso = -1); + + // Hardware SPI constructor using the default SPI port: expects width & + // height (at default rotation setting 0), 2 signal pins (cs, dc), + // optional reset pin. cs is required but can be -1 if unused -- rather + // than moving it to the optional arguments, it was done this way to + // avoid breaking existing code (-1 option was a later addition). + Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, int8_t dc, + int8_t rst = -1); + +#if !defined(ESP8266) // See notes in .cpp + // Hardware SPI constructor using an arbitrary SPI peripheral: expects + // width & height (rotation 0), SPIClass pointer, 2 signal pins (cs, dc) + // and optional reset pin. cs is required but can be -1 if unused. + Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, int8_t cs, + int8_t dc, int8_t rst = -1); +#endif // end !ESP8266 + + // Parallel constructor: expects width & height (rotation 0), flag + // indicating whether 16-bit (true) or 8-bit (false) interface, 3 signal + // pins (d0, wr, dc), 3 optional pins (cs, rst, rd). 16-bit parallel + // isn't even fully implemented but the 'wide' flag was added as a + // required argument to avoid ambiguity with other constructors. + Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth, int8_t d0, + int8_t wr, int8_t dc, int8_t cs = -1, int8_t rst = -1, + int8_t rd = -1); + + // DESTRUCTOR ---------------------------------------------------------- + + ~Adafruit_SPITFT(){}; + + // CLASS MEMBER FUNCTIONS ---------------------------------------------- + + // These first two functions MUST be declared by subclasses: + + /*! + @brief Display-specific initialization function. + @param freq SPI frequency, in hz (or 0 for default or unused). + */ + virtual void begin(uint32_t freq) = 0; + + /*! + @brief Set up the specific display hardware's "address window" + for subsequent pixel-pushing operations. + @param x Leftmost pixel of area to be drawn (MUST be within + display bounds at current rotation setting). + @param y Topmost pixel of area to be drawn (MUST be within + display bounds at current rotation setting). + @param w Width of area to be drawn, in pixels (MUST be >0 and, + added to x, within display bounds at current rotation). + @param h Height of area to be drawn, in pixels (MUST be >0 and, + added to x, within display bounds at current rotation). + */ + virtual void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, + uint16_t h) = 0; + + // Remaining functions do not need to be declared in subclasses + // unless they wish to provide hardware-specific optimizations. + // Brief comments here...documented more thoroughly in .cpp file. + + // Subclass' begin() function invokes this to initialize hardware. + // freq=0 to use default SPI speed. spiMode must be one of the SPI_MODEn + // values defined in SPI.h, which are NOT the same as 0 for SPI_MODE0, + // 1 for SPI_MODE1, etc...use ONLY the SPI_MODEn defines! Only! + // Name is outdated (interface may be parallel) but for compatibility: + void initSPI(uint32_t freq = 0, uint8_t spiMode = SPI_MODE0); + void setSPISpeed(uint32_t freq); + // Chip select and/or hardware SPI transaction start as needed: + void startWrite(void); + // Chip deselect and/or hardware SPI transaction end as needed: + void endWrite(void); + void sendCommand(uint8_t commandByte, uint8_t *dataBytes, + uint8_t numDataBytes); + void sendCommand(uint8_t commandByte, const uint8_t *dataBytes = NULL, + uint8_t numDataBytes = 0); + void sendCommand16(uint16_t commandWord, const uint8_t *dataBytes = NULL, + uint8_t numDataBytes = 0); + uint8_t readcommand8(uint8_t commandByte, uint8_t index = 0); + uint16_t readcommand16(uint16_t addr); + + // These functions require a chip-select and/or SPI transaction + // around them. Higher-level graphics primitives might start a + // single transaction and then make multiple calls to these functions + // (e.g. circle or text rendering might make repeated lines or rects) + // before ending the transaction. It's more efficient than starting a + // transaction every time. + void writePixel(int16_t x, int16_t y, uint16_t color); + void writePixels(uint16_t *colors, uint32_t len, bool block = true, + bool bigEndian = false); + void writeColor(uint16_t color, uint32_t len); + void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + // This is a new function, similar to writeFillRect() except that + // all arguments MUST be onscreen, sorted and clipped. If higher-level + // primitives can handle their own sorting/clipping, it avoids repeating + // such operations in the low-level code, making it potentially faster. + // CALLING THIS WITH UNCLIPPED OR NEGATIVE VALUES COULD BE DISASTROUS. + inline void writeFillRectPreclipped(int16_t x, int16_t y, int16_t w, + int16_t h, uint16_t color); + // Another new function, companion to the new non-blocking + // writePixels() variant. + void dmaWait(void); + + // These functions are similar to the 'write' functions above, but with + // a chip-select and/or SPI transaction built-in. They're typically used + // solo -- that is, as graphics primitives in themselves, not invoked by + // higher-level primitives (which should use the functions above). + void drawPixel(int16_t x, int16_t y, uint16_t color); + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + // A single-pixel push encapsulated in a transaction. I don't think + // this is used anymore (BMP demos might've used it?) but is provided + // for backward compatibility, consider it deprecated: + void pushColor(uint16_t color); + + using Adafruit_GFX::drawRGBBitmap; // Check base class first + void drawRGBBitmap(int16_t x, int16_t y, uint16_t *pcolors, int16_t w, + int16_t h); + + void invertDisplay(bool i); + uint16_t color565(uint8_t r, uint8_t g, uint8_t b); + + // Despite parallel additions, function names kept for compatibility: + void spiWrite(uint8_t b); // Write single byte as DATA + void writeCommand(uint8_t cmd); // Write single byte as COMMAND + uint8_t spiRead(void); // Read single byte of data + void write16(uint16_t w); // Write 16-bit value as DATA + void writeCommand16(uint16_t cmd); // Write 16-bit value as COMMAND + uint16_t read16(void); // Read single 16-bit value + + // Most of these low-level functions were formerly macros in + // Adafruit_SPITFT_Macros.h. Some have been made into inline functions + // to avoid macro mishaps. Despite the addition of code for a parallel + // display interface, the names have been kept for backward + // compatibility (some subclasses may be invoking these): + void SPI_WRITE16(uint16_t w); // Not inline + void SPI_WRITE32(uint32_t l); // Not inline + // Old code had both a spiWrite16() function and SPI_WRITE16 macro + // in addition to the SPI_WRITE32 macro. The latter two have been + // made into functions here, and spiWrite16() removed (use SPI_WRITE16() + // instead). It looks like most subclasses had gotten comfortable with + // SPI_WRITE16 and SPI_WRITE32 anyway so those names were kept rather + // than the less-obnoxious camelcase variants, oh well. + + // Placing these functions entirely in the class definition inlines + // them implicitly them while allowing their use in other code: + + /*! + @brief Set the chip-select line HIGH. Does NOT check whether CS pin + is set (>=0), that should be handled in calling function. + Despite function name, this is used even if the display + connection is parallel. + */ + void SPI_CS_HIGH(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *csPortSet = 1; +#else // !KINETISK + *csPortSet = csPinMask; +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + *csPort |= csPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(_cs, HIGH); +#endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the chip-select line LOW. Does NOT check whether CS pin + is set (>=0), that should be handled in calling function. + Despite function name, this is used even if the display + connection is parallel. + */ + void SPI_CS_LOW(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *csPortClr = 1; +#else // !KINETISK + *csPortClr = csPinMask; +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + *csPort &= csPinMaskClr; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(_cs, LOW); +#endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the data/command line HIGH (data mode). + */ + void SPI_DC_HIGH(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *dcPortSet = 1; +#else // !KINETISK + *dcPortSet = dcPinMask; +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + *dcPort |= dcPinMaskSet; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(_dc, HIGH); +#endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the data/command line LOW (command mode). + */ + void SPI_DC_LOW(void) { +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if defined(KINETISK) + *dcPortClr = 1; +#else // !KINETISK + *dcPortClr = dcPinMask; +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + *dcPort &= dcPinMaskClr; +#endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(_dc, LOW); +#endif // end !USE_FAST_PINIO + } + +protected: + // A few more low-level member functions -- some may have previously + // been macros. Shouldn't have a need to access these externally, so + // they've been moved to the protected section. Additionally, they're + // declared inline here and the code is in the .cpp file, since outside + // code doesn't need to see these. + inline void SPI_MOSI_HIGH(void); + inline void SPI_MOSI_LOW(void); + inline void SPI_SCK_HIGH(void); + inline void SPI_SCK_LOW(void); + inline bool SPI_MISO_READ(void); + inline void SPI_BEGIN_TRANSACTION(void); + inline void SPI_END_TRANSACTION(void); + inline void TFT_WR_STROBE(void); // Parallel interface write strobe + inline void TFT_RD_HIGH(void); // Parallel interface read high + inline void TFT_RD_LOW(void); // Parallel interface read low + + // CLASS INSTANCE VARIABLES -------------------------------------------- + + // Here be dragons! There's a big union of three structures here -- + // one each for hardware SPI, software (bitbang) SPI, and parallel + // interfaces. This is to save some memory, since a display's connection + // will be only one of these. The order of some things is a little weird + // in an attempt to get values to align and pack better in RAM. + +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + PORTreg_t csPortSet; ///< PORT register for chip select SET + PORTreg_t csPortClr; ///< PORT register for chip select CLEAR + PORTreg_t dcPortSet; ///< PORT register for data/command SET + PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR +#else // !HAS_PORT_SET_CLR + PORTreg_t csPort; ///< PORT register for chip select + PORTreg_t dcPort; ///< PORT register for data/command +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +#if defined(__cplusplus) && (__cplusplus >= 201100) + union { +#endif + struct { // Values specific to HARDWARE SPI: + SPIClass *_spi; ///< SPI class pointer +#if defined(SPI_HAS_TRANSACTION) + SPISettings settings; ///< SPI transaction settings +#else + uint32_t _freq; ///< SPI bitrate (if no SPI transactions) +#endif + uint32_t _mode; ///< SPI data mode (transactions or no) + } hwspi; ///< Hardware SPI values + struct { // Values specific to SOFTWARE SPI: +#if defined(USE_FAST_PINIO) + PORTreg_t misoPort; ///< PORT (PIN) register for MISO +#if defined(HAS_PORT_SET_CLR) + PORTreg_t mosiPortSet; ///< PORT register for MOSI SET + PORTreg_t mosiPortClr; ///< PORT register for MOSI CLEAR + PORTreg_t sckPortSet; ///< PORT register for SCK SET + PORTreg_t sckPortClr; ///< PORT register for SCK CLEAR +#if !defined(KINETISK) + ADAGFX_PORT_t mosiPinMask; ///< Bitmask for MOSI + ADAGFX_PORT_t sckPinMask; ///< Bitmask for SCK +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + PORTreg_t mosiPort; ///< PORT register for MOSI + PORTreg_t sckPort; ///< PORT register for SCK + ADAGFX_PORT_t mosiPinMaskSet; ///< Bitmask for MOSI SET (OR) + ADAGFX_PORT_t mosiPinMaskClr; ///< Bitmask for MOSI CLEAR (AND) + ADAGFX_PORT_t sckPinMaskSet; ///< Bitmask for SCK SET (OR bitmask) + ADAGFX_PORT_t sckPinMaskClr; ///< Bitmask for SCK CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#if !defined(KINETISK) + ADAGFX_PORT_t misoPinMask; ///< Bitmask for MISO +#endif // end !KINETISK +#endif // end USE_FAST_PINIO + int8_t _mosi; ///< MOSI pin # + int8_t _miso; ///< MISO pin # + int8_t _sck; ///< SCK pin # + } swspi; ///< Software SPI values + struct { // Values specific to 8-bit parallel: +#if defined(USE_FAST_PINIO) + +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + volatile uint32_t *writePort; ///< PORT register for DATA WRITE + volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ +#else + volatile uint8_t *writePort; ///< PORT register for DATA WRITE + volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ +#endif +#if defined(HAS_PORT_SET_CLR) + // Port direction register pointers are always 8-bit regardless of + // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits. +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + volatile uint32_t *dirSet; ///< PORT byte data direction SET + volatile uint32_t *dirClr; ///< PORT byte data direction CLEAR +#else + volatile uint8_t *dirSet; ///< PORT byte data direction SET + volatile uint8_t *dirClr; ///< PORT byte data direction CLEAR +#endif + PORTreg_t wrPortSet; ///< PORT register for write strobe SET + PORTreg_t wrPortClr; ///< PORT register for write strobe CLEAR + PORTreg_t rdPortSet; ///< PORT register for read strobe SET + PORTreg_t rdPortClr; ///< PORT register for read strobe CLEAR +#if !defined(KINETISK) + ADAGFX_PORT_t wrPinMask; ///< Bitmask for write strobe +#endif // end !KINETISK + ADAGFX_PORT_t rdPinMask; ///< Bitmask for read strobe +#else // !HAS_PORT_SET_CLR + // Port direction register pointer is always 8-bit regardless of + // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits. + volatile uint8_t *portDir; ///< PORT direction register + PORTreg_t wrPort; ///< PORT register for write strobe + PORTreg_t rdPort; ///< PORT register for read strobe + ADAGFX_PORT_t wrPinMaskSet; ///< Bitmask for write strobe SET (OR) + ADAGFX_PORT_t wrPinMaskClr; ///< Bitmask for write strobe CLEAR (AND) + ADAGFX_PORT_t rdPinMaskSet; ///< Bitmask for read strobe SET (OR) + ADAGFX_PORT_t rdPinMaskClr; ///< Bitmask for read strobe CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + int8_t _d0; ///< Data pin 0 # + int8_t _wr; ///< Write strobe pin # + int8_t _rd; ///< Read strobe pin # (or -1) + bool wide = 0; ///< If true, is 16-bit interface + } tft8; ///< Parallel interface settings +#if defined(__cplusplus) && (__cplusplus >= 201100) + }; ///< Only one interface is active +#endif +#if defined(USE_SPI_DMA) && \ + (defined(__SAMD51__) || \ + defined(ARDUINO_SAMD_ZERO)) // Used by hardware SPI and tft8 + Adafruit_ZeroDMA dma; ///< DMA instance + DmacDescriptor *dptr = NULL; ///< 1st descriptor + DmacDescriptor *descriptor = NULL; ///< Allocated descriptor list + uint16_t *pixelBuf[2]; ///< Working buffers + uint16_t maxFillLen; ///< Max pixels per DMA xfer + uint16_t lastFillColor = 0; ///< Last color used w/fill + uint32_t lastFillLen = 0; ///< # of pixels w/last fill + uint8_t onePixelBuf; ///< For hi==lo fill +#endif +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) +#if !defined(KINETISK) + ADAGFX_PORT_t csPinMask; ///< Bitmask for chip select + ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command +#endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR) + ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND) + ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR) + ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc. + int8_t _rst; ///< Reset pin # (or -1) + int8_t _cs; ///< Chip select pin # (or -1) + int8_t _dc; ///< Data/command pin # + + int16_t _xstart = 0; ///< Internal framebuffer X offset + int16_t _ystart = 0; ///< Internal framebuffer Y offset + uint8_t invertOnCommand = 0; ///< Command to enable invert mode + uint8_t invertOffCommand = 0; ///< Command to disable invert mode + + uint32_t _freq = 0; ///< Dummy var to keep subclasses happy +}; + +#endif // end __AVR_ATtiny85__ +#endif // end _ADAFRUIT_SPITFT_H_ diff --git a/libraries/Adafruit_GFX_Library/Adafruit_SPITFT_Macros.h b/libraries/Adafruit_GFX_Library/Adafruit_SPITFT_Macros.h new file mode 100644 index 0000000..fcd6253 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Adafruit_SPITFT_Macros.h @@ -0,0 +1,6 @@ +// THIS FILE INTENTIONALLY LEFT BLANK. + +// Macros previously #defined here have been made into (mostly) inline +// functions in the Adafruit_SPITFT class. Other libraries might still +// contain code trying to #include this header file, so until everything's +// updated this file still exists (but doing nothing) to avoid trouble. diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMono12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMono12pt7b.h new file mode 100644 index 0000000..effbf4d --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMono12pt7b.h @@ -0,0 +1,226 @@ +const uint8_t FreeMono12pt7bBitmaps[] PROGMEM = { + 0x49, 0x24, 0x92, 0x48, 0x01, 0xF8, 0xE7, 0xE7, 0x67, 0x42, 0x42, 0x42, + 0x42, 0x09, 0x02, 0x41, 0x10, 0x44, 0x11, 0x1F, 0xF1, 0x10, 0x4C, 0x12, + 0x3F, 0xE1, 0x20, 0x48, 0x12, 0x04, 0x81, 0x20, 0x48, 0x04, 0x07, 0xA2, + 0x19, 0x02, 0x40, 0x10, 0x03, 0x00, 0x3C, 0x00, 0x80, 0x10, 0x06, 0x01, + 0xE0, 0xA7, 0xC0, 0x40, 0x10, 0x04, 0x00, 0x3C, 0x19, 0x84, 0x21, 0x08, + 0x66, 0x0F, 0x00, 0x0C, 0x1C, 0x78, 0x01, 0xE0, 0xCC, 0x21, 0x08, 0x43, + 0x30, 0x78, 0x3E, 0x30, 0x10, 0x08, 0x02, 0x03, 0x03, 0x47, 0x14, 0x8A, + 0x43, 0x11, 0x8F, 0x60, 0xFD, 0xA4, 0x90, 0x05, 0x25, 0x24, 0x92, 0x48, + 0x92, 0x24, 0x11, 0x24, 0x89, 0x24, 0x92, 0x92, 0x90, 0x00, 0x04, 0x02, + 0x11, 0x07, 0xF0, 0xC0, 0x50, 0x48, 0x42, 0x00, 0x08, 0x04, 0x02, 0x01, + 0x00, 0x87, 0xFC, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x3B, 0x9C, 0xCE, + 0x62, 0x00, 0xFF, 0xE0, 0xFF, 0x80, 0x00, 0x80, 0xC0, 0x40, 0x20, 0x20, + 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01, 0x00, 0x80, + 0x80, 0x40, 0x00, 0x1C, 0x31, 0x90, 0x58, 0x38, 0x0C, 0x06, 0x03, 0x01, + 0x80, 0xC0, 0x60, 0x30, 0x34, 0x13, 0x18, 0x70, 0x30, 0xE1, 0x44, 0x81, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x1F, 0xC0, 0x1E, 0x10, 0x90, + 0x68, 0x10, 0x08, 0x0C, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x0E, + 0x07, 0xFE, 0x3E, 0x10, 0x40, 0x08, 0x02, 0x00, 0x80, 0x40, 0xE0, 0x04, + 0x00, 0x80, 0x10, 0x04, 0x01, 0x00, 0xD8, 0x63, 0xE0, 0x06, 0x0A, 0x0A, + 0x12, 0x22, 0x22, 0x42, 0x42, 0x82, 0x82, 0xFF, 0x02, 0x02, 0x02, 0x0F, + 0x7F, 0x20, 0x10, 0x08, 0x04, 0x02, 0xF1, 0x8C, 0x03, 0x00, 0x80, 0x40, + 0x20, 0x18, 0x16, 0x18, 0xF0, 0x0F, 0x8C, 0x08, 0x08, 0x04, 0x04, 0x02, + 0x79, 0x46, 0xC1, 0xE0, 0x60, 0x28, 0x14, 0x19, 0x08, 0x78, 0xFF, 0x81, + 0x81, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, + 0x08, 0x3E, 0x31, 0xB0, 0x70, 0x18, 0x0C, 0x05, 0x8C, 0x38, 0x63, 0x40, + 0x60, 0x30, 0x18, 0x1B, 0x18, 0xF8, 0x3C, 0x31, 0x30, 0x50, 0x28, 0x0C, + 0x0F, 0x06, 0x85, 0x3C, 0x80, 0x40, 0x40, 0x20, 0x20, 0x63, 0xE0, 0xFF, + 0x80, 0x07, 0xFC, 0x39, 0xCE, 0x00, 0x00, 0x06, 0x33, 0x98, 0xC4, 0x00, + 0x00, 0xC0, 0x60, 0x18, 0x0C, 0x06, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, + 0x00, 0x30, 0x01, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x06, + 0x00, 0x30, 0x01, 0x80, 0x18, 0x01, 0x80, 0xC0, 0x30, 0x18, 0x0C, 0x02, + 0x00, 0x00, 0x3E, 0x60, 0xA0, 0x20, 0x10, 0x08, 0x08, 0x18, 0x10, 0x08, + 0x00, 0x00, 0x00, 0x01, 0xC0, 0xE0, 0x1C, 0x31, 0x10, 0x50, 0x28, 0x14, + 0x3A, 0x25, 0x22, 0x91, 0x4C, 0xA3, 0xF0, 0x08, 0x02, 0x01, 0x80, 0x7C, + 0x3F, 0x00, 0x0C, 0x00, 0x48, 0x01, 0x20, 0x04, 0x40, 0x21, 0x00, 0x84, + 0x04, 0x08, 0x1F, 0xE0, 0x40, 0x82, 0x01, 0x08, 0x04, 0x20, 0x13, 0xE1, + 0xF0, 0xFF, 0x08, 0x11, 0x01, 0x20, 0x24, 0x04, 0x81, 0x1F, 0xC2, 0x06, + 0x40, 0x68, 0x05, 0x00, 0xA0, 0x14, 0x05, 0xFF, 0x00, 0x1E, 0x48, 0x74, + 0x05, 0x01, 0x80, 0x20, 0x08, 0x02, 0x00, 0x80, 0x20, 0x04, 0x01, 0x01, + 0x30, 0x87, 0xC0, 0xFE, 0x10, 0x44, 0x09, 0x02, 0x40, 0x50, 0x14, 0x05, + 0x01, 0x40, 0x50, 0x14, 0x0D, 0x02, 0x41, 0x3F, 0x80, 0xFF, 0xC8, 0x09, + 0x01, 0x20, 0x04, 0x00, 0x88, 0x1F, 0x02, 0x20, 0x40, 0x08, 0x01, 0x00, + 0xA0, 0x14, 0x03, 0xFF, 0xC0, 0xFF, 0xE8, 0x05, 0x00, 0xA0, 0x04, 0x00, + 0x88, 0x1F, 0x02, 0x20, 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x01, 0xF0, + 0x00, 0x1F, 0x46, 0x19, 0x01, 0x60, 0x28, 0x01, 0x00, 0x20, 0x04, 0x00, + 0x83, 0xF0, 0x0B, 0x01, 0x20, 0x23, 0x0C, 0x3E, 0x00, 0xE1, 0xD0, 0x24, + 0x09, 0x02, 0x40, 0x90, 0x27, 0xF9, 0x02, 0x40, 0x90, 0x24, 0x09, 0x02, + 0x40, 0xB8, 0x70, 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x81, 0x1F, 0xC0, 0x0F, 0xE0, 0x10, 0x02, 0x00, 0x40, 0x08, 0x01, + 0x00, 0x20, 0x04, 0x80, 0x90, 0x12, 0x02, 0x40, 0xC6, 0x30, 0x7C, 0x00, + 0xF1, 0xE4, 0x0C, 0x41, 0x04, 0x20, 0x44, 0x04, 0x80, 0x5C, 0x06, 0x60, + 0x43, 0x04, 0x10, 0x40, 0x84, 0x08, 0x40, 0xCF, 0x07, 0xF8, 0x04, 0x00, + 0x80, 0x10, 0x02, 0x00, 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x04, 0x80, + 0x90, 0x12, 0x03, 0xFF, 0xC0, 0xE0, 0x3B, 0x01, 0x94, 0x14, 0xA0, 0xA4, + 0x89, 0x24, 0x49, 0x14, 0x48, 0xA2, 0x45, 0x12, 0x10, 0x90, 0x04, 0x80, + 0x24, 0x01, 0x78, 0x3C, 0xE0, 0xF6, 0x02, 0x50, 0x25, 0x02, 0x48, 0x24, + 0xC2, 0x44, 0x24, 0x22, 0x43, 0x24, 0x12, 0x40, 0xA4, 0x0A, 0x40, 0x6F, + 0x06, 0x0F, 0x03, 0x0C, 0x60, 0x64, 0x02, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x01, 0x40, 0x26, 0x06, 0x30, 0xC0, 0xF0, 0xFF, 0x10, + 0x64, 0x05, 0x01, 0x40, 0x50, 0x34, 0x19, 0xFC, 0x40, 0x10, 0x04, 0x01, + 0x00, 0x40, 0x3E, 0x00, 0x0F, 0x03, 0x0C, 0x60, 0x64, 0x02, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x40, 0x26, 0x06, 0x30, 0xC1, + 0xF0, 0x0C, 0x01, 0xF1, 0x30, 0xE0, 0xFF, 0x04, 0x18, 0x40, 0xC4, 0x04, + 0x40, 0x44, 0x0C, 0x41, 0x87, 0xE0, 0x43, 0x04, 0x10, 0x40, 0x84, 0x04, + 0x40, 0x4F, 0x03, 0x1F, 0x48, 0x34, 0x05, 0x01, 0x40, 0x08, 0x01, 0xC0, + 0x0E, 0x00, 0x40, 0x18, 0x06, 0x01, 0xE1, 0xA7, 0xC0, 0xFF, 0xF0, 0x86, + 0x10, 0x82, 0x00, 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x80, 0x10, + 0x02, 0x00, 0x40, 0x7F, 0x00, 0xF0, 0xF4, 0x02, 0x40, 0x24, 0x02, 0x40, + 0x24, 0x02, 0x40, 0x24, 0x02, 0x40, 0x24, 0x02, 0x40, 0x22, 0x04, 0x30, + 0xC0, 0xF0, 0xF8, 0x7C, 0x80, 0x22, 0x01, 0x04, 0x04, 0x10, 0x20, 0x40, + 0x80, 0x82, 0x02, 0x10, 0x08, 0x40, 0x11, 0x00, 0x48, 0x01, 0xA0, 0x03, + 0x00, 0x0C, 0x00, 0xF8, 0x7C, 0x80, 0x22, 0x00, 0x88, 0xC2, 0x23, 0x10, + 0x8E, 0x42, 0x29, 0x09, 0x24, 0x24, 0x90, 0x91, 0x41, 0x85, 0x06, 0x14, + 0x18, 0x70, 0x60, 0x80, 0xF0, 0xF2, 0x06, 0x30, 0x41, 0x08, 0x09, 0x80, + 0x50, 0x06, 0x00, 0x60, 0x0D, 0x00, 0x88, 0x10, 0xC2, 0x04, 0x60, 0x2F, + 0x0F, 0xF0, 0xF2, 0x02, 0x10, 0x41, 0x04, 0x08, 0x80, 0x50, 0x05, 0x00, + 0x20, 0x02, 0x00, 0x20, 0x02, 0x00, 0x20, 0x02, 0x01, 0xFC, 0xFF, 0x40, + 0xA0, 0x90, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x10, 0x50, 0x30, 0x18, + 0x0F, 0xFC, 0xF2, 0x49, 0x24, 0x92, 0x49, 0x24, 0x9C, 0x80, 0x60, 0x10, + 0x08, 0x02, 0x01, 0x00, 0x40, 0x20, 0x08, 0x04, 0x01, 0x00, 0x80, 0x20, + 0x10, 0x04, 0x02, 0x00, 0x80, 0x40, 0xE4, 0x92, 0x49, 0x24, 0x92, 0x49, + 0x3C, 0x08, 0x0C, 0x09, 0x0C, 0x4C, 0x14, 0x04, 0xFF, 0xFC, 0x84, 0x21, + 0x3E, 0x00, 0x60, 0x08, 0x02, 0x3F, 0x98, 0x28, 0x0A, 0x02, 0xC3, 0x9F, + 0x30, 0xE0, 0x01, 0x00, 0x08, 0x00, 0x40, 0x02, 0x00, 0x13, 0xE0, 0xA0, + 0x86, 0x02, 0x20, 0x09, 0x00, 0x48, 0x02, 0x40, 0x13, 0x01, 0x14, 0x1B, + 0x9F, 0x00, 0x1F, 0x4C, 0x19, 0x01, 0x40, 0x28, 0x01, 0x00, 0x20, 0x02, + 0x00, 0x60, 0x43, 0xF0, 0x00, 0xC0, 0x08, 0x01, 0x00, 0x20, 0x04, 0x3C, + 0x98, 0x52, 0x06, 0x80, 0x50, 0x0A, 0x01, 0x40, 0x24, 0x0C, 0xC2, 0x87, + 0x98, 0x3F, 0x18, 0x68, 0x06, 0x01, 0xFF, 0xE0, 0x08, 0x03, 0x00, 0x60, + 0xC7, 0xC0, 0x0F, 0x98, 0x08, 0x04, 0x02, 0x07, 0xF8, 0x80, 0x40, 0x20, + 0x10, 0x08, 0x04, 0x02, 0x01, 0x03, 0xF8, 0x1E, 0x6C, 0x39, 0x03, 0x40, + 0x28, 0x05, 0x00, 0xA0, 0x12, 0x06, 0x61, 0x43, 0xC8, 0x01, 0x00, 0x20, + 0x08, 0x3E, 0x00, 0xC0, 0x10, 0x04, 0x01, 0x00, 0x40, 0x13, 0x87, 0x11, + 0x82, 0x40, 0x90, 0x24, 0x09, 0x02, 0x40, 0x90, 0x2E, 0x1C, 0x08, 0x04, + 0x02, 0x00, 0x00, 0x03, 0xC0, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, + 0x80, 0x43, 0xFE, 0x04, 0x08, 0x10, 0x00, 0x1F, 0xC0, 0x81, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x0B, 0xE0, 0xE0, 0x02, 0x00, 0x20, + 0x02, 0x00, 0x20, 0x02, 0x3C, 0x21, 0x02, 0x60, 0x2C, 0x03, 0x80, 0x24, + 0x02, 0x20, 0x21, 0x02, 0x08, 0xE1, 0xF0, 0x78, 0x04, 0x02, 0x01, 0x00, + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x80, 0x43, 0xFE, + 0xDC, 0xE3, 0x19, 0x90, 0x84, 0x84, 0x24, 0x21, 0x21, 0x09, 0x08, 0x48, + 0x42, 0x42, 0x17, 0x18, 0xC0, 0x67, 0x83, 0x84, 0x20, 0x22, 0x02, 0x20, + 0x22, 0x02, 0x20, 0x22, 0x02, 0x20, 0x2F, 0x07, 0x1F, 0x04, 0x11, 0x01, + 0x40, 0x18, 0x03, 0x00, 0x60, 0x0A, 0x02, 0x20, 0x83, 0xE0, 0xCF, 0x85, + 0x06, 0x60, 0x24, 0x01, 0x40, 0x14, 0x01, 0x40, 0x16, 0x02, 0x50, 0x44, + 0xF8, 0x40, 0x04, 0x00, 0x40, 0x0F, 0x00, 0x1E, 0x6C, 0x3B, 0x03, 0x40, + 0x28, 0x05, 0x00, 0xA0, 0x12, 0x06, 0x61, 0x43, 0xC8, 0x01, 0x00, 0x20, + 0x04, 0x03, 0xC0, 0xE3, 0x8B, 0x13, 0x80, 0x80, 0x20, 0x08, 0x02, 0x00, + 0x80, 0x20, 0x3F, 0x80, 0x1F, 0x58, 0x34, 0x05, 0x80, 0x1E, 0x00, 0x60, + 0x06, 0x01, 0xC0, 0xAF, 0xC0, 0x20, 0x04, 0x00, 0x80, 0x10, 0x0F, 0xF0, + 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x80, 0x10, 0x03, 0x04, 0x3F, + 0x00, 0xC1, 0xC8, 0x09, 0x01, 0x20, 0x24, 0x04, 0x80, 0x90, 0x12, 0x02, + 0x61, 0xC7, 0xCC, 0xF8, 0xF9, 0x01, 0x08, 0x10, 0x60, 0x81, 0x08, 0x08, + 0x40, 0x22, 0x01, 0x20, 0x05, 0x00, 0x30, 0x00, 0xF0, 0x7A, 0x01, 0x10, + 0x08, 0x8C, 0x42, 0x62, 0x12, 0x90, 0xA5, 0x05, 0x18, 0x28, 0xC0, 0x86, + 0x00, 0x78, 0xF3, 0x04, 0x18, 0x80, 0xD0, 0x06, 0x00, 0x70, 0x09, 0x81, + 0x0C, 0x20, 0x6F, 0x8F, 0xF0, 0xF2, 0x02, 0x20, 0x41, 0x04, 0x10, 0x80, + 0x88, 0x09, 0x00, 0x50, 0x06, 0x00, 0x20, 0x04, 0x00, 0x40, 0x08, 0x0F, + 0xE0, 0xFF, 0x41, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0xBF, + 0xC0, 0x19, 0x08, 0x42, 0x10, 0x84, 0x64, 0x18, 0x42, 0x10, 0x84, 0x20, + 0xC0, 0xFF, 0xFF, 0xC0, 0xC1, 0x08, 0x42, 0x10, 0x84, 0x10, 0x4C, 0x42, + 0x10, 0x84, 0x26, 0x00, 0x38, 0x13, 0x38, 0x38}; + +const GFXglyph FreeMono12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 14, 0, 1}, // 0x20 ' ' + {0, 3, 15, 14, 6, -14}, // 0x21 '!' + {6, 8, 7, 14, 3, -14}, // 0x22 '"' + {13, 10, 16, 14, 2, -14}, // 0x23 '#' + {33, 10, 17, 14, 2, -14}, // 0x24 '$' + {55, 10, 15, 14, 2, -14}, // 0x25 '%' + {74, 9, 12, 14, 3, -11}, // 0x26 '&' + {88, 3, 7, 14, 5, -14}, // 0x27 ''' + {91, 3, 18, 14, 7, -14}, // 0x28 '(' + {98, 3, 18, 14, 4, -14}, // 0x29 ')' + {105, 9, 9, 14, 3, -14}, // 0x2A '*' + {116, 9, 11, 14, 3, -11}, // 0x2B '+' + {129, 5, 7, 14, 3, -3}, // 0x2C ',' + {134, 11, 1, 14, 2, -6}, // 0x2D '-' + {136, 3, 3, 14, 5, -2}, // 0x2E '.' + {138, 9, 18, 14, 3, -15}, // 0x2F '/' + {159, 9, 15, 14, 3, -14}, // 0x30 '0' + {176, 7, 14, 14, 4, -13}, // 0x31 '1' + {189, 9, 15, 14, 2, -14}, // 0x32 '2' + {206, 10, 15, 14, 2, -14}, // 0x33 '3' + {225, 8, 15, 14, 3, -14}, // 0x34 '4' + {240, 9, 15, 14, 3, -14}, // 0x35 '5' + {257, 9, 15, 14, 3, -14}, // 0x36 '6' + {274, 8, 15, 14, 3, -14}, // 0x37 '7' + {289, 9, 15, 14, 3, -14}, // 0x38 '8' + {306, 9, 15, 14, 3, -14}, // 0x39 '9' + {323, 3, 10, 14, 5, -9}, // 0x3A ':' + {327, 5, 13, 14, 3, -9}, // 0x3B ';' + {336, 11, 11, 14, 2, -11}, // 0x3C '<' + {352, 12, 4, 14, 1, -8}, // 0x3D '=' + {358, 11, 11, 14, 2, -11}, // 0x3E '>' + {374, 9, 14, 14, 3, -13}, // 0x3F '?' + {390, 9, 16, 14, 3, -14}, // 0x40 '@' + {408, 14, 14, 14, 0, -13}, // 0x41 'A' + {433, 11, 14, 14, 2, -13}, // 0x42 'B' + {453, 10, 14, 14, 2, -13}, // 0x43 'C' + {471, 10, 14, 14, 2, -13}, // 0x44 'D' + {489, 11, 14, 14, 2, -13}, // 0x45 'E' + {509, 11, 14, 14, 2, -13}, // 0x46 'F' + {529, 11, 14, 14, 2, -13}, // 0x47 'G' + {549, 10, 14, 14, 2, -13}, // 0x48 'H' + {567, 7, 14, 14, 4, -13}, // 0x49 'I' + {580, 11, 14, 14, 2, -13}, // 0x4A 'J' + {600, 12, 14, 14, 2, -13}, // 0x4B 'K' + {621, 11, 14, 14, 2, -13}, // 0x4C 'L' + {641, 13, 14, 14, 1, -13}, // 0x4D 'M' + {664, 12, 14, 14, 1, -13}, // 0x4E 'N' + {685, 12, 14, 14, 1, -13}, // 0x4F 'O' + {706, 10, 14, 14, 2, -13}, // 0x50 'P' + {724, 12, 17, 14, 1, -13}, // 0x51 'Q' + {750, 12, 14, 14, 2, -13}, // 0x52 'R' + {771, 10, 14, 14, 2, -13}, // 0x53 'S' + {789, 11, 14, 14, 2, -13}, // 0x54 'T' + {809, 12, 14, 14, 1, -13}, // 0x55 'U' + {830, 14, 14, 14, 0, -13}, // 0x56 'V' + {855, 14, 14, 14, 0, -13}, // 0x57 'W' + {880, 12, 14, 14, 1, -13}, // 0x58 'X' + {901, 12, 14, 14, 1, -13}, // 0x59 'Y' + {922, 9, 14, 14, 3, -13}, // 0x5A 'Z' + {938, 3, 18, 14, 7, -14}, // 0x5B '[' + {945, 9, 18, 14, 3, -15}, // 0x5C '\' + {966, 3, 18, 14, 5, -14}, // 0x5D ']' + {973, 9, 6, 14, 3, -14}, // 0x5E '^' + {980, 14, 1, 14, 0, 3}, // 0x5F '_' + {982, 4, 4, 14, 4, -15}, // 0x60 '`' + {984, 10, 10, 14, 2, -9}, // 0x61 'a' + {997, 13, 15, 14, 0, -14}, // 0x62 'b' + {1022, 11, 10, 14, 2, -9}, // 0x63 'c' + {1036, 11, 15, 14, 2, -14}, // 0x64 'd' + {1057, 10, 10, 14, 2, -9}, // 0x65 'e' + {1070, 9, 15, 14, 4, -14}, // 0x66 'f' + {1087, 11, 14, 14, 2, -9}, // 0x67 'g' + {1107, 10, 15, 14, 2, -14}, // 0x68 'h' + {1126, 9, 15, 14, 3, -14}, // 0x69 'i' + {1143, 7, 19, 14, 3, -14}, // 0x6A 'j' + {1160, 12, 15, 14, 1, -14}, // 0x6B 'k' + {1183, 9, 15, 14, 3, -14}, // 0x6C 'l' + {1200, 13, 10, 14, 1, -9}, // 0x6D 'm' + {1217, 12, 10, 14, 1, -9}, // 0x6E 'n' + {1232, 11, 10, 14, 2, -9}, // 0x6F 'o' + {1246, 12, 14, 14, 1, -9}, // 0x70 'p' + {1267, 11, 14, 14, 2, -9}, // 0x71 'q' + {1287, 10, 10, 14, 3, -9}, // 0x72 'r' + {1300, 10, 10, 14, 2, -9}, // 0x73 's' + {1313, 11, 14, 14, 1, -13}, // 0x74 't' + {1333, 11, 10, 14, 2, -9}, // 0x75 'u' + {1347, 13, 10, 14, 1, -9}, // 0x76 'v' + {1364, 13, 10, 14, 1, -9}, // 0x77 'w' + {1381, 12, 10, 14, 1, -9}, // 0x78 'x' + {1396, 12, 14, 14, 1, -9}, // 0x79 'y' + {1417, 9, 10, 14, 3, -9}, // 0x7A 'z' + {1429, 5, 18, 14, 5, -14}, // 0x7B '{' + {1441, 1, 18, 14, 7, -14}, // 0x7C '|' + {1444, 5, 18, 14, 5, -14}, // 0x7D '}' + {1456, 10, 3, 14, 2, -7}}; // 0x7E '~' + +const GFXfont FreeMono12pt7b PROGMEM = {(uint8_t *)FreeMono12pt7bBitmaps, + (GFXglyph *)FreeMono12pt7bGlyphs, 0x20, + 0x7E, 24}; + +// Approx. 2132 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMono18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMono18pt7b.h new file mode 100644 index 0000000..2361c98 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMono18pt7b.h @@ -0,0 +1,362 @@ +const uint8_t FreeMono18pt7bBitmaps[] PROGMEM = { + 0x27, 0x77, 0x77, 0x77, 0x77, 0x22, 0x22, 0x20, 0x00, 0x6F, 0xF6, 0xF1, + 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1E, 0xC3, 0x98, 0x33, 0x06, 0x60, 0xCC, + 0x18, 0x04, 0x20, 0x10, 0x80, 0x42, 0x01, 0x08, 0x04, 0x20, 0x10, 0x80, + 0x42, 0x01, 0x10, 0x04, 0x41, 0xFF, 0xF0, 0x44, 0x02, 0x10, 0x08, 0x40, + 0x21, 0x0F, 0xFF, 0xC2, 0x10, 0x08, 0x40, 0x21, 0x00, 0x84, 0x02, 0x10, + 0x08, 0x40, 0x23, 0x00, 0x88, 0x02, 0x20, 0x02, 0x00, 0x10, 0x00, 0x80, + 0x1F, 0xA3, 0x07, 0x10, 0x09, 0x00, 0x48, 0x00, 0x40, 0x03, 0x00, 0x0C, + 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x18, 0x00, 0x20, 0x01, 0x80, 0x0C, 0x00, + 0x70, 0x05, 0xE0, 0xC9, 0xF8, 0x01, 0x00, 0x08, 0x00, 0x40, 0x02, 0x00, + 0x10, 0x00, 0x1E, 0x00, 0x42, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, + 0x10, 0x08, 0x40, 0x0F, 0x00, 0x00, 0x1E, 0x01, 0xF0, 0x1F, 0x01, 0xE0, + 0x0E, 0x00, 0x00, 0x3C, 0x00, 0x86, 0x02, 0x06, 0x04, 0x04, 0x08, 0x08, + 0x10, 0x30, 0x10, 0xC0, 0x1E, 0x00, 0x0F, 0xC1, 0x00, 0x20, 0x02, 0x00, + 0x20, 0x02, 0x00, 0x10, 0x01, 0x00, 0x08, 0x03, 0xC0, 0x6C, 0x3C, 0x62, + 0x82, 0x68, 0x34, 0x81, 0xCC, 0x08, 0x61, 0xC3, 0xE7, 0xFF, 0xFF, 0xF6, + 0x66, 0x66, 0x08, 0xC4, 0x62, 0x31, 0x8C, 0xC6, 0x31, 0x8C, 0x63, 0x18, + 0xC3, 0x18, 0xC2, 0x18, 0xC3, 0x18, 0x86, 0x10, 0xC2, 0x18, 0xC6, 0x10, + 0xC6, 0x31, 0x8C, 0x63, 0x18, 0x8C, 0x62, 0x31, 0x98, 0x80, 0x02, 0x00, + 0x10, 0x00, 0x80, 0x04, 0x0C, 0x21, 0x9D, 0x70, 0x1C, 0x00, 0xA0, 0x0D, + 0x80, 0xC6, 0x04, 0x10, 0x40, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0xFF, 0xFE, 0x02, + 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, + 0x01, 0x00, 0x3E, 0x78, 0xF3, 0xC7, 0x8E, 0x18, 0x70, 0xC1, 0x80, 0xFF, + 0xFE, 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x08, 0x00, 0xC0, 0x04, 0x00, 0x60, + 0x02, 0x00, 0x30, 0x01, 0x00, 0x18, 0x00, 0x80, 0x0C, 0x00, 0x40, 0x02, + 0x00, 0x20, 0x01, 0x00, 0x10, 0x00, 0x80, 0x08, 0x00, 0x40, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x10, 0x01, 0x00, 0x08, 0x00, 0x80, 0x04, 0x00, 0x00, + 0x0F, 0x81, 0x82, 0x08, 0x08, 0x80, 0x24, 0x01, 0x60, 0x0E, 0x00, 0x30, + 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x06, 0x00, + 0x30, 0x03, 0x40, 0x12, 0x00, 0x88, 0x08, 0x60, 0xC0, 0xF8, 0x00, 0x06, + 0x00, 0x70, 0x06, 0x80, 0x64, 0x06, 0x20, 0x31, 0x00, 0x08, 0x00, 0x40, + 0x02, 0x00, 0x10, 0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08, 0x00, + 0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x04, 0x0F, 0xFF, 0x80, 0x0F, 0x80, + 0xC3, 0x08, 0x04, 0x80, 0x24, 0x00, 0x80, 0x04, 0x00, 0x20, 0x02, 0x00, + 0x10, 0x01, 0x00, 0x10, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x58, 0x03, 0x80, 0x1F, 0xFF, 0x80, 0x0F, 0xC0, 0xC0, + 0x86, 0x01, 0x00, 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x04, 0x00, + 0x20, 0x0F, 0x00, 0x06, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x40, + 0x01, 0x00, 0x04, 0x00, 0x2C, 0x01, 0x9C, 0x0C, 0x0F, 0xC0, 0x01, 0xC0, + 0x14, 0x02, 0x40, 0x64, 0x04, 0x40, 0xC4, 0x08, 0x41, 0x84, 0x10, 0x42, + 0x04, 0x20, 0x44, 0x04, 0x40, 0x48, 0x04, 0xFF, 0xF0, 0x04, 0x00, 0x40, + 0x04, 0x00, 0x40, 0x04, 0x07, 0xF0, 0x3F, 0xF0, 0x80, 0x02, 0x00, 0x08, + 0x00, 0x20, 0x00, 0x80, 0x02, 0x00, 0x0B, 0xF0, 0x30, 0x30, 0x00, 0x60, + 0x00, 0x80, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00, 0x40, 0x01, 0x00, 0x0E, + 0x00, 0x2C, 0x01, 0x0C, 0x18, 0x0F, 0xC0, 0x01, 0xF0, 0x60, 0x18, 0x03, + 0x00, 0x20, 0x04, 0x00, 0x40, 0x0C, 0x00, 0x80, 0x08, 0xF8, 0x98, 0x4A, + 0x02, 0xE0, 0x3C, 0x01, 0x80, 0x14, 0x01, 0x40, 0x14, 0x03, 0x20, 0x21, + 0x0C, 0x0F, 0x80, 0xFF, 0xF8, 0x01, 0x80, 0x18, 0x03, 0x00, 0x20, 0x02, + 0x00, 0x20, 0x04, 0x00, 0x40, 0x04, 0x00, 0xC0, 0x08, 0x00, 0x80, 0x18, + 0x01, 0x00, 0x10, 0x01, 0x00, 0x30, 0x02, 0x00, 0x20, 0x02, 0x00, 0x0F, + 0x81, 0x83, 0x10, 0x05, 0x80, 0x38, 0x00, 0xC0, 0x06, 0x00, 0x30, 0x03, + 0x40, 0x11, 0x83, 0x07, 0xF0, 0x60, 0xC4, 0x01, 0x60, 0x0E, 0x00, 0x30, + 0x01, 0x80, 0x0E, 0x00, 0xD0, 0x04, 0x60, 0xC1, 0xFC, 0x00, 0x1F, 0x03, + 0x08, 0x40, 0x4C, 0x02, 0x80, 0x28, 0x02, 0x80, 0x18, 0x03, 0xC0, 0x74, + 0x05, 0x21, 0x91, 0xF1, 0x00, 0x10, 0x03, 0x00, 0x20, 0x02, 0x00, 0x40, + 0x0C, 0x01, 0x80, 0x60, 0xF8, 0x00, 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x00, + 0x1D, 0xFF, 0xFD, 0xC0, 0x1C, 0x7C, 0xF9, 0xF1, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0xF1, 0xE3, 0x8F, 0x1C, 0x38, 0xE1, 0xC3, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x18, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x06, 0x00, 0x38, + 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0E, + 0x00, 0x07, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xFF, 0xFC, 0xC0, 0x00, 0xC0, 0x00, 0xE0, 0x00, 0x70, + 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x70, + 0x03, 0x80, 0x0C, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0x60, 0x00, 0x3F, + 0x8E, 0x0C, 0x80, 0x28, 0x01, 0x80, 0x10, 0x01, 0x00, 0x10, 0x02, 0x00, + 0xC0, 0x38, 0x06, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, + 0x01, 0xF0, 0x1F, 0x00, 0xE0, 0x0F, 0x01, 0x86, 0x08, 0x08, 0x80, 0x24, + 0x01, 0x40, 0x0A, 0x00, 0x50, 0x1E, 0x83, 0x14, 0x20, 0xA2, 0x05, 0x10, + 0x28, 0x81, 0x46, 0x0A, 0x18, 0x50, 0x3F, 0x80, 0x04, 0x00, 0x10, 0x00, + 0x80, 0x02, 0x00, 0x18, 0x18, 0x3F, 0x00, 0x1F, 0xF0, 0x00, 0x06, 0x80, + 0x00, 0x34, 0x00, 0x01, 0x30, 0x00, 0x18, 0x80, 0x00, 0x86, 0x00, 0x04, + 0x30, 0x00, 0x60, 0x80, 0x02, 0x06, 0x00, 0x10, 0x10, 0x01, 0x80, 0x80, + 0x08, 0x06, 0x00, 0x7F, 0xF0, 0x06, 0x00, 0x80, 0x20, 0x06, 0x01, 0x00, + 0x10, 0x18, 0x00, 0xC0, 0x80, 0x06, 0x04, 0x00, 0x11, 0xFC, 0x0F, 0xF0, + 0xFF, 0xF8, 0x04, 0x01, 0x01, 0x00, 0x20, 0x40, 0x04, 0x10, 0x01, 0x04, + 0x00, 0x41, 0x00, 0x10, 0x40, 0x08, 0x10, 0x0C, 0x07, 0xFF, 0x01, 0x00, + 0x70, 0x40, 0x06, 0x10, 0x00, 0x84, 0x00, 0x11, 0x00, 0x04, 0x40, 0x01, + 0x10, 0x00, 0x44, 0x00, 0x21, 0x00, 0x33, 0xFF, 0xF8, 0x03, 0xF1, 0x06, + 0x0E, 0x8C, 0x01, 0xC4, 0x00, 0x64, 0x00, 0x12, 0x00, 0x0A, 0x00, 0x01, + 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x20, 0x01, 0x88, 0x01, 0x83, + 0x03, 0x80, 0x7E, 0x00, 0xFF, 0xE0, 0x20, 0x18, 0x20, 0x0C, 0x20, 0x04, + 0x20, 0x02, 0x20, 0x02, 0x20, 0x01, 0x20, 0x01, 0x20, 0x01, 0x20, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x20, 0x01, 0x20, 0x01, 0x20, 0x02, 0x20, 0x02, + 0x20, 0x04, 0x20, 0x0C, 0x20, 0x18, 0xFF, 0xE0, 0xFF, 0xFF, 0x08, 0x00, + 0x84, 0x00, 0x42, 0x00, 0x21, 0x00, 0x10, 0x80, 0x00, 0x40, 0x00, 0x20, + 0x40, 0x10, 0x20, 0x0F, 0xF0, 0x04, 0x08, 0x02, 0x04, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x40, 0x02, 0x20, 0x01, 0x10, 0x00, 0x88, 0x00, 0x44, 0x00, + 0x3F, 0xFF, 0xF0, 0xFF, 0xFF, 0x88, 0x00, 0x44, 0x00, 0x22, 0x00, 0x11, + 0x00, 0x08, 0x80, 0x00, 0x40, 0x00, 0x20, 0x40, 0x10, 0x20, 0x0F, 0xF0, + 0x04, 0x08, 0x02, 0x04, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x1F, 0xF8, 0x00, 0x03, 0xF9, + 0x06, 0x07, 0x84, 0x00, 0xC4, 0x00, 0x24, 0x00, 0x12, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x0F, 0xF8, + 0x00, 0x14, 0x00, 0x09, 0x00, 0x04, 0x80, 0x02, 0x20, 0x01, 0x18, 0x00, + 0x83, 0x01, 0xC0, 0x7F, 0x00, 0xFC, 0x3F, 0x20, 0x04, 0x20, 0x04, 0x20, + 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x3F, + 0xFC, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, + 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0xFC, 0x3F, 0xFF, 0xF8, 0x10, + 0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08, 0x00, 0x40, 0x02, 0x00, + 0x10, 0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08, 0x00, 0x40, 0x02, + 0x00, 0x10, 0x00, 0x81, 0xFF, 0xF0, 0x03, 0xFF, 0x80, 0x04, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x02, 0x10, 0x01, 0x08, 0x00, 0x84, 0x00, 0x42, + 0x00, 0x21, 0x00, 0x10, 0x80, 0x10, 0x20, 0x18, 0x0C, 0x18, 0x01, 0xF0, + 0x00, 0xFF, 0x1F, 0x84, 0x01, 0x81, 0x00, 0xC0, 0x40, 0x60, 0x10, 0x30, + 0x04, 0x18, 0x01, 0x0C, 0x00, 0x46, 0x00, 0x13, 0x00, 0x05, 0xF0, 0x01, + 0xC6, 0x00, 0x60, 0xC0, 0x10, 0x18, 0x04, 0x06, 0x01, 0x00, 0xC0, 0x40, + 0x30, 0x10, 0x04, 0x04, 0x01, 0x81, 0x00, 0x23, 0xFC, 0x0F, 0xFF, 0x80, + 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, + 0x42, 0x00, 0x84, 0x01, 0x08, 0x02, 0x10, 0x04, 0x20, 0x0F, 0xFF, 0xF0, + 0xF0, 0x01, 0xE7, 0x00, 0x70, 0xA0, 0x0A, 0x16, 0x03, 0x42, 0x40, 0x48, + 0x4C, 0x19, 0x08, 0x82, 0x21, 0x10, 0x44, 0x23, 0x18, 0x84, 0x22, 0x10, + 0x86, 0xC2, 0x10, 0x50, 0x42, 0x0E, 0x08, 0x41, 0xC1, 0x08, 0x00, 0x21, + 0x00, 0x04, 0x20, 0x00, 0x84, 0x00, 0x10, 0x80, 0x02, 0x7F, 0x03, 0xF0, + 0xF8, 0x1F, 0xC6, 0x00, 0x41, 0xC0, 0x10, 0x50, 0x04, 0x12, 0x01, 0x04, + 0xC0, 0x41, 0x10, 0x10, 0x46, 0x04, 0x10, 0x81, 0x04, 0x10, 0x41, 0x04, + 0x10, 0x40, 0x84, 0x10, 0x31, 0x04, 0x04, 0x41, 0x01, 0x90, 0x40, 0x24, + 0x10, 0x05, 0x04, 0x01, 0xC1, 0x00, 0x31, 0xFC, 0x0C, 0x03, 0xE0, 0x06, + 0x0C, 0x04, 0x01, 0x04, 0x00, 0x46, 0x00, 0x32, 0x00, 0x0B, 0x00, 0x05, + 0x00, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x0E, 0x00, 0x0D, 0x00, 0x04, 0xC0, 0x06, 0x20, 0x02, 0x08, 0x02, 0x03, + 0x06, 0x00, 0x7C, 0x00, 0xFF, 0xF0, 0x10, 0x0C, 0x10, 0x02, 0x10, 0x03, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x03, 0x10, 0x06, 0x10, 0x0C, + 0x1F, 0xF0, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xFF, 0xC0, 0x03, 0xE0, 0x06, 0x0C, + 0x04, 0x01, 0x04, 0x00, 0x46, 0x00, 0x32, 0x00, 0x0B, 0x00, 0x07, 0x00, + 0x01, 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0E, + 0x00, 0x0D, 0x00, 0x04, 0xC0, 0x06, 0x20, 0x02, 0x08, 0x02, 0x03, 0x06, + 0x00, 0xFC, 0x00, 0x30, 0x00, 0x30, 0x00, 0x7F, 0xC6, 0x38, 0x1E, 0xFF, + 0xF0, 0x02, 0x01, 0x80, 0x40, 0x08, 0x08, 0x01, 0x81, 0x00, 0x10, 0x20, + 0x02, 0x04, 0x00, 0x40, 0x80, 0x18, 0x10, 0x06, 0x02, 0x03, 0x80, 0x7F, + 0xC0, 0x08, 0x18, 0x01, 0x01, 0x80, 0x20, 0x18, 0x04, 0x01, 0x80, 0x80, + 0x10, 0x10, 0x03, 0x02, 0x00, 0x20, 0x40, 0x06, 0x7F, 0x80, 0x70, 0x0F, + 0xC8, 0x61, 0xE2, 0x01, 0x90, 0x02, 0x40, 0x09, 0x00, 0x04, 0x00, 0x08, + 0x00, 0x38, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, + 0x00, 0x60, 0x01, 0x80, 0x0F, 0x00, 0x2B, 0x03, 0x23, 0xF0, 0xFF, 0xFF, + 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x10, 0x20, 0x20, 0x00, 0x40, 0x00, + 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, + 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xFF, 0xC0, + 0xFC, 0x1F, 0x90, 0x01, 0x08, 0x00, 0x84, 0x00, 0x42, 0x00, 0x21, 0x00, + 0x10, 0x80, 0x08, 0x40, 0x04, 0x20, 0x02, 0x10, 0x01, 0x08, 0x00, 0x84, + 0x00, 0x42, 0x00, 0x21, 0x00, 0x10, 0x80, 0x08, 0x40, 0x04, 0x10, 0x04, + 0x0C, 0x06, 0x03, 0x06, 0x00, 0x7C, 0x00, 0xFE, 0x03, 0xF8, 0x80, 0x02, + 0x04, 0x00, 0x10, 0x30, 0x01, 0x80, 0x80, 0x08, 0x06, 0x00, 0xC0, 0x30, + 0x06, 0x00, 0x80, 0x20, 0x06, 0x03, 0x00, 0x30, 0x10, 0x00, 0x80, 0x80, + 0x06, 0x0C, 0x00, 0x10, 0x40, 0x00, 0x86, 0x00, 0x06, 0x20, 0x00, 0x11, + 0x00, 0x00, 0xD8, 0x00, 0x06, 0x80, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x00, + 0xFC, 0x0F, 0xE8, 0x00, 0x19, 0x00, 0x03, 0x10, 0x00, 0x62, 0x00, 0x08, + 0x41, 0x81, 0x08, 0x28, 0x21, 0x05, 0x04, 0x21, 0xA0, 0x84, 0x36, 0x30, + 0x84, 0x46, 0x08, 0x88, 0xC1, 0x31, 0x18, 0x24, 0x12, 0x04, 0x82, 0x40, + 0xB0, 0x48, 0x14, 0x09, 0x02, 0x80, 0xA0, 0x30, 0x1C, 0x06, 0x03, 0x80, + 0x7E, 0x0F, 0xC2, 0x00, 0x60, 0x60, 0x0C, 0x06, 0x03, 0x00, 0x60, 0xC0, + 0x0C, 0x10, 0x00, 0xC6, 0x00, 0x0D, 0x80, 0x00, 0xA0, 0x00, 0x1C, 0x00, + 0x03, 0x80, 0x00, 0xD8, 0x00, 0x11, 0x00, 0x06, 0x30, 0x01, 0x83, 0x00, + 0x60, 0x30, 0x08, 0x06, 0x03, 0x00, 0x60, 0xC0, 0x06, 0x7F, 0x07, 0xF0, + 0xFC, 0x1F, 0x98, 0x03, 0x04, 0x01, 0x03, 0x01, 0x80, 0xC1, 0x80, 0x20, + 0x80, 0x18, 0xC0, 0x04, 0x40, 0x03, 0x60, 0x00, 0xE0, 0x00, 0x20, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, + 0x00, 0x40, 0x00, 0x20, 0x03, 0xFF, 0x80, 0xFF, 0xF4, 0x00, 0xA0, 0x09, + 0x00, 0x48, 0x04, 0x40, 0x40, 0x02, 0x00, 0x20, 0x02, 0x00, 0x10, 0x01, + 0x00, 0x10, 0x00, 0x80, 0x08, 0x04, 0x80, 0x24, 0x01, 0x40, 0x0C, 0x00, + 0x60, 0x03, 0xFF, 0xF0, 0xFC, 0x21, 0x08, 0x42, 0x10, 0x84, 0x21, 0x08, + 0x42, 0x10, 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8, 0x80, 0x02, 0x00, 0x10, + 0x00, 0xC0, 0x02, 0x00, 0x18, 0x00, 0x40, 0x03, 0x00, 0x08, 0x00, 0x40, + 0x01, 0x00, 0x08, 0x00, 0x20, 0x01, 0x00, 0x04, 0x00, 0x20, 0x00, 0x80, + 0x04, 0x00, 0x10, 0x00, 0x80, 0x02, 0x00, 0x10, 0x00, 0x40, 0x02, 0x00, + 0x08, 0x00, 0x40, 0xF8, 0x42, 0x10, 0x84, 0x21, 0x08, 0x42, 0x10, 0x84, + 0x21, 0x08, 0x42, 0x10, 0x84, 0x21, 0xF8, 0x02, 0x00, 0x38, 0x03, 0x60, + 0x11, 0x01, 0x8C, 0x18, 0x31, 0x80, 0xD8, 0x03, 0x80, 0x08, 0xFF, 0xFF, + 0xF8, 0xC1, 0x83, 0x06, 0x0C, 0x0F, 0xC0, 0x70, 0x30, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x0F, 0xF8, 0x30, 0x08, 0x40, 0x08, 0x80, + 0x08, 0x80, 0x08, 0x80, 0x08, 0x80, 0x38, 0x60, 0xE8, 0x3F, 0x8F, 0xF0, + 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x10, 0x00, 0x04, 0x00, + 0x01, 0x0F, 0x80, 0x4C, 0x18, 0x14, 0x01, 0x06, 0x00, 0x21, 0x80, 0x08, + 0x40, 0x01, 0x10, 0x00, 0x44, 0x00, 0x11, 0x00, 0x04, 0x40, 0x01, 0x18, + 0x00, 0x86, 0x00, 0x21, 0xC0, 0x10, 0x5C, 0x18, 0xF1, 0xF8, 0x00, 0x07, + 0xE4, 0x30, 0x78, 0x80, 0x32, 0x00, 0x24, 0x00, 0x50, 0x00, 0x20, 0x00, + 0x40, 0x00, 0x80, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x00, 0xC3, + 0x07, 0x01, 0xF8, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x7C, 0x20, 0x60, 0xC8, 0x20, 0x0A, + 0x10, 0x01, 0x84, 0x00, 0x62, 0x00, 0x08, 0x80, 0x02, 0x20, 0x00, 0x88, + 0x00, 0x22, 0x00, 0x08, 0xC0, 0x06, 0x10, 0x01, 0x82, 0x00, 0xE0, 0x60, + 0xE8, 0x0F, 0xE3, 0xC0, 0x07, 0xE0, 0x1C, 0x18, 0x30, 0x0C, 0x60, 0x06, + 0x40, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0xC0, 0x00, + 0x40, 0x00, 0x60, 0x00, 0x30, 0x03, 0x0C, 0x0E, 0x03, 0xF0, 0x03, 0xFC, + 0x18, 0x00, 0x80, 0x02, 0x00, 0x08, 0x00, 0x20, 0x0F, 0xFF, 0x82, 0x00, + 0x08, 0x00, 0x20, 0x00, 0x80, 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, + 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x02, 0x00, 0xFF, 0xF0, 0x0F, + 0xC7, 0x9C, 0x3A, 0x18, 0x07, 0x08, 0x01, 0x8C, 0x00, 0xC4, 0x00, 0x22, + 0x00, 0x11, 0x00, 0x08, 0x80, 0x04, 0x40, 0x02, 0x10, 0x03, 0x08, 0x01, + 0x82, 0x01, 0x40, 0xC3, 0x20, 0x3F, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x7F, 0x00, 0xF0, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x47, + 0xC0, 0x2C, 0x18, 0x1C, 0x04, 0x0C, 0x01, 0x04, 0x00, 0x82, 0x00, 0x41, + 0x00, 0x20, 0x80, 0x10, 0x40, 0x08, 0x20, 0x04, 0x10, 0x02, 0x08, 0x01, + 0x04, 0x00, 0x82, 0x00, 0x47, 0xC0, 0xF8, 0x06, 0x00, 0x18, 0x00, 0x60, + 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x02, 0x00, 0x08, + 0x00, 0x20, 0x00, 0x80, 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x02, + 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x02, 0x03, 0xFF, 0xF0, 0x03, 0x00, + 0xC0, 0x30, 0x0C, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x40, 0x10, 0x04, + 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, + 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x08, 0x06, 0xFE, 0x00, 0xF0, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, + 0xFE, 0x10, 0x30, 0x10, 0xE0, 0x11, 0xC0, 0x13, 0x00, 0x16, 0x00, 0x1E, + 0x00, 0x1B, 0x00, 0x11, 0x80, 0x10, 0xC0, 0x10, 0x60, 0x10, 0x30, 0x10, + 0x18, 0x10, 0x1C, 0xF0, 0x3F, 0x7E, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, + 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x02, 0x00, 0x08, 0x00, 0x20, + 0x00, 0x80, 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x02, 0x00, 0x08, + 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFC, 0xEF, 0x9E, 0x07, 0x1E, 0x20, 0xC1, + 0x82, 0x10, 0x20, 0x42, 0x04, 0x08, 0x40, 0x81, 0x08, 0x10, 0x21, 0x02, + 0x04, 0x20, 0x40, 0x84, 0x08, 0x10, 0x81, 0x02, 0x10, 0x20, 0x42, 0x04, + 0x08, 0x40, 0x81, 0x3E, 0x1C, 0x38, 0x71, 0xF0, 0x0B, 0x06, 0x07, 0x01, + 0x03, 0x00, 0x41, 0x00, 0x20, 0x80, 0x10, 0x40, 0x08, 0x20, 0x04, 0x10, + 0x02, 0x08, 0x01, 0x04, 0x00, 0x82, 0x00, 0x41, 0x00, 0x20, 0x80, 0x13, + 0xF0, 0x3E, 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x24, 0x00, 0x50, + 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x05, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x83, 0x06, 0x01, 0xF0, 0x00, 0xF1, 0xFC, 0x05, 0xC1, 0x81, + 0xC0, 0x10, 0x60, 0x02, 0x18, 0x00, 0xC4, 0x00, 0x11, 0x00, 0x04, 0x40, + 0x01, 0x10, 0x00, 0x44, 0x00, 0x11, 0x80, 0x08, 0x60, 0x02, 0x14, 0x01, + 0x04, 0xC1, 0x81, 0x0F, 0x80, 0x40, 0x00, 0x10, 0x00, 0x04, 0x00, 0x01, + 0x00, 0x00, 0x40, 0x00, 0x10, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xE3, 0xC6, + 0x0E, 0x86, 0x00, 0xE1, 0x00, 0x18, 0xC0, 0x06, 0x20, 0x00, 0x88, 0x00, + 0x22, 0x00, 0x08, 0x80, 0x02, 0x20, 0x00, 0x84, 0x00, 0x61, 0x00, 0x18, + 0x20, 0x0A, 0x06, 0x0C, 0x80, 0x7C, 0x20, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x00, 0x80, 0x00, 0x20, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0F, 0xF0, 0xF8, + 0x7C, 0x11, 0x8C, 0x2C, 0x00, 0x70, 0x00, 0xC0, 0x01, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, + 0x00, 0x3F, 0xFC, 0x00, 0x0F, 0xD1, 0x83, 0x98, 0x04, 0x80, 0x24, 0x00, + 0x30, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0x30, 0x00, 0xE0, 0x03, 0x00, 0x1C, + 0x01, 0xF0, 0x1A, 0x7F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x00, 0xFF, 0xFC, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x00, 0x08, 0x01, 0x06, 0x0F, 0x03, 0xF8, 0xF0, 0x3E, 0x08, + 0x01, 0x04, 0x00, 0x82, 0x00, 0x41, 0x00, 0x20, 0x80, 0x10, 0x40, 0x08, + 0x20, 0x04, 0x10, 0x02, 0x08, 0x01, 0x04, 0x00, 0x82, 0x00, 0x41, 0x00, + 0xE0, 0x41, 0xD0, 0x1F, 0x8E, 0xFE, 0x0F, 0xE2, 0x00, 0x20, 0x60, 0x0C, + 0x0C, 0x01, 0x80, 0x80, 0x20, 0x18, 0x0C, 0x01, 0x01, 0x00, 0x30, 0x60, + 0x02, 0x08, 0x00, 0x41, 0x00, 0x0C, 0x60, 0x00, 0x88, 0x00, 0x19, 0x00, + 0x01, 0x40, 0x00, 0x38, 0x00, 0xFC, 0x07, 0xE4, 0x00, 0x10, 0x80, 0x02, + 0x18, 0x20, 0xC3, 0x0E, 0x18, 0x21, 0x42, 0x04, 0x28, 0x40, 0x8D, 0x88, + 0x19, 0x93, 0x03, 0x22, 0x60, 0x2C, 0x68, 0x05, 0x85, 0x00, 0xA0, 0xA0, + 0x1C, 0x1C, 0x01, 0x81, 0x80, 0x7C, 0x1F, 0x18, 0x03, 0x06, 0x03, 0x01, + 0x83, 0x00, 0x63, 0x00, 0x1B, 0x00, 0x07, 0x00, 0x03, 0x80, 0x03, 0x60, + 0x03, 0x18, 0x03, 0x06, 0x03, 0x01, 0x83, 0x00, 0x61, 0x00, 0x33, 0xF0, + 0x7E, 0xFC, 0x1F, 0x90, 0x01, 0x8C, 0x00, 0x86, 0x00, 0xC1, 0x80, 0x40, + 0xC0, 0x60, 0x20, 0x20, 0x18, 0x30, 0x04, 0x10, 0x03, 0x08, 0x00, 0x8C, + 0x00, 0x64, 0x00, 0x16, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x01, 0x00, 0x01, + 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x20, 0x07, 0xFE, 0x00, + 0xFF, 0xF4, 0x01, 0x20, 0x09, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, + 0xC0, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x14, 0x00, 0xA0, 0x07, 0xFF, + 0xE0, 0x07, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x30, 0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x0C, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x30, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x07, 0x0C, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x30, 0xE0, 0x1C, 0x00, 0x44, 0x0D, 0x84, + 0x36, 0x04, 0x40, 0x07, 0x00}; + +const GFXglyph FreeMono18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 21, 0, 1}, // 0x20 ' ' + {0, 4, 22, 21, 8, -21}, // 0x21 '!' + {11, 11, 10, 21, 5, -20}, // 0x22 '"' + {25, 14, 24, 21, 3, -21}, // 0x23 '#' + {67, 13, 26, 21, 4, -22}, // 0x24 '$' + {110, 15, 21, 21, 3, -20}, // 0x25 '%' + {150, 12, 18, 21, 4, -17}, // 0x26 '&' + {177, 4, 10, 21, 8, -20}, // 0x27 ''' + {182, 5, 25, 21, 10, -20}, // 0x28 '(' + {198, 5, 25, 21, 6, -20}, // 0x29 ')' + {214, 13, 12, 21, 4, -20}, // 0x2A '*' + {234, 15, 17, 21, 3, -17}, // 0x2B '+' + {266, 7, 10, 21, 5, -4}, // 0x2C ',' + {275, 15, 1, 21, 3, -9}, // 0x2D '-' + {277, 5, 5, 21, 8, -4}, // 0x2E '.' + {281, 13, 26, 21, 4, -22}, // 0x2F '/' + {324, 13, 21, 21, 4, -20}, // 0x30 '0' + {359, 13, 21, 21, 4, -20}, // 0x31 '1' + {394, 13, 21, 21, 3, -20}, // 0x32 '2' + {429, 14, 21, 21, 3, -20}, // 0x33 '3' + {466, 12, 21, 21, 4, -20}, // 0x34 '4' + {498, 14, 21, 21, 3, -20}, // 0x35 '5' + {535, 12, 21, 21, 5, -20}, // 0x36 '6' + {567, 12, 21, 21, 4, -20}, // 0x37 '7' + {599, 13, 21, 21, 4, -20}, // 0x38 '8' + {634, 12, 21, 21, 5, -20}, // 0x39 '9' + {666, 5, 15, 21, 8, -14}, // 0x3A ':' + {676, 7, 20, 21, 5, -14}, // 0x3B ';' + {694, 15, 16, 21, 3, -17}, // 0x3C '<' + {724, 17, 6, 21, 2, -12}, // 0x3D '=' + {737, 15, 16, 21, 3, -17}, // 0x3E '>' + {767, 12, 20, 21, 5, -19}, // 0x3F '?' + {797, 13, 23, 21, 4, -20}, // 0x40 '@' + {835, 21, 20, 21, 0, -19}, // 0x41 'A' + {888, 18, 20, 21, 1, -19}, // 0x42 'B' + {933, 17, 20, 21, 2, -19}, // 0x43 'C' + {976, 16, 20, 21, 2, -19}, // 0x44 'D' + {1016, 17, 20, 21, 1, -19}, // 0x45 'E' + {1059, 17, 20, 21, 1, -19}, // 0x46 'F' + {1102, 17, 20, 21, 2, -19}, // 0x47 'G' + {1145, 16, 20, 21, 2, -19}, // 0x48 'H' + {1185, 13, 20, 21, 4, -19}, // 0x49 'I' + {1218, 17, 20, 21, 3, -19}, // 0x4A 'J' + {1261, 18, 20, 21, 1, -19}, // 0x4B 'K' + {1306, 15, 20, 21, 3, -19}, // 0x4C 'L' + {1344, 19, 20, 21, 1, -19}, // 0x4D 'M' + {1392, 18, 20, 21, 1, -19}, // 0x4E 'N' + {1437, 17, 20, 21, 2, -19}, // 0x4F 'O' + {1480, 16, 20, 21, 1, -19}, // 0x50 'P' + {1520, 17, 24, 21, 2, -19}, // 0x51 'Q' + {1571, 19, 20, 21, 1, -19}, // 0x52 'R' + {1619, 14, 20, 21, 3, -19}, // 0x53 'S' + {1654, 15, 20, 21, 3, -19}, // 0x54 'T' + {1692, 17, 20, 21, 2, -19}, // 0x55 'U' + {1735, 21, 20, 21, 0, -19}, // 0x56 'V' + {1788, 19, 20, 21, 1, -19}, // 0x57 'W' + {1836, 19, 20, 21, 1, -19}, // 0x58 'X' + {1884, 17, 20, 21, 2, -19}, // 0x59 'Y' + {1927, 13, 20, 21, 4, -19}, // 0x5A 'Z' + {1960, 5, 25, 21, 10, -20}, // 0x5B '[' + {1976, 13, 26, 21, 4, -22}, // 0x5C '\' + {2019, 5, 25, 21, 6, -20}, // 0x5D ']' + {2035, 13, 9, 21, 4, -20}, // 0x5E '^' + {2050, 21, 1, 21, 0, 4}, // 0x5F '_' + {2053, 6, 5, 21, 5, -21}, // 0x60 '`' + {2057, 16, 15, 21, 3, -14}, // 0x61 'a' + {2087, 18, 21, 21, 1, -20}, // 0x62 'b' + {2135, 15, 15, 21, 3, -14}, // 0x63 'c' + {2164, 18, 21, 21, 2, -20}, // 0x64 'd' + {2212, 16, 15, 21, 2, -14}, // 0x65 'e' + {2242, 14, 21, 21, 4, -20}, // 0x66 'f' + {2279, 17, 22, 21, 2, -14}, // 0x67 'g' + {2326, 17, 21, 21, 1, -20}, // 0x68 'h' + {2371, 14, 22, 21, 4, -21}, // 0x69 'i' + {2410, 10, 29, 21, 5, -21}, // 0x6A 'j' + {2447, 16, 21, 21, 2, -20}, // 0x6B 'k' + {2489, 14, 21, 21, 4, -20}, // 0x6C 'l' + {2526, 19, 15, 21, 1, -14}, // 0x6D 'm' + {2562, 17, 15, 21, 1, -14}, // 0x6E 'n' + {2594, 15, 15, 21, 3, -14}, // 0x6F 'o' + {2623, 18, 22, 21, 1, -14}, // 0x70 'p' + {2673, 18, 22, 21, 2, -14}, // 0x71 'q' + {2723, 15, 15, 21, 3, -14}, // 0x72 'r' + {2752, 13, 15, 21, 4, -14}, // 0x73 's' + {2777, 16, 20, 21, 1, -19}, // 0x74 't' + {2817, 17, 15, 21, 1, -14}, // 0x75 'u' + {2849, 19, 15, 21, 1, -14}, // 0x76 'v' + {2885, 19, 15, 21, 1, -14}, // 0x77 'w' + {2921, 17, 15, 21, 2, -14}, // 0x78 'x' + {2953, 17, 22, 21, 2, -14}, // 0x79 'y' + {3000, 13, 15, 21, 4, -14}, // 0x7A 'z' + {3025, 8, 25, 21, 6, -20}, // 0x7B '{' + {3050, 1, 25, 21, 10, -20}, // 0x7C '|' + {3054, 8, 25, 21, 7, -20}, // 0x7D '}' + {3079, 15, 5, 21, 3, -11}}; // 0x7E '~' + +const GFXfont FreeMono18pt7b PROGMEM = {(uint8_t *)FreeMono18pt7bBitmaps, + (GFXglyph *)FreeMono18pt7bGlyphs, 0x20, + 0x7E, 35}; + +// Approx. 3761 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMono24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMono24pt7b.h new file mode 100644 index 0000000..8596d0a --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMono24pt7b.h @@ -0,0 +1,576 @@ +const uint8_t FreeMono24pt7bBitmaps[] PROGMEM = { + 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x10, 0x84, 0x21, 0x08, + 0x00, 0x00, 0x00, 0x03, 0xBF, 0xFF, 0xB8, 0xFE, 0x7F, 0x7C, 0x3E, 0x7C, + 0x3E, 0x7C, 0x3E, 0x7C, 0x3E, 0x7C, 0x3E, 0x7C, 0x3E, 0x7C, 0x3E, 0x3C, + 0x3E, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x01, + 0x86, 0x00, 0x30, 0xC0, 0x06, 0x18, 0x00, 0xC3, 0x00, 0x18, 0x60, 0x03, + 0x0C, 0x00, 0x61, 0x80, 0x0C, 0x70, 0x01, 0x8C, 0x00, 0x61, 0x80, 0x0C, + 0x30, 0x3F, 0xFF, 0xF7, 0xFF, 0xFE, 0x06, 0x18, 0x00, 0xC3, 0x00, 0x18, + 0x60, 0x03, 0x0C, 0x00, 0x61, 0x80, 0x0C, 0x30, 0x7F, 0xFF, 0xEF, 0xFF, + 0xFC, 0x06, 0x18, 0x00, 0xC7, 0x00, 0x38, 0xC0, 0x06, 0x18, 0x00, 0xC3, + 0x00, 0x18, 0x60, 0x03, 0x0C, 0x00, 0x61, 0x80, 0x0C, 0x30, 0x01, 0x86, + 0x00, 0x30, 0xC0, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x0F, 0xC0, + 0x0F, 0xFD, 0x87, 0x03, 0xE3, 0x80, 0x39, 0xC0, 0x06, 0x60, 0x01, 0x98, + 0x00, 0x06, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x07, 0xC0, 0x00, 0x7F, + 0x80, 0x03, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0x60, 0x00, 0x1C, 0x00, 0x03, + 0x80, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, 0x80, 0x0E, 0xFC, 0x0F, 0x37, + 0xFF, 0x80, 0x7F, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, + 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x07, 0x80, 0x01, 0xFE, 0x00, 0x38, + 0x70, 0x03, 0x03, 0x00, 0x60, 0x18, 0x06, 0x01, 0x80, 0x60, 0x18, 0x06, + 0x01, 0x80, 0x30, 0x30, 0x03, 0x87, 0x00, 0x1F, 0xE0, 0x30, 0x78, 0x1F, + 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x00, 0xF8, 0x00, + 0x0C, 0x01, 0xE0, 0x00, 0x7F, 0x80, 0x0E, 0x1C, 0x00, 0xC0, 0xC0, 0x18, + 0x06, 0x01, 0x80, 0x60, 0x18, 0x06, 0x01, 0x80, 0x60, 0x0C, 0x0E, 0x00, + 0xE1, 0xC0, 0x07, 0xF8, 0x00, 0x1E, 0x00, 0x03, 0xEC, 0x01, 0xFF, 0x00, + 0xE1, 0x00, 0x70, 0x00, 0x18, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0x30, + 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x7C, 0x00, 0x3B, 0x83, + 0xD8, 0x60, 0xFE, 0x0C, 0x33, 0x03, 0x98, 0xC0, 0x66, 0x30, 0x0D, 0x8C, + 0x03, 0xC3, 0x00, 0x70, 0x60, 0x1C, 0x1C, 0x0F, 0x03, 0x87, 0x7C, 0x7F, + 0x9F, 0x07, 0x80, 0x00, 0xFE, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x3C, + 0x70, 0xE1, 0xC3, 0x87, 0x00, 0x06, 0x1C, 0x30, 0xE1, 0x87, 0x0E, 0x18, + 0x70, 0xE1, 0xC3, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x0C, + 0x1C, 0x38, 0x70, 0x60, 0xE1, 0xC1, 0x83, 0x83, 0x06, 0x06, 0x04, 0xC1, + 0xC1, 0x83, 0x83, 0x07, 0x0E, 0x0C, 0x1C, 0x38, 0x70, 0xE0, 0xE1, 0xC3, + 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0x87, 0x0E, 0x1C, 0x30, 0x61, 0xC3, + 0x0E, 0x18, 0x70, 0xC1, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0xC0, 0x10, 0x30, 0x3F, 0x8C, 0x7C, 0xFF, 0xFC, 0x07, + 0xF8, 0x00, 0x78, 0x00, 0x1F, 0x00, 0x0C, 0xC0, 0x06, 0x18, 0x03, 0x87, + 0x00, 0xC0, 0xC0, 0x60, 0x18, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, 0x60, + 0x00, 0x06, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, 0x60, 0x00, 0x06, + 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, 0x60, 0x00, + 0x06, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, + 0x1F, 0x8F, 0x87, 0xC7, 0xC3, 0xE1, 0xE1, 0xF0, 0xF0, 0x78, 0x38, 0x3C, + 0x1C, 0x0E, 0x06, 0x00, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF0, 0x7D, 0xFF, + 0xFF, 0xFF, 0xEF, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x70, 0x00, 0x18, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x18, 0x00, 0x0C, + 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x30, 0x00, 0x0C, 0x00, + 0x06, 0x00, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x18, 0x00, 0x06, + 0x00, 0x03, 0x80, 0x00, 0xC0, 0x00, 0x70, 0x00, 0x18, 0x00, 0x0E, 0x00, + 0x03, 0x00, 0x01, 0xC0, 0x00, 0x60, 0x00, 0x38, 0x00, 0x0C, 0x00, 0x07, + 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x00, 0x03, + 0xF0, 0x03, 0xFF, 0x01, 0xE1, 0xE0, 0xE0, 0x18, 0x30, 0x03, 0x1C, 0x00, + 0xE6, 0x00, 0x19, 0x80, 0x06, 0xE0, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x0F, + 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, + 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF8, 0x00, + 0x76, 0x00, 0x19, 0x80, 0x06, 0x70, 0x03, 0x8C, 0x00, 0xC3, 0x80, 0x60, + 0x78, 0x78, 0x0F, 0xFC, 0x00, 0xFC, 0x00, 0x03, 0x80, 0x07, 0x80, 0x0F, + 0x80, 0x1D, 0x80, 0x39, 0x80, 0x71, 0x80, 0xE1, 0x80, 0xC1, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xC0, 0xE0, + 0xC0, 0x1C, 0x60, 0x03, 0xB8, 0x00, 0x6C, 0x00, 0x0F, 0x00, 0x03, 0x00, + 0x00, 0xC0, 0x00, 0x30, 0x00, 0x18, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, + 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0C, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, + 0x00, 0xD0, 0x00, 0x38, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, + 0xF8, 0x01, 0xFF, 0xC0, 0x70, 0x3C, 0x18, 0x01, 0xC6, 0x00, 0x18, 0x00, + 0x01, 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x18, 0x00, + 0x06, 0x00, 0x01, 0xC0, 0x00, 0x70, 0x01, 0xFC, 0x00, 0x3F, 0x00, 0x00, + 0x78, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, + 0x06, 0x00, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0xD8, 0x00, + 0x3B, 0x80, 0x0E, 0x3E, 0x07, 0x81, 0xFF, 0xE0, 0x07, 0xE0, 0x00, 0x00, + 0x3C, 0x00, 0x7C, 0x00, 0x6C, 0x00, 0xCC, 0x00, 0x8C, 0x01, 0x8C, 0x03, + 0x0C, 0x03, 0x0C, 0x06, 0x0C, 0x04, 0x0C, 0x0C, 0x0C, 0x08, 0x0C, 0x10, + 0x0C, 0x30, 0x0C, 0x20, 0x0C, 0x60, 0x0C, 0x40, 0x0C, 0x80, 0x0C, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0xFF, 0x00, 0xFF, 0x3F, 0xFF, 0x07, 0xFF, 0xE0, + 0xC0, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x0C, 0x00, 0x01, + 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0xC7, 0xE0, 0x1F, 0xFF, 0x03, + 0x80, 0x70, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0x60, + 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0xC0, + 0x00, 0x30, 0x00, 0x06, 0xC0, 0x01, 0xDC, 0x00, 0x71, 0xF0, 0x3C, 0x0F, + 0xFF, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x80, 0x3F, 0xF0, 0x3E, 0x00, 0x1E, + 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x00, 0xC0, 0x00, 0x70, 0x00, + 0x18, 0x00, 0x06, 0x00, 0x03, 0x80, 0x00, 0xC1, 0xF8, 0x31, 0xFF, 0x0C, + 0xF0, 0xF3, 0x70, 0x0C, 0xD8, 0x01, 0xBC, 0x00, 0x6E, 0x00, 0x0F, 0x80, + 0x03, 0xC0, 0x00, 0xD8, 0x00, 0x36, 0x00, 0x0D, 0x80, 0x03, 0x30, 0x01, + 0x8E, 0x00, 0x61, 0xC0, 0x30, 0x38, 0x38, 0x07, 0xFC, 0x00, 0x7C, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x01, 0xC0, + 0x00, 0x60, 0x00, 0x18, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, + 0x30, 0x00, 0x18, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x30, + 0x00, 0x0C, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x30, 0x00, + 0x0C, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x18, 0x00, 0x0C, + 0x00, 0x03, 0x00, 0x03, 0xF0, 0x03, 0xFF, 0x03, 0xC0, 0xF1, 0xC0, 0x0E, + 0x60, 0x01, 0xB8, 0x00, 0x7C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, + 0x00, 0x36, 0x00, 0x18, 0xC0, 0x0C, 0x1C, 0x0E, 0x03, 0xFF, 0x00, 0xFF, + 0xC0, 0x70, 0x38, 0x30, 0x03, 0x18, 0x00, 0x66, 0x00, 0x1B, 0x00, 0x03, + 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0x60, 0x01, 0x98, + 0x00, 0xE3, 0x00, 0x70, 0x70, 0x38, 0x0F, 0xFC, 0x00, 0xFC, 0x00, 0x07, + 0xE0, 0x03, 0xFE, 0x01, 0xC1, 0xC0, 0xC0, 0x38, 0x60, 0x07, 0x18, 0x00, + 0xCC, 0x00, 0x1B, 0x00, 0x06, 0xC0, 0x01, 0xB0, 0x00, 0x3C, 0x00, 0x1F, + 0x00, 0x07, 0x60, 0x03, 0xD8, 0x01, 0xB3, 0x00, 0xCC, 0xF0, 0xF3, 0x0F, + 0xF8, 0xC1, 0xF8, 0x30, 0x00, 0x1C, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, + 0xE0, 0x00, 0x30, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x07, 0x80, + 0x07, 0xC0, 0xFF, 0xC0, 0x1F, 0xC0, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xEF, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, + 0xF7, 0xC0, 0x0F, 0x87, 0xF1, 0xFC, 0x7F, 0x1F, 0xC3, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF1, 0xF8, 0x7C, 0x3F, 0x0F, + 0x83, 0xE0, 0xF0, 0x7C, 0x1E, 0x07, 0x81, 0xC0, 0xF0, 0x38, 0x04, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, + 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x00, + 0x00, 0x78, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x20, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, + 0xFF, 0x7F, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x03, + 0xC0, 0x00, 0x07, 0x80, 0x00, 0x0E, 0x00, 0x00, 0x3C, 0x00, 0x01, 0xE0, + 0x00, 0x3C, 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x01, 0xE0, + 0x00, 0x3C, 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x0E, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x07, 0xF0, 0x1F, 0xFE, 0x3E, 0x07, 0x98, 0x00, 0xEC, 0x00, + 0x36, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, 0x80, 0x01, 0xC0, + 0x00, 0xC0, 0x01, 0xC0, 0x03, 0xC0, 0x07, 0xC0, 0x07, 0x00, 0x03, 0x00, + 0x01, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x80, 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x00, + 0x78, 0x00, 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xE0, 0x1C, 0x30, + 0x03, 0x1C, 0x00, 0x66, 0x00, 0x19, 0x80, 0x06, 0xC0, 0x01, 0xB0, 0x07, + 0xEC, 0x07, 0xFB, 0x03, 0xC6, 0xC1, 0xC1, 0xB0, 0xE0, 0x6C, 0x30, 0x1B, + 0x0C, 0x06, 0xC3, 0x01, 0xB0, 0xC0, 0x6C, 0x18, 0x1B, 0x07, 0x86, 0xC0, + 0xFF, 0xF0, 0x0F, 0xFC, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x18, 0x00, + 0x07, 0x00, 0x00, 0xC0, 0x00, 0x38, 0x00, 0x07, 0x80, 0xC0, 0xFF, 0xF0, + 0x0F, 0xE0, 0x07, 0xFF, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x1B, 0x00, + 0x00, 0x01, 0x98, 0x00, 0x00, 0x11, 0x80, 0x00, 0x03, 0x0C, 0x00, 0x00, + 0x30, 0xC0, 0x00, 0x06, 0x0C, 0x00, 0x00, 0x60, 0x60, 0x00, 0x06, 0x06, + 0x00, 0x00, 0xC0, 0x30, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x80, 0x30, 0x00, + 0x18, 0x01, 0x80, 0x01, 0x80, 0x18, 0x00, 0x3F, 0xFF, 0x80, 0x03, 0xFF, + 0xFC, 0x00, 0x20, 0x00, 0xC0, 0x06, 0x00, 0x06, 0x00, 0x60, 0x00, 0x60, + 0x0C, 0x00, 0x06, 0x00, 0xC0, 0x00, 0x30, 0x0C, 0x00, 0x03, 0x01, 0x80, + 0x00, 0x18, 0x7F, 0xC0, 0x3F, 0xF7, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0x03, + 0xFF, 0xFF, 0x01, 0x80, 0x0E, 0x06, 0x00, 0x1C, 0x18, 0x00, 0x38, 0x60, + 0x00, 0x61, 0x80, 0x01, 0x86, 0x00, 0x06, 0x18, 0x00, 0x38, 0x60, 0x01, + 0xC1, 0x80, 0x1E, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, 0xC0, 0x60, 0x03, 0xC1, + 0x80, 0x03, 0x86, 0x00, 0x06, 0x18, 0x00, 0x1C, 0x60, 0x00, 0x31, 0x80, + 0x00, 0xC6, 0x00, 0x03, 0x18, 0x00, 0x0C, 0x60, 0x00, 0x61, 0x80, 0x03, + 0x86, 0x00, 0x1C, 0xFF, 0xFF, 0xE3, 0xFF, 0xFE, 0x00, 0x00, 0xFC, 0x00, + 0x0F, 0xFE, 0x60, 0xF0, 0x3D, 0x87, 0x00, 0x3E, 0x38, 0x00, 0x38, 0xC0, + 0x00, 0xE7, 0x00, 0x01, 0x98, 0x00, 0x06, 0x60, 0x00, 0x03, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, + 0xC7, 0x00, 0x06, 0x0E, 0x00, 0x70, 0x1E, 0x07, 0x80, 0x3F, 0xFC, 0x00, + 0x1F, 0x80, 0xFF, 0xFE, 0x03, 0xFF, 0xFE, 0x03, 0x00, 0x3C, 0x0C, 0x00, + 0x38, 0x30, 0x00, 0x70, 0xC0, 0x00, 0xC3, 0x00, 0x03, 0x8C, 0x00, 0x06, + 0x30, 0x00, 0x1C, 0xC0, 0x00, 0x33, 0x00, 0x00, 0xCC, 0x00, 0x03, 0x30, + 0x00, 0x0C, 0xC0, 0x00, 0x33, 0x00, 0x00, 0xCC, 0x00, 0x03, 0x30, 0x00, + 0x0C, 0xC0, 0x00, 0x33, 0x00, 0x01, 0x8C, 0x00, 0x06, 0x30, 0x00, 0x30, + 0xC0, 0x01, 0xC3, 0x00, 0x0E, 0x0C, 0x00, 0xF0, 0xFF, 0xFF, 0x83, 0xFF, + 0xF8, 0x00, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xE1, 0x80, 0x01, 0x86, 0x00, + 0x06, 0x18, 0x00, 0x18, 0x60, 0x00, 0x61, 0x80, 0x01, 0x86, 0x00, 0x00, + 0x18, 0x0C, 0x00, 0x60, 0x30, 0x01, 0x80, 0xC0, 0x07, 0xFF, 0x00, 0x1F, + 0xFC, 0x00, 0x60, 0x30, 0x01, 0x80, 0xC0, 0x06, 0x03, 0x00, 0x18, 0x00, + 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0xC6, 0x00, 0x03, 0x18, 0x00, 0x0C, + 0x60, 0x00, 0x31, 0x80, 0x00, 0xC6, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x80, 0x00, 0xC6, 0x00, + 0x03, 0x18, 0x00, 0x0C, 0x60, 0x00, 0x31, 0x80, 0x00, 0xC6, 0x00, 0x00, + 0x18, 0x0C, 0x00, 0x60, 0x30, 0x01, 0x80, 0xC0, 0x07, 0xFF, 0x00, 0x1F, + 0xFC, 0x00, 0x60, 0x30, 0x01, 0x80, 0xC0, 0x06, 0x03, 0x00, 0x18, 0x00, + 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x60, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0xFF, 0xF0, 0x03, 0xFF, + 0xC0, 0x00, 0x00, 0xFF, 0x00, 0x07, 0xFF, 0x98, 0x1E, 0x03, 0xF0, 0x70, + 0x01, 0xE1, 0x80, 0x01, 0xC6, 0x00, 0x01, 0x9C, 0x00, 0x03, 0x30, 0x00, + 0x00, 0x60, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x03, 0xFF, + 0xC0, 0x07, 0xFF, 0x80, 0x00, 0x1B, 0x00, 0x00, 0x37, 0x00, 0x00, 0x66, + 0x00, 0x00, 0xCC, 0x00, 0x01, 0x8C, 0x00, 0x03, 0x1C, 0x00, 0x06, 0x1E, + 0x00, 0x0C, 0x0F, 0x00, 0xF8, 0x0F, 0xFF, 0xC0, 0x03, 0xFC, 0x00, 0x7F, + 0x01, 0xFC, 0xFE, 0x03, 0xF8, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, + 0x03, 0x03, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, + 0x30, 0x30, 0x00, 0x60, 0x60, 0x00, 0xC0, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0x03, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x30, + 0x30, 0x00, 0x60, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, 0x03, 0x03, + 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0xFF, 0x01, 0xFF, 0xFE, + 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFE, 0x01, 0xFF, 0xFC, + 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, + 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x60, 0xC0, 0x00, 0xC1, 0x80, 0x01, 0x83, 0x00, 0x03, + 0x06, 0x00, 0x06, 0x0C, 0x00, 0x0C, 0x18, 0x00, 0x30, 0x38, 0x00, 0x60, + 0x38, 0x01, 0x80, 0x3C, 0x0E, 0x00, 0x3F, 0xF8, 0x00, 0x0F, 0xC0, 0x00, + 0xFF, 0x81, 0xFE, 0xFF, 0x81, 0xFE, 0x18, 0x00, 0x30, 0x18, 0x00, 0xE0, + 0x18, 0x01, 0xC0, 0x18, 0x03, 0x80, 0x18, 0x07, 0x00, 0x18, 0x0E, 0x00, + 0x18, 0x18, 0x00, 0x18, 0x70, 0x00, 0x18, 0xE0, 0x00, 0x19, 0xE0, 0x00, + 0x1B, 0xF8, 0x00, 0x1F, 0x1C, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x03, 0x00, + 0x18, 0x03, 0x80, 0x18, 0x01, 0x80, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, + 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x70, 0x18, 0x00, 0x30, + 0xFF, 0x80, 0x3F, 0xFF, 0x80, 0x1F, 0xFF, 0xF0, 0x07, 0xFF, 0x80, 0x01, + 0x80, 0x00, 0x0C, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, + 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, + 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, + 0x06, 0x00, 0x18, 0x30, 0x00, 0xC1, 0x80, 0x06, 0x0C, 0x00, 0x30, 0x60, + 0x01, 0x83, 0x00, 0x0C, 0x18, 0x00, 0x60, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0xFC, 0x00, 0x0F, 0xFF, 0x00, 0x03, 0xF3, 0x60, 0x01, + 0xB0, 0xD8, 0x00, 0x6C, 0x33, 0x00, 0x33, 0x0C, 0xC0, 0x0C, 0xC3, 0x38, + 0x07, 0x30, 0xC6, 0x01, 0x8C, 0x31, 0xC0, 0xE3, 0x0C, 0x30, 0x30, 0xC3, + 0x0C, 0x0C, 0x30, 0xC1, 0x86, 0x0C, 0x30, 0x61, 0x83, 0x0C, 0x0C, 0xC0, + 0xC3, 0x03, 0x30, 0x30, 0xC0, 0x78, 0x0C, 0x30, 0x1E, 0x03, 0x0C, 0x03, + 0x00, 0xC3, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x0C, 0x30, 0x00, 0x03, 0x0C, + 0x00, 0x00, 0xC3, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x0C, 0xFF, 0x00, 0x3F, + 0xFF, 0xC0, 0x0F, 0xF0, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0xFF, 0x1E, 0x00, + 0x0C, 0x1F, 0x00, 0x0C, 0x1B, 0x00, 0x0C, 0x19, 0x80, 0x0C, 0x19, 0xC0, + 0x0C, 0x18, 0xC0, 0x0C, 0x18, 0x60, 0x0C, 0x18, 0x60, 0x0C, 0x18, 0x30, + 0x0C, 0x18, 0x38, 0x0C, 0x18, 0x18, 0x0C, 0x18, 0x0C, 0x0C, 0x18, 0x0E, + 0x0C, 0x18, 0x06, 0x0C, 0x18, 0x03, 0x0C, 0x18, 0x03, 0x0C, 0x18, 0x01, + 0x8C, 0x18, 0x01, 0xCC, 0x18, 0x00, 0xCC, 0x18, 0x00, 0x6C, 0x18, 0x00, + 0x7C, 0x18, 0x00, 0x3C, 0x7F, 0x80, 0x1C, 0x7F, 0x80, 0x1C, 0x00, 0x7E, + 0x00, 0x01, 0xFF, 0xC0, 0x07, 0x81, 0xE0, 0x0E, 0x00, 0x70, 0x1C, 0x00, + 0x38, 0x38, 0x00, 0x1C, 0x30, 0x00, 0x0C, 0x70, 0x00, 0x0E, 0x60, 0x00, + 0x06, 0x60, 0x00, 0x06, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, + 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, + 0x03, 0xC0, 0x00, 0x03, 0x60, 0x00, 0x06, 0x60, 0x00, 0x06, 0x70, 0x00, + 0x0E, 0x30, 0x00, 0x0C, 0x38, 0x00, 0x1C, 0x1C, 0x00, 0x38, 0x0E, 0x00, + 0x70, 0x07, 0x81, 0xE0, 0x03, 0xFF, 0xC0, 0x00, 0x7E, 0x00, 0xFF, 0xFF, + 0x07, 0xFF, 0xFE, 0x06, 0x00, 0x78, 0x30, 0x00, 0xE1, 0x80, 0x03, 0x0C, + 0x00, 0x0C, 0x60, 0x00, 0x63, 0x00, 0x03, 0x18, 0x00, 0x18, 0xC0, 0x01, + 0xC6, 0x00, 0x0C, 0x30, 0x00, 0xC1, 0x80, 0x1E, 0x0F, 0xFF, 0xC0, 0x7F, + 0xF8, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, + 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, + 0x00, 0xFF, 0xF0, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xFF, + 0x80, 0x07, 0x81, 0xE0, 0x0E, 0x00, 0x70, 0x1C, 0x00, 0x38, 0x38, 0x00, + 0x1C, 0x30, 0x00, 0x0C, 0x70, 0x00, 0x0E, 0x60, 0x00, 0x06, 0x60, 0x00, + 0x06, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, + 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, + 0x03, 0x60, 0x00, 0x06, 0x60, 0x00, 0x06, 0x70, 0x00, 0x0E, 0x30, 0x00, + 0x0C, 0x18, 0x00, 0x1C, 0x0C, 0x00, 0x38, 0x06, 0x00, 0x70, 0x03, 0x81, + 0xE0, 0x00, 0xFF, 0xC0, 0x00, 0x7E, 0x00, 0x00, 0xE0, 0x00, 0x03, 0xFF, + 0x87, 0x07, 0xFF, 0xFE, 0x07, 0x00, 0xF8, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, + 0x80, 0x18, 0x03, 0xC0, 0x18, 0x00, 0xE0, 0x18, 0x00, 0x60, 0x18, 0x00, + 0x30, 0x18, 0x00, 0x30, 0x18, 0x00, 0x30, 0x18, 0x00, 0x30, 0x18, 0x00, + 0x70, 0x18, 0x00, 0x60, 0x18, 0x01, 0xC0, 0x18, 0x07, 0x80, 0x1F, 0xFF, + 0x00, 0x1F, 0xFC, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x07, 0x00, 0x18, 0x03, + 0x80, 0x18, 0x01, 0xC0, 0x18, 0x00, 0xE0, 0x18, 0x00, 0x60, 0x18, 0x00, + 0x30, 0x18, 0x00, 0x30, 0x18, 0x00, 0x18, 0xFF, 0x80, 0x1F, 0xFF, 0x80, + 0x0F, 0x03, 0xF8, 0x00, 0xFF, 0xE6, 0x1E, 0x07, 0xE3, 0x80, 0x1E, 0x30, + 0x00, 0xE6, 0x00, 0x06, 0x60, 0x00, 0x66, 0x00, 0x06, 0x60, 0x00, 0x07, + 0x00, 0x00, 0x30, 0x00, 0x01, 0xC0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0xC0, + 0x00, 0x3F, 0x80, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, + 0x30, 0x00, 0x03, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0xE0, 0x00, 0x7E, 0x00, + 0x06, 0xF8, 0x01, 0xED, 0xE0, 0x7C, 0xCF, 0xFF, 0x00, 0x3F, 0xC0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0x00, 0xF0, 0x0C, 0x03, 0xC0, 0x30, + 0x0F, 0x00, 0xC0, 0x3C, 0x03, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0x30, 0x00, + 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, + 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, + 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, + 0x03, 0x00, 0x00, 0x0C, 0x00, 0x0F, 0xFF, 0xC0, 0x3F, 0xFF, 0x00, 0xFF, + 0x01, 0xFF, 0xFE, 0x03, 0xFC, 0xC0, 0x00, 0x61, 0x80, 0x00, 0xC3, 0x00, + 0x01, 0x86, 0x00, 0x03, 0x0C, 0x00, 0x06, 0x18, 0x00, 0x0C, 0x30, 0x00, + 0x18, 0x60, 0x00, 0x30, 0xC0, 0x00, 0x61, 0x80, 0x00, 0xC3, 0x00, 0x01, + 0x86, 0x00, 0x03, 0x0C, 0x00, 0x06, 0x18, 0x00, 0x0C, 0x30, 0x00, 0x18, + 0x60, 0x00, 0x30, 0xC0, 0x00, 0x61, 0x80, 0x00, 0xC3, 0x80, 0x03, 0x83, + 0x00, 0x06, 0x07, 0x00, 0x1C, 0x07, 0x00, 0x70, 0x07, 0x83, 0xC0, 0x07, + 0xFF, 0x00, 0x03, 0xF8, 0x00, 0x7F, 0xC0, 0x3F, 0xF7, 0xFC, 0x03, 0xFF, + 0x18, 0x00, 0x01, 0x80, 0xC0, 0x00, 0x30, 0x0C, 0x00, 0x03, 0x00, 0x60, + 0x00, 0x30, 0x06, 0x00, 0x06, 0x00, 0x60, 0x00, 0x60, 0x03, 0x00, 0x0C, + 0x00, 0x30, 0x00, 0xC0, 0x03, 0x80, 0x0C, 0x00, 0x18, 0x01, 0x80, 0x01, + 0x80, 0x18, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x0E, 0x03, + 0x00, 0x00, 0x60, 0x60, 0x00, 0x06, 0x06, 0x00, 0x00, 0x30, 0xC0, 0x00, + 0x03, 0x0C, 0x00, 0x00, 0x30, 0x80, 0x00, 0x01, 0x98, 0x00, 0x00, 0x19, + 0x80, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xE0, 0x00, + 0xFF, 0x80, 0x7F, 0xFF, 0xE0, 0x1F, 0xF3, 0x00, 0x00, 0x30, 0xC0, 0x00, + 0x0C, 0x30, 0x00, 0x03, 0x0C, 0x03, 0x80, 0xC3, 0x01, 0xE0, 0x30, 0x60, + 0x78, 0x0C, 0x18, 0x1F, 0x02, 0x06, 0x04, 0xC0, 0x81, 0x83, 0x30, 0x60, + 0x60, 0xCC, 0x18, 0x18, 0x31, 0x86, 0x06, 0x18, 0x61, 0x81, 0x86, 0x18, + 0x60, 0x71, 0x87, 0x18, 0x0C, 0x40, 0xC6, 0x03, 0x30, 0x31, 0x00, 0xCC, + 0x0C, 0xC0, 0x33, 0x01, 0xB0, 0x0D, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, + 0xD8, 0x06, 0xC0, 0x34, 0x00, 0xF0, 0x07, 0x00, 0x3C, 0x01, 0xC0, 0x0E, + 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0x00, 0xFF, 0x18, 0x00, 0x18, 0x0C, 0x00, + 0x38, 0x0E, 0x00, 0x70, 0x07, 0x00, 0x60, 0x03, 0x00, 0xC0, 0x01, 0x81, + 0x80, 0x01, 0xC3, 0x80, 0x00, 0xE7, 0x00, 0x00, 0x76, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x66, + 0x00, 0x00, 0xC3, 0x00, 0x01, 0x81, 0x80, 0x03, 0x81, 0xC0, 0x07, 0x00, + 0xE0, 0x06, 0x00, 0x60, 0x0C, 0x00, 0x30, 0x18, 0x00, 0x18, 0x38, 0x00, + 0x1C, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0x18, 0x00, 0x18, 0x0C, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x06, 0x00, + 0x60, 0x03, 0x00, 0xC0, 0x03, 0x81, 0xC0, 0x01, 0x81, 0x80, 0x00, 0xC3, + 0x00, 0x00, 0xE7, 0x00, 0x00, 0x66, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x07, 0xFF, 0xE0, 0x07, 0xFF, + 0xE0, 0x7F, 0xFF, 0x9F, 0xFF, 0xE6, 0x00, 0x19, 0x80, 0x0C, 0x60, 0x07, + 0x18, 0x03, 0x86, 0x00, 0xC1, 0x80, 0x70, 0x00, 0x38, 0x00, 0x0C, 0x00, + 0x07, 0x00, 0x03, 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x38, 0x00, 0x1C, + 0x00, 0x06, 0x00, 0x03, 0x80, 0x31, 0xC0, 0x0C, 0x60, 0x03, 0x30, 0x00, + 0xDC, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, + 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, + 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFC, 0xC0, 0x00, 0x30, 0x00, 0x06, 0x00, + 0x01, 0x80, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, + 0x0C, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, + 0xC0, 0x00, 0x18, 0x00, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x06, + 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, 0x80, 0x00, 0x60, + 0x00, 0x1C, 0x00, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x18, 0x00, 0x07, 0x00, + 0x00, 0xC0, 0x00, 0x30, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, + 0x0C, 0xFF, 0xFC, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, + 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, + 0x18, 0x30, 0x60, 0xC1, 0x83, 0xFF, 0xFC, 0x00, 0x40, 0x00, 0x30, 0x00, + 0x1E, 0x00, 0x0E, 0xC0, 0x07, 0x38, 0x01, 0x87, 0x00, 0xC0, 0xC0, 0x60, + 0x18, 0x38, 0x03, 0x1C, 0x00, 0xE6, 0x00, 0x1F, 0x00, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xE0, 0x70, 0x3C, 0x0E, 0x07, 0x03, + 0x01, 0xFC, 0x00, 0x7F, 0xFC, 0x01, 0xC0, 0x3C, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x60, 0x0F, 0xF9, 0x81, 0xFF, 0xFE, 0x0F, 0x80, 0x38, 0x70, 0x00, 0x63, + 0x80, 0x01, 0x8C, 0x00, 0x06, 0x30, 0x00, 0x18, 0xC0, 0x00, 0xE3, 0x00, + 0x07, 0x86, 0x00, 0x76, 0x1E, 0x07, 0x9F, 0x3F, 0xF8, 0x7C, 0x3F, 0x80, + 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, + 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x1F, 0x80, + 0x18, 0xFF, 0xC0, 0x33, 0x81, 0xC0, 0x6E, 0x01, 0xC0, 0xF0, 0x00, 0xC1, + 0xE0, 0x01, 0xC3, 0x80, 0x01, 0x87, 0x00, 0x03, 0x8C, 0x00, 0x03, 0x18, + 0x00, 0x06, 0x30, 0x00, 0x0C, 0x60, 0x00, 0x18, 0xC0, 0x00, 0x31, 0x80, + 0x00, 0x63, 0x80, 0x01, 0x87, 0x00, 0x03, 0x0F, 0x00, 0x0E, 0x1F, 0x00, + 0x38, 0x37, 0x00, 0xE3, 0xE7, 0x03, 0x87, 0xC7, 0xFE, 0x00, 0x03, 0xF0, + 0x00, 0x01, 0xFC, 0x00, 0x3F, 0xF9, 0x83, 0xC0, 0xFC, 0x38, 0x01, 0xE3, + 0x00, 0x07, 0x38, 0x00, 0x19, 0x80, 0x00, 0xDC, 0x00, 0x06, 0xC0, 0x00, + 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x60, + 0x00, 0x03, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x00, 0x01, 0x80, 0x00, + 0xC7, 0x00, 0x1E, 0x1E, 0x03, 0xC0, 0x7F, 0xFC, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x01, 0xF8, 0x18, 0x07, + 0xFE, 0x18, 0x0F, 0x07, 0x98, 0x1C, 0x01, 0xD8, 0x38, 0x00, 0xF8, 0x70, + 0x00, 0x78, 0x60, 0x00, 0x38, 0xE0, 0x00, 0x38, 0xC0, 0x00, 0x18, 0xC0, + 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, + 0x00, 0x18, 0x60, 0x00, 0x38, 0x60, 0x00, 0x38, 0x70, 0x00, 0x78, 0x38, + 0x00, 0xD8, 0x1C, 0x01, 0xD8, 0x0F, 0x07, 0x9F, 0x07, 0xFE, 0x1F, 0x01, + 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x3F, 0xF8, 0x07, 0x80, 0xF0, 0x70, 0x01, + 0xC3, 0x00, 0x07, 0x30, 0x00, 0x19, 0x80, 0x00, 0x78, 0x00, 0x03, 0xC0, + 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x0C, 0x00, 0x00, + 0x60, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x01, 0xC0, + 0x00, 0xC7, 0x00, 0x0E, 0x1E, 0x03, 0xE0, 0x3F, 0xFC, 0x00, 0x7F, 0x00, + 0x00, 0x7F, 0xC0, 0x3F, 0xFC, 0x0E, 0x00, 0x03, 0x80, 0x00, 0x60, 0x00, + 0x0C, 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0xFF, 0xFF, 0x9F, 0xFF, 0xF0, + 0x18, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, + 0x30, 0x00, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, + 0x60, 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, + 0xC0, 0x03, 0xFF, 0xFC, 0x7F, 0xFF, 0x80, 0x01, 0xF8, 0x00, 0x0F, 0xFC, + 0x7C, 0x38, 0x1C, 0xF8, 0xE0, 0x0D, 0x83, 0x00, 0x0F, 0x0E, 0x00, 0x1E, + 0x18, 0x00, 0x1C, 0x70, 0x00, 0x38, 0xC0, 0x00, 0x31, 0x80, 0x00, 0x63, + 0x00, 0x00, 0xC6, 0x00, 0x01, 0x8C, 0x00, 0x03, 0x18, 0x00, 0x06, 0x18, + 0x00, 0x1C, 0x30, 0x00, 0x38, 0x30, 0x00, 0xF0, 0x70, 0x03, 0x60, 0x78, + 0x1C, 0xC0, 0x3F, 0xF1, 0x80, 0x1F, 0x83, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, + 0x80, 0x00, 0x0E, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xC0, 0x00, 0xF8, 0x00, + 0x01, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, + 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x3F, 0x00, 0x18, 0xFF, 0x80, + 0x37, 0x03, 0x80, 0x7C, 0x03, 0x80, 0xF0, 0x03, 0x81, 0xC0, 0x03, 0x03, + 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x30, 0x30, + 0x00, 0x60, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, 0x03, 0x03, 0x00, + 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x30, 0x30, 0x00, + 0x63, 0xFC, 0x07, 0xFF, 0xF8, 0x0F, 0xF0, 0x01, 0xC0, 0x00, 0x70, 0x00, + 0x1C, 0x00, 0x07, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x03, 0xFC, 0x00, 0x03, 0x00, 0x00, 0xC0, + 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, + 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, + 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0x00, 0x0C, + 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, + 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, + 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, + 0x70, 0x03, 0x80, 0x1C, 0xFF, 0xE3, 0xFF, 0x00, 0xF8, 0x00, 0x03, 0xE0, + 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, + 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x18, 0x1F, 0xE0, 0x60, 0x7F, 0x81, + 0x80, 0x60, 0x06, 0x07, 0x00, 0x18, 0x38, 0x00, 0x61, 0xC0, 0x01, 0x8E, + 0x00, 0x06, 0x70, 0x00, 0x1B, 0x80, 0x00, 0x7F, 0x00, 0x01, 0xCE, 0x00, + 0x06, 0x1C, 0x00, 0x18, 0x38, 0x00, 0x60, 0x70, 0x01, 0x80, 0xE0, 0x06, + 0x01, 0xC0, 0x18, 0x03, 0x80, 0x60, 0x07, 0x0F, 0x80, 0x7F, 0xFE, 0x01, + 0xFF, 0x3F, 0xC0, 0x0F, 0xF0, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, + 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, + 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, + 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, + 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0x3C, 0x0F, 0x9F, 0x87, 0xE0, 0xFB, + 0x1C, 0xC7, 0x01, 0xE0, 0xD8, 0x38, 0x1C, 0x07, 0x01, 0x81, 0x80, 0x60, + 0x18, 0x18, 0x06, 0x01, 0x81, 0x80, 0x60, 0x18, 0x18, 0x06, 0x01, 0x81, + 0x80, 0x60, 0x18, 0x18, 0x06, 0x01, 0x81, 0x80, 0x60, 0x18, 0x18, 0x06, + 0x01, 0x81, 0x80, 0x60, 0x18, 0x18, 0x06, 0x01, 0x81, 0x80, 0x60, 0x18, + 0x18, 0x06, 0x01, 0x81, 0x80, 0x60, 0x18, 0x18, 0x06, 0x01, 0x8F, 0xE0, + 0x7C, 0x1F, 0xFE, 0x07, 0xC1, 0xF0, 0x00, 0x1F, 0x00, 0xF8, 0xFF, 0x81, + 0xF3, 0x83, 0x80, 0x6C, 0x03, 0x80, 0xF0, 0x03, 0x81, 0xC0, 0x03, 0x03, + 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x30, 0x30, + 0x00, 0x60, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, 0x03, 0x03, 0x00, + 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x30, 0x30, 0x00, + 0x67, 0xFC, 0x03, 0xFF, 0xF8, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0xFE, + 0x00, 0xF0, 0x3C, 0x07, 0x00, 0x38, 0x38, 0x00, 0x71, 0xC0, 0x00, 0xE6, + 0x00, 0x01, 0x98, 0x00, 0x06, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x36, 0x00, 0x01, + 0x98, 0x00, 0x06, 0x70, 0x00, 0x38, 0xE0, 0x01, 0xC1, 0xC0, 0x0E, 0x03, + 0xC0, 0xF0, 0x07, 0xFF, 0x80, 0x03, 0xF0, 0x00, 0x00, 0x3F, 0x01, 0xF1, + 0xFF, 0x83, 0xE7, 0x03, 0x80, 0xD8, 0x01, 0x81, 0xE0, 0x01, 0x83, 0xC0, + 0x03, 0x87, 0x00, 0x03, 0x0E, 0x00, 0x07, 0x18, 0x00, 0x06, 0x30, 0x00, + 0x0C, 0x60, 0x00, 0x18, 0xC0, 0x00, 0x31, 0x80, 0x00, 0x63, 0x00, 0x00, + 0xC7, 0x00, 0x03, 0x0E, 0x00, 0x06, 0x1E, 0x00, 0x18, 0x36, 0x00, 0x70, + 0x67, 0x03, 0xC0, 0xC7, 0xFE, 0x01, 0x83, 0xF0, 0x03, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, + 0x00, 0x00, 0xC0, 0x00, 0x0F, 0xFC, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x01, + 0xF8, 0x00, 0x07, 0xFF, 0x1F, 0x0F, 0x07, 0x9F, 0x1C, 0x01, 0xD8, 0x38, + 0x00, 0x78, 0x70, 0x00, 0x78, 0x60, 0x00, 0x38, 0xE0, 0x00, 0x38, 0xC0, + 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, + 0x00, 0x18, 0xC0, 0x00, 0x18, 0x60, 0x00, 0x38, 0x70, 0x00, 0x78, 0x30, + 0x00, 0x78, 0x1C, 0x01, 0xD8, 0x0F, 0x07, 0x98, 0x07, 0xFF, 0x18, 0x01, + 0xFC, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x03, 0xFF, 0x00, 0x03, 0xFF, 0x7E, 0x03, 0xC3, 0xF0, 0x7F, 0x81, 0x8F, + 0x0E, 0x0C, 0xE0, 0x00, 0x7E, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x00, + 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, + 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, + 0x00, 0x00, 0x30, 0x00, 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x00, 0x07, 0xF0, + 0x07, 0xFF, 0x63, 0xC0, 0xF9, 0xC0, 0x0E, 0x60, 0x01, 0x98, 0x00, 0x66, + 0x00, 0x19, 0xC0, 0x00, 0x38, 0x00, 0x07, 0xC0, 0x00, 0x7F, 0xC0, 0x00, + 0x7C, 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, + 0xF8, 0x00, 0x7F, 0x00, 0x3B, 0xF0, 0x3C, 0xDF, 0xFE, 0x00, 0xFE, 0x00, + 0x0C, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, + 0x00, 0x06, 0x00, 0x03, 0xFF, 0xFE, 0x1F, 0xFF, 0xF0, 0x0C, 0x00, 0x00, + 0x60, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x00, + 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x60, 0x00, 0x03, + 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, + 0x00, 0xC0, 0x07, 0x07, 0x01, 0xF0, 0x1F, 0xFF, 0x00, 0x3F, 0x80, 0xF8, + 0x03, 0xF1, 0xF0, 0x07, 0xE0, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, + 0x03, 0x03, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, + 0x30, 0x30, 0x00, 0x60, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, 0x03, + 0x03, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x38, 0x18, 0x00, 0xF0, + 0x18, 0x03, 0x60, 0x38, 0x3C, 0xF8, 0x3F, 0xF1, 0xF0, 0x1F, 0x00, 0x00, + 0x7F, 0xC0, 0xFF, 0xDF, 0xF0, 0x3F, 0xF0, 0xC0, 0x00, 0xC0, 0x30, 0x00, + 0x30, 0x06, 0x00, 0x1C, 0x01, 0x80, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0C, + 0x00, 0xC0, 0x03, 0x80, 0x30, 0x00, 0x60, 0x18, 0x00, 0x18, 0x06, 0x00, + 0x03, 0x03, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x18, 0x30, 0x00, 0x06, 0x18, + 0x00, 0x00, 0xC6, 0x00, 0x00, 0x33, 0x00, 0x00, 0x0E, 0xC0, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x78, 0x00, 0x7F, 0x00, 0x3F, 0xDF, 0xC0, 0x0F, 0xF1, + 0x80, 0x00, 0x20, 0x60, 0x00, 0x18, 0x18, 0x00, 0x06, 0x06, 0x03, 0x01, + 0x80, 0x81, 0xE0, 0x60, 0x30, 0x78, 0x10, 0x0C, 0x1E, 0x0C, 0x03, 0x0C, + 0xC3, 0x00, 0xC3, 0x30, 0xC0, 0x10, 0xCC, 0x30, 0x06, 0x61, 0x98, 0x01, + 0x98, 0x66, 0x00, 0x66, 0x19, 0x80, 0x0B, 0x03, 0x60, 0x03, 0xC0, 0xD0, + 0x00, 0xF0, 0x1C, 0x00, 0x38, 0x07, 0x00, 0x0E, 0x01, 0xC0, 0x3F, 0x81, + 0xFE, 0x3F, 0x81, 0xFE, 0x0C, 0x00, 0x38, 0x06, 0x00, 0x70, 0x03, 0x00, + 0xE0, 0x01, 0x81, 0xC0, 0x00, 0xC3, 0x80, 0x00, 0x67, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x67, 0x00, 0x00, 0xC3, + 0x80, 0x01, 0x81, 0xC0, 0x03, 0x00, 0xE0, 0x06, 0x00, 0x70, 0x0C, 0x00, + 0x38, 0x18, 0x00, 0x1C, 0x7F, 0x81, 0xFF, 0x7F, 0x81, 0xFF, 0x7F, 0x00, + 0xFF, 0x7F, 0x00, 0xFF, 0x18, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x0C, 0x00, + 0x18, 0x0C, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x60, 0x03, 0x00, + 0x60, 0x03, 0x00, 0xC0, 0x01, 0x80, 0xC0, 0x01, 0x81, 0x80, 0x00, 0xC1, + 0x80, 0x00, 0xC3, 0x00, 0x00, 0x63, 0x00, 0x00, 0x66, 0x00, 0x00, 0x36, + 0x00, 0x00, 0x34, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFC, + 0x00, 0xFF, 0xFF, 0x7F, 0xFF, 0xB0, 0x01, 0x98, 0x01, 0xCC, 0x01, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xE0, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x03, 0x70, + 0x01, 0xB0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xE0, 0x7C, 0x0C, + 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, + 0x00, 0x60, 0x0C, 0x03, 0x00, 0xE0, 0xF0, 0x1E, 0x00, 0x70, 0x06, 0x00, + 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, + 0x0C, 0x01, 0x80, 0x18, 0x03, 0xE0, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0xE0, 0x1F, 0x00, 0x60, 0x06, 0x00, 0xC0, 0x18, + 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x01, + 0x80, 0x38, 0x01, 0xE0, 0x3C, 0x1C, 0x03, 0x00, 0xC0, 0x18, 0x03, 0x00, + 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0xC0, + 0xF8, 0x1C, 0x00, 0x0F, 0x00, 0x03, 0xFC, 0x03, 0x70, 0xE0, 0x76, 0x07, + 0x8E, 0xC0, 0x1F, 0xC0, 0x00, 0xF0}; + +const GFXglyph FreeMono24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 28, 0, 1}, // 0x20 ' ' + {0, 5, 30, 28, 11, -28}, // 0x21 '!' + {19, 16, 14, 28, 6, -28}, // 0x22 '"' + {47, 19, 32, 28, 4, -29}, // 0x23 '#' + {123, 18, 33, 28, 5, -29}, // 0x24 '$' + {198, 20, 29, 28, 4, -27}, // 0x25 '%' + {271, 18, 25, 28, 5, -23}, // 0x26 '&' + {328, 7, 14, 28, 11, -28}, // 0x27 ''' + {341, 7, 34, 28, 14, -27}, // 0x28 '(' + {371, 7, 34, 28, 8, -27}, // 0x29 ')' + {401, 18, 16, 28, 5, -27}, // 0x2A '*' + {437, 20, 22, 28, 4, -23}, // 0x2B '+' + {492, 9, 14, 28, 6, -6}, // 0x2C ',' + {508, 22, 2, 28, 3, -13}, // 0x2D '-' + {514, 7, 6, 28, 11, -4}, // 0x2E '.' + {520, 18, 35, 28, 5, -30}, // 0x2F '/' + {599, 18, 30, 28, 5, -28}, // 0x30 '0' + {667, 16, 29, 28, 6, -28}, // 0x31 '1' + {725, 18, 29, 28, 5, -28}, // 0x32 '2' + {791, 19, 30, 28, 5, -28}, // 0x33 '3' + {863, 16, 28, 28, 6, -27}, // 0x34 '4' + {919, 19, 29, 28, 5, -27}, // 0x35 '5' + {988, 18, 30, 28, 6, -28}, // 0x36 '6' + {1056, 18, 28, 28, 5, -27}, // 0x37 '7' + {1119, 18, 30, 28, 5, -28}, // 0x38 '8' + {1187, 18, 30, 28, 6, -28}, // 0x39 '9' + {1255, 7, 21, 28, 11, -19}, // 0x3A ':' + {1274, 10, 27, 28, 7, -19}, // 0x3B ';' + {1308, 22, 22, 28, 3, -23}, // 0x3C '<' + {1369, 24, 9, 28, 2, -17}, // 0x3D '=' + {1396, 21, 22, 28, 4, -23}, // 0x3E '>' + {1454, 17, 28, 28, 6, -26}, // 0x3F '?' + {1514, 18, 32, 28, 5, -28}, // 0x40 '@' + {1586, 28, 26, 28, 0, -25}, // 0x41 'A' + {1677, 22, 26, 28, 3, -25}, // 0x42 'B' + {1749, 22, 28, 28, 3, -26}, // 0x43 'C' + {1826, 22, 26, 28, 3, -25}, // 0x44 'D' + {1898, 22, 26, 28, 3, -25}, // 0x45 'E' + {1970, 22, 26, 28, 3, -25}, // 0x46 'F' + {2042, 23, 28, 28, 3, -26}, // 0x47 'G' + {2123, 23, 26, 28, 3, -25}, // 0x48 'H' + {2198, 16, 26, 28, 6, -25}, // 0x49 'I' + {2250, 23, 27, 28, 4, -25}, // 0x4A 'J' + {2328, 24, 26, 28, 3, -25}, // 0x4B 'K' + {2406, 21, 26, 28, 4, -25}, // 0x4C 'L' + {2475, 26, 26, 28, 1, -25}, // 0x4D 'M' + {2560, 24, 26, 28, 2, -25}, // 0x4E 'N' + {2638, 24, 28, 28, 2, -26}, // 0x4F 'O' + {2722, 21, 26, 28, 3, -25}, // 0x50 'P' + {2791, 24, 32, 28, 2, -26}, // 0x51 'Q' + {2887, 24, 26, 28, 3, -25}, // 0x52 'R' + {2965, 20, 28, 28, 4, -26}, // 0x53 'S' + {3035, 22, 26, 28, 3, -25}, // 0x54 'T' + {3107, 23, 27, 28, 3, -25}, // 0x55 'U' + {3185, 28, 26, 28, 0, -25}, // 0x56 'V' + {3276, 26, 26, 28, 1, -25}, // 0x57 'W' + {3361, 24, 26, 28, 2, -25}, // 0x58 'X' + {3439, 24, 26, 28, 2, -25}, // 0x59 'Y' + {3517, 18, 26, 28, 5, -25}, // 0x5A 'Z' + {3576, 7, 34, 28, 13, -27}, // 0x5B '[' + {3606, 18, 35, 28, 5, -30}, // 0x5C '\' + {3685, 7, 34, 28, 8, -27}, // 0x5D ']' + {3715, 18, 12, 28, 5, -28}, // 0x5E '^' + {3742, 28, 2, 28, 0, 5}, // 0x5F '_' + {3749, 8, 7, 28, 7, -29}, // 0x60 '`' + {3756, 22, 22, 28, 3, -20}, // 0x61 'a' + {3817, 23, 29, 28, 2, -27}, // 0x62 'b' + {3901, 21, 22, 28, 4, -20}, // 0x63 'c' + {3959, 24, 29, 28, 3, -27}, // 0x64 'd' + {4046, 21, 22, 28, 3, -20}, // 0x65 'e' + {4104, 19, 28, 28, 6, -27}, // 0x66 'f' + {4171, 23, 30, 28, 3, -20}, // 0x67 'g' + {4258, 23, 28, 28, 3, -27}, // 0x68 'h' + {4339, 18, 29, 28, 5, -28}, // 0x69 'i' + {4405, 14, 38, 28, 6, -28}, // 0x6A 'j' + {4472, 22, 28, 28, 4, -27}, // 0x6B 'k' + {4549, 18, 28, 28, 5, -27}, // 0x6C 'l' + {4612, 28, 21, 28, 0, -20}, // 0x6D 'm' + {4686, 23, 21, 28, 2, -20}, // 0x6E 'n' + {4747, 22, 22, 28, 3, -20}, // 0x6F 'o' + {4808, 23, 30, 28, 2, -20}, // 0x70 'p' + {4895, 24, 30, 28, 3, -20}, // 0x71 'q' + {4985, 21, 20, 28, 5, -19}, // 0x72 'r' + {5038, 18, 22, 28, 5, -20}, // 0x73 's' + {5088, 21, 27, 28, 3, -25}, // 0x74 't' + {5159, 23, 21, 28, 3, -19}, // 0x75 'u' + {5220, 26, 20, 28, 1, -19}, // 0x76 'v' + {5285, 26, 20, 28, 1, -19}, // 0x77 'w' + {5350, 24, 20, 28, 2, -19}, // 0x78 'x' + {5410, 24, 29, 28, 2, -19}, // 0x79 'y' + {5497, 17, 20, 28, 6, -19}, // 0x7A 'z' + {5540, 11, 34, 28, 8, -27}, // 0x7B '{' + {5587, 2, 34, 28, 13, -27}, // 0x7C '|' + {5596, 11, 34, 28, 9, -27}, // 0x7D '}' + {5643, 20, 6, 28, 4, -15}}; // 0x7E '~' + +const GFXfont FreeMono24pt7b PROGMEM = {(uint8_t *)FreeMono24pt7bBitmaps, + (GFXglyph *)FreeMono24pt7bGlyphs, 0x20, + 0x7E, 47}; + +// Approx. 6330 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMono9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMono9pt7b.h new file mode 100644 index 0000000..a3034eb --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMono9pt7b.h @@ -0,0 +1,175 @@ +const uint8_t FreeMono9pt7bBitmaps[] PROGMEM = { + 0xAA, 0xA8, 0x0C, 0xED, 0x24, 0x92, 0x48, 0x24, 0x48, 0x91, 0x2F, 0xE4, + 0x89, 0x7F, 0x28, 0x51, 0x22, 0x40, 0x08, 0x3E, 0x62, 0x40, 0x30, 0x0E, + 0x01, 0x81, 0xC3, 0xBE, 0x08, 0x08, 0x71, 0x12, 0x23, 0x80, 0x23, 0xB8, + 0x0E, 0x22, 0x44, 0x70, 0x38, 0x81, 0x02, 0x06, 0x1A, 0x65, 0x46, 0xC8, + 0xEC, 0xE9, 0x24, 0x5A, 0xAA, 0xA9, 0x40, 0xA9, 0x55, 0x5A, 0x80, 0x10, + 0x22, 0x4B, 0xE3, 0x05, 0x11, 0x00, 0x10, 0x20, 0x47, 0xF1, 0x02, 0x04, + 0x00, 0x6B, 0x48, 0xFF, 0x00, 0xF0, 0x02, 0x08, 0x10, 0x60, 0x81, 0x04, + 0x08, 0x20, 0x41, 0x02, 0x08, 0x00, 0x38, 0x8A, 0x0C, 0x18, 0x30, 0x60, + 0xC1, 0x82, 0x88, 0xE0, 0x27, 0x28, 0x42, 0x10, 0x84, 0x21, 0x3E, 0x38, + 0x8A, 0x08, 0x10, 0x20, 0x82, 0x08, 0x61, 0x03, 0xF8, 0x7C, 0x06, 0x02, + 0x02, 0x1C, 0x06, 0x01, 0x01, 0x01, 0x42, 0x3C, 0x18, 0xA2, 0x92, 0x8A, + 0x28, 0xBF, 0x08, 0x21, 0xC0, 0x7C, 0x81, 0x03, 0xE4, 0x40, 0x40, 0x81, + 0x03, 0x88, 0xE0, 0x1E, 0x41, 0x04, 0x0B, 0x98, 0xB0, 0xC1, 0xC2, 0x88, + 0xE0, 0xFE, 0x04, 0x08, 0x20, 0x40, 0x82, 0x04, 0x08, 0x20, 0x40, 0x38, + 0x8A, 0x0C, 0x14, 0x47, 0x11, 0x41, 0x83, 0x8C, 0xE0, 0x38, 0x8A, 0x1C, + 0x18, 0x68, 0xCE, 0x81, 0x04, 0x13, 0xC0, 0xF0, 0x0F, 0x6C, 0x00, 0xD2, + 0xD2, 0x00, 0x03, 0x04, 0x18, 0x60, 0x60, 0x18, 0x04, 0x03, 0xFF, 0x80, + 0x00, 0x1F, 0xF0, 0x40, 0x18, 0x03, 0x00, 0x60, 0x20, 0x60, 0xC0, 0x80, + 0x3D, 0x84, 0x08, 0x30, 0xC2, 0x00, 0x00, 0x00, 0x30, 0x3C, 0x46, 0x82, + 0x8E, 0xB2, 0xA2, 0xA2, 0x9F, 0x80, 0x80, 0x40, 0x3C, 0x3C, 0x01, 0x40, + 0x28, 0x09, 0x01, 0x10, 0x42, 0x0F, 0xC1, 0x04, 0x40, 0x9E, 0x3C, 0xFE, + 0x21, 0x90, 0x48, 0x67, 0xE2, 0x09, 0x02, 0x81, 0x41, 0xFF, 0x80, 0x3E, + 0xB0, 0xF0, 0x30, 0x08, 0x04, 0x02, 0x00, 0x80, 0x60, 0x8F, 0x80, 0xFE, + 0x21, 0x90, 0x68, 0x14, 0x0A, 0x05, 0x02, 0x83, 0x43, 0x7F, 0x00, 0xFF, + 0x20, 0x90, 0x08, 0x87, 0xC2, 0x21, 0x00, 0x81, 0x40, 0xFF, 0xC0, 0xFF, + 0xA0, 0x50, 0x08, 0x87, 0xC2, 0x21, 0x00, 0x80, 0x40, 0x78, 0x00, 0x1E, + 0x98, 0x6C, 0x0A, 0x00, 0x80, 0x20, 0xF8, 0x0B, 0x02, 0x60, 0x87, 0xC0, + 0xE3, 0xA0, 0x90, 0x48, 0x27, 0xF2, 0x09, 0x04, 0x82, 0x41, 0x71, 0xC0, + 0xF9, 0x08, 0x42, 0x10, 0x84, 0x27, 0xC0, 0x1F, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x82, 0x82, 0xC6, 0x78, 0xE3, 0xA1, 0x11, 0x09, 0x05, 0x83, 0x21, + 0x08, 0x84, 0x41, 0x70, 0xC0, 0xE0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, + 0x41, 0x41, 0xFF, 0xE0, 0xEC, 0x19, 0x45, 0x28, 0xA4, 0xA4, 0x94, 0x91, + 0x12, 0x02, 0x40, 0x5C, 0x1C, 0xC3, 0xB0, 0x94, 0x4A, 0x24, 0x92, 0x49, + 0x14, 0x8A, 0x43, 0x70, 0x80, 0x1E, 0x31, 0x90, 0x50, 0x18, 0x0C, 0x06, + 0x02, 0x82, 0x63, 0x0F, 0x00, 0xFE, 0x43, 0x41, 0x41, 0x42, 0x7C, 0x40, + 0x40, 0x40, 0xF0, 0x1C, 0x31, 0x90, 0x50, 0x18, 0x0C, 0x06, 0x02, 0x82, + 0x63, 0x1F, 0x04, 0x07, 0x92, 0x30, 0xFE, 0x21, 0x90, 0x48, 0x24, 0x23, + 0xE1, 0x10, 0x84, 0x41, 0x70, 0xC0, 0x3A, 0xCD, 0x0A, 0x03, 0x01, 0x80, + 0xC1, 0xC7, 0x78, 0xFF, 0xC4, 0x62, 0x21, 0x00, 0x80, 0x40, 0x20, 0x10, + 0x08, 0x1F, 0x00, 0xE3, 0xA0, 0x90, 0x48, 0x24, 0x12, 0x09, 0x04, 0x82, + 0x22, 0x0E, 0x00, 0xF1, 0xE8, 0x10, 0x82, 0x10, 0x42, 0x10, 0x22, 0x04, + 0x80, 0x50, 0x0C, 0x00, 0x80, 0xF1, 0xE8, 0x09, 0x11, 0x25, 0x44, 0xA8, + 0x55, 0x0C, 0xA1, 0x8C, 0x31, 0x84, 0x30, 0xE3, 0xA0, 0x88, 0x82, 0x80, + 0x80, 0xC0, 0x90, 0x44, 0x41, 0x71, 0xC0, 0xE3, 0xA0, 0x88, 0x82, 0x81, + 0x40, 0x40, 0x20, 0x10, 0x08, 0x1F, 0x00, 0xFD, 0x0A, 0x20, 0x81, 0x04, + 0x10, 0x21, 0x83, 0xFC, 0xEA, 0xAA, 0xAA, 0xC0, 0x80, 0x81, 0x03, 0x02, + 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0xD5, 0x55, 0x55, 0xC0, + 0x10, 0x51, 0x22, 0x28, 0x20, 0xFF, 0xE0, 0x88, 0x80, 0x7E, 0x00, 0x80, + 0x47, 0xEC, 0x14, 0x0A, 0x0C, 0xFB, 0xC0, 0x20, 0x10, 0x0B, 0xC6, 0x12, + 0x05, 0x02, 0x81, 0x40, 0xB0, 0xB7, 0x80, 0x3A, 0x8E, 0x0C, 0x08, 0x10, + 0x10, 0x9E, 0x03, 0x00, 0x80, 0x47, 0xA4, 0x34, 0x0A, 0x05, 0x02, 0x81, + 0x21, 0x8F, 0x60, 0x3C, 0x43, 0x81, 0xFF, 0x80, 0x80, 0x61, 0x3E, 0x3D, + 0x04, 0x3E, 0x41, 0x04, 0x10, 0x41, 0x0F, 0x80, 0x3D, 0xA1, 0xA0, 0x50, + 0x28, 0x14, 0x09, 0x0C, 0x7A, 0x01, 0x01, 0x87, 0x80, 0xC0, 0x20, 0x10, + 0x0B, 0xC6, 0x32, 0x09, 0x04, 0x82, 0x41, 0x20, 0xB8, 0xE0, 0x10, 0x01, + 0xC0, 0x81, 0x02, 0x04, 0x08, 0x11, 0xFC, 0x10, 0x3E, 0x10, 0x84, 0x21, + 0x08, 0x42, 0x3F, 0x00, 0xC0, 0x40, 0x40, 0x4F, 0x44, 0x58, 0x70, 0x48, + 0x44, 0x42, 0xC7, 0x70, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, 0x23, + 0xF8, 0xB7, 0x64, 0x62, 0x31, 0x18, 0x8C, 0x46, 0x23, 0x91, 0x5E, 0x31, + 0x90, 0x48, 0x24, 0x12, 0x09, 0x05, 0xC7, 0x3E, 0x31, 0xA0, 0x30, 0x18, + 0x0C, 0x05, 0x8C, 0x7C, 0xDE, 0x30, 0x90, 0x28, 0x14, 0x0A, 0x05, 0x84, + 0xBC, 0x40, 0x20, 0x38, 0x00, 0x3D, 0xA1, 0xA0, 0x50, 0x28, 0x14, 0x09, + 0x0C, 0x7A, 0x01, 0x00, 0x80, 0xE0, 0xCE, 0xA1, 0x82, 0x04, 0x08, 0x10, + 0x7C, 0x3A, 0x8D, 0x0B, 0x80, 0xF0, 0x70, 0xDE, 0x40, 0x40, 0xFC, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x41, 0x3E, 0xC3, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x43, 0x3D, 0xE3, 0xA0, 0x90, 0x84, 0x42, 0x20, 0xA0, 0x50, 0x10, 0xE3, + 0xC0, 0x92, 0x4B, 0x25, 0x92, 0xA9, 0x98, 0x44, 0xE3, 0x31, 0x05, 0x01, + 0x01, 0x41, 0x11, 0x05, 0xC7, 0xE3, 0xA0, 0x90, 0x84, 0x42, 0x40, 0xA0, + 0x60, 0x10, 0x10, 0x08, 0x3E, 0x00, 0xFD, 0x08, 0x20, 0x82, 0x08, 0x10, + 0xBF, 0x29, 0x24, 0xA2, 0x49, 0x26, 0xFF, 0xF8, 0x89, 0x24, 0x8A, 0x49, + 0x2C, 0x61, 0x24, 0x30}; + +const GFXglyph FreeMono9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 11, 0, 1}, // 0x20 ' ' + {0, 2, 11, 11, 4, -10}, // 0x21 '!' + {3, 6, 5, 11, 2, -10}, // 0x22 '"' + {7, 7, 12, 11, 2, -10}, // 0x23 '#' + {18, 8, 12, 11, 1, -10}, // 0x24 '$' + {30, 7, 11, 11, 2, -10}, // 0x25 '%' + {40, 7, 10, 11, 2, -9}, // 0x26 '&' + {49, 3, 5, 11, 4, -10}, // 0x27 ''' + {51, 2, 13, 11, 5, -10}, // 0x28 '(' + {55, 2, 13, 11, 4, -10}, // 0x29 ')' + {59, 7, 7, 11, 2, -10}, // 0x2A '*' + {66, 7, 7, 11, 2, -8}, // 0x2B '+' + {73, 3, 5, 11, 2, -1}, // 0x2C ',' + {75, 9, 1, 11, 1, -5}, // 0x2D '-' + {77, 2, 2, 11, 4, -1}, // 0x2E '.' + {78, 7, 13, 11, 2, -11}, // 0x2F '/' + {90, 7, 11, 11, 2, -10}, // 0x30 '0' + {100, 5, 11, 11, 3, -10}, // 0x31 '1' + {107, 7, 11, 11, 2, -10}, // 0x32 '2' + {117, 8, 11, 11, 1, -10}, // 0x33 '3' + {128, 6, 11, 11, 3, -10}, // 0x34 '4' + {137, 7, 11, 11, 2, -10}, // 0x35 '5' + {147, 7, 11, 11, 2, -10}, // 0x36 '6' + {157, 7, 11, 11, 2, -10}, // 0x37 '7' + {167, 7, 11, 11, 2, -10}, // 0x38 '8' + {177, 7, 11, 11, 2, -10}, // 0x39 '9' + {187, 2, 8, 11, 4, -7}, // 0x3A ':' + {189, 3, 11, 11, 3, -7}, // 0x3B ';' + {194, 8, 8, 11, 1, -8}, // 0x3C '<' + {202, 9, 4, 11, 1, -6}, // 0x3D '=' + {207, 9, 8, 11, 1, -8}, // 0x3E '>' + {216, 7, 10, 11, 2, -9}, // 0x3F '?' + {225, 8, 12, 11, 2, -10}, // 0x40 '@' + {237, 11, 10, 11, 0, -9}, // 0x41 'A' + {251, 9, 10, 11, 1, -9}, // 0x42 'B' + {263, 9, 10, 11, 1, -9}, // 0x43 'C' + {275, 9, 10, 11, 1, -9}, // 0x44 'D' + {287, 9, 10, 11, 1, -9}, // 0x45 'E' + {299, 9, 10, 11, 1, -9}, // 0x46 'F' + {311, 10, 10, 11, 1, -9}, // 0x47 'G' + {324, 9, 10, 11, 1, -9}, // 0x48 'H' + {336, 5, 10, 11, 3, -9}, // 0x49 'I' + {343, 8, 10, 11, 2, -9}, // 0x4A 'J' + {353, 9, 10, 11, 1, -9}, // 0x4B 'K' + {365, 8, 10, 11, 2, -9}, // 0x4C 'L' + {375, 11, 10, 11, 0, -9}, // 0x4D 'M' + {389, 9, 10, 11, 1, -9}, // 0x4E 'N' + {401, 9, 10, 11, 1, -9}, // 0x4F 'O' + {413, 8, 10, 11, 1, -9}, // 0x50 'P' + {423, 9, 13, 11, 1, -9}, // 0x51 'Q' + {438, 9, 10, 11, 1, -9}, // 0x52 'R' + {450, 7, 10, 11, 2, -9}, // 0x53 'S' + {459, 9, 10, 11, 1, -9}, // 0x54 'T' + {471, 9, 10, 11, 1, -9}, // 0x55 'U' + {483, 11, 10, 11, 0, -9}, // 0x56 'V' + {497, 11, 10, 11, 0, -9}, // 0x57 'W' + {511, 9, 10, 11, 1, -9}, // 0x58 'X' + {523, 9, 10, 11, 1, -9}, // 0x59 'Y' + {535, 7, 10, 11, 2, -9}, // 0x5A 'Z' + {544, 2, 13, 11, 5, -10}, // 0x5B '[' + {548, 7, 13, 11, 2, -11}, // 0x5C '\' + {560, 2, 13, 11, 4, -10}, // 0x5D ']' + {564, 7, 5, 11, 2, -10}, // 0x5E '^' + {569, 11, 1, 11, 0, 2}, // 0x5F '_' + {571, 3, 3, 11, 3, -11}, // 0x60 '`' + {573, 9, 8, 11, 1, -7}, // 0x61 'a' + {582, 9, 11, 11, 1, -10}, // 0x62 'b' + {595, 7, 8, 11, 2, -7}, // 0x63 'c' + {602, 9, 11, 11, 1, -10}, // 0x64 'd' + {615, 8, 8, 11, 1, -7}, // 0x65 'e' + {623, 6, 11, 11, 3, -10}, // 0x66 'f' + {632, 9, 11, 11, 1, -7}, // 0x67 'g' + {645, 9, 11, 11, 1, -10}, // 0x68 'h' + {658, 7, 10, 11, 2, -9}, // 0x69 'i' + {667, 5, 13, 11, 3, -9}, // 0x6A 'j' + {676, 8, 11, 11, 2, -10}, // 0x6B 'k' + {687, 7, 11, 11, 2, -10}, // 0x6C 'l' + {697, 9, 8, 11, 1, -7}, // 0x6D 'm' + {706, 9, 8, 11, 1, -7}, // 0x6E 'n' + {715, 9, 8, 11, 1, -7}, // 0x6F 'o' + {724, 9, 11, 11, 1, -7}, // 0x70 'p' + {737, 9, 11, 11, 1, -7}, // 0x71 'q' + {750, 7, 8, 11, 3, -7}, // 0x72 'r' + {757, 7, 8, 11, 2, -7}, // 0x73 's' + {764, 8, 10, 11, 2, -9}, // 0x74 't' + {774, 8, 8, 11, 1, -7}, // 0x75 'u' + {782, 9, 8, 11, 1, -7}, // 0x76 'v' + {791, 9, 8, 11, 1, -7}, // 0x77 'w' + {800, 9, 8, 11, 1, -7}, // 0x78 'x' + {809, 9, 11, 11, 1, -7}, // 0x79 'y' + {822, 7, 8, 11, 2, -7}, // 0x7A 'z' + {829, 3, 13, 11, 4, -10}, // 0x7B '{' + {834, 1, 13, 11, 5, -10}, // 0x7C '|' + {836, 3, 13, 11, 4, -10}, // 0x7D '}' + {841, 7, 3, 11, 2, -6}}; // 0x7E '~' + +const GFXfont FreeMono9pt7b PROGMEM = {(uint8_t *)FreeMono9pt7bBitmaps, + (GFXglyph *)FreeMono9pt7bGlyphs, 0x20, + 0x7E, 18}; + +// Approx. 1516 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold12pt7b.h new file mode 100644 index 0000000..9faf351 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold12pt7b.h @@ -0,0 +1,249 @@ +const uint8_t FreeMonoBold12pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xF6, 0x66, 0x60, 0x6F, 0x60, 0xE7, 0xE7, 0x62, 0x42, + 0x42, 0x42, 0x42, 0x11, 0x87, 0x30, 0xC6, 0x18, 0xC3, 0x31, 0xFF, 0xFF, + 0xF9, 0x98, 0x33, 0x06, 0x60, 0xCC, 0x7F, 0xEF, 0xFC, 0x66, 0x0C, 0xC3, + 0x98, 0x63, 0x04, 0x40, 0x0C, 0x03, 0x00, 0xC0, 0xFE, 0x7F, 0x9C, 0x66, + 0x09, 0x80, 0x78, 0x0F, 0xE0, 0x7F, 0x03, 0xE0, 0xF8, 0x7F, 0xFB, 0xFC, + 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x38, 0x1F, 0x0C, 0x42, 0x10, 0xC4, 0x1F, + 0x03, 0x9C, 0x3C, 0x7F, 0x33, 0xE0, 0x8C, 0x21, 0x08, 0xC3, 0xE0, 0x70, + 0x3E, 0x1F, 0xC6, 0x61, 0x80, 0x70, 0x0C, 0x07, 0x83, 0xEE, 0xDF, 0xB3, + 0xCC, 0x73, 0xFE, 0x7F, 0x80, 0xFD, 0x24, 0x90, 0x39, 0xDC, 0xE6, 0x73, + 0x18, 0xC6, 0x31, 0x8C, 0x31, 0x8E, 0x31, 0xC4, 0xE7, 0x1C, 0xE3, 0x1C, + 0x63, 0x18, 0xC6, 0x31, 0x98, 0xCE, 0x67, 0x10, 0x0C, 0x03, 0x00, 0xC3, + 0xB7, 0xFF, 0xDF, 0xE1, 0xE0, 0xFC, 0x33, 0x0C, 0xC0, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x0F, 0xFF, 0xFF, 0xF0, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x00, 0x3B, 0x9C, 0xCE, 0x62, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x00, 0x40, 0x30, 0x1C, 0x07, 0x03, 0x80, 0xE0, 0x30, + 0x1C, 0x06, 0x03, 0x80, 0xC0, 0x70, 0x18, 0x0E, 0x03, 0x01, 0xC0, 0x60, + 0x38, 0x0E, 0x01, 0x00, 0x1E, 0x0F, 0xC6, 0x1B, 0x87, 0xC0, 0xF0, 0x3C, + 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x87, 0x61, 0x8F, 0xC1, 0xE0, 0x1C, + 0x0F, 0x0F, 0xC3, 0xB0, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, + 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x1F, 0x1F, 0xEE, 0x1F, 0x83, 0xC0, + 0xC0, 0x70, 0x38, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE3, 0xF0, 0xFF, 0xFF, + 0xFC, 0x3F, 0x0F, 0xF1, 0x87, 0x00, 0x60, 0x0C, 0x03, 0x83, 0xE0, 0x7C, + 0x01, 0xC0, 0x0C, 0x01, 0x80, 0x3C, 0x0F, 0xFF, 0x9F, 0xC0, 0x07, 0x07, + 0x83, 0xC3, 0xE1, 0xB1, 0xD8, 0xCC, 0xC6, 0xE3, 0x7F, 0xFF, 0xE0, 0x61, + 0xF8, 0xFC, 0x7F, 0x9F, 0xE6, 0x01, 0x80, 0x60, 0x1F, 0x87, 0xF9, 0x86, + 0x00, 0xC0, 0x30, 0x0C, 0x03, 0xC1, 0xBF, 0xE7, 0xE0, 0x07, 0xC7, 0xF3, + 0xC1, 0xC0, 0x60, 0x38, 0x0E, 0xF3, 0xFE, 0xF1, 0xF8, 0x3E, 0x0F, 0x83, + 0x71, 0xCF, 0xE1, 0xF0, 0xFF, 0xFF, 0xFC, 0x1F, 0x07, 0x01, 0x80, 0x60, + 0x38, 0x0C, 0x03, 0x01, 0xC0, 0x60, 0x18, 0x0E, 0x03, 0x00, 0xC0, 0x1E, + 0x1F, 0xEE, 0x1F, 0x03, 0xC0, 0xF0, 0x36, 0x19, 0xFE, 0x7F, 0xB8, 0x7C, + 0x0F, 0x03, 0xE1, 0xDF, 0xE3, 0xF0, 0x3E, 0x1F, 0xCE, 0x3B, 0x07, 0xC1, + 0xF0, 0x7E, 0x3D, 0xFF, 0x3D, 0xC0, 0x70, 0x18, 0x0E, 0x0F, 0x3F, 0x8F, + 0x80, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x77, 0x70, 0x00, 0x00, 0x76, 0x6C, + 0xC8, 0x80, 0x00, 0x30, 0x0F, 0x03, 0xE0, 0xF8, 0x3E, 0x0F, 0x80, 0x3E, + 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x00, 0x20, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x60, 0x0F, 0x80, 0x3E, 0x00, 0xF8, + 0x03, 0xE0, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x0F, 0x00, 0x40, 0x00, 0x7C, + 0x7F, 0xB0, 0xF8, 0x30, 0x18, 0x1C, 0x3C, 0x3C, 0x18, 0x08, 0x00, 0x07, + 0x03, 0x81, 0xC0, 0x1E, 0x07, 0xF1, 0xC7, 0x30, 0x6C, 0x0D, 0x87, 0xB3, + 0xF6, 0xE6, 0xD8, 0xDB, 0x1B, 0x73, 0x67, 0xFC, 0x7F, 0x80, 0x30, 0x03, + 0x00, 0x71, 0xC7, 0xF8, 0x7C, 0x00, 0x3F, 0x80, 0x7F, 0x80, 0x1F, 0x00, + 0x76, 0x00, 0xEE, 0x01, 0x8C, 0x07, 0x18, 0x0E, 0x38, 0x1F, 0xF0, 0x7F, + 0xF0, 0xC0, 0x61, 0x80, 0xCF, 0xC7, 0xFF, 0x8F, 0xC0, 0xFF, 0xC7, 0xFF, + 0x0C, 0x1C, 0x60, 0x63, 0x03, 0x18, 0x38, 0xFF, 0x87, 0xFE, 0x30, 0x39, + 0x80, 0xCC, 0x06, 0x60, 0x7F, 0xFF, 0x7F, 0xF0, 0x0F, 0xF3, 0xFF, 0x70, + 0x76, 0x03, 0xC0, 0x3C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0x60, + 0x37, 0x07, 0x3F, 0xF0, 0xFC, 0xFF, 0x0F, 0xFC, 0x60, 0xE6, 0x06, 0x60, + 0x36, 0x03, 0x60, 0x36, 0x03, 0x60, 0x36, 0x03, 0x60, 0x76, 0x0E, 0xFF, + 0xCF, 0xF8, 0xFF, 0xF7, 0xFF, 0x8C, 0x0C, 0x60, 0x63, 0x1B, 0x18, 0xC0, + 0xFE, 0x07, 0xF0, 0x31, 0x81, 0x8C, 0xCC, 0x06, 0x60, 0x3F, 0xFF, 0xFF, + 0xFC, 0xFF, 0xFF, 0xFF, 0xCC, 0x06, 0x60, 0x33, 0x19, 0x98, 0xC0, 0xFE, + 0x07, 0xF0, 0x31, 0x81, 0x8C, 0x0C, 0x00, 0x60, 0x0F, 0xF0, 0x7F, 0x80, + 0x0F, 0xF1, 0xFF, 0x9C, 0x1C, 0xC0, 0x6C, 0x03, 0x60, 0x03, 0x00, 0x18, + 0x7F, 0xC3, 0xFE, 0x01, 0xB8, 0x0C, 0xE0, 0xE3, 0xFF, 0x07, 0xE0, 0x7C, + 0xF9, 0xF3, 0xE3, 0x03, 0x0C, 0x0C, 0x30, 0x30, 0xC0, 0xC3, 0xFF, 0x0F, + 0xFC, 0x30, 0x30, 0xC0, 0xC3, 0x03, 0x0C, 0x0C, 0xFC, 0xFF, 0xF3, 0xF0, + 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, + 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xF0, 0x0F, 0xF8, 0x7F, 0xC0, 0x30, 0x01, + 0x80, 0x0C, 0x00, 0x60, 0x03, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30, 0x31, + 0xC3, 0x0F, 0xF8, 0x1F, 0x00, 0xFC, 0xFB, 0xF3, 0xE3, 0x0E, 0x0C, 0x70, + 0x33, 0x80, 0xFC, 0x03, 0xF0, 0x0F, 0xE0, 0x39, 0xC0, 0xC3, 0x03, 0x0E, + 0x0C, 0x18, 0xFC, 0x7F, 0xF0, 0xF0, 0xFF, 0x0F, 0xF0, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, + 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xC0, 0xF7, 0x87, 0x9E, 0x1E, 0x7C, 0xF9, + 0xB3, 0xE6, 0xFD, 0x99, 0xF6, 0x67, 0x99, 0x8E, 0x66, 0x31, 0x98, 0x06, + 0xFC, 0xFF, 0xF3, 0xF0, 0xF1, 0xFF, 0xCF, 0xCF, 0x0C, 0x78, 0x63, 0xE3, + 0x1B, 0x18, 0xDC, 0xC6, 0x76, 0x31, 0xB1, 0x8F, 0x8C, 0x3C, 0x61, 0xE7, + 0xE7, 0x3F, 0x18, 0x0F, 0x03, 0xFC, 0x70, 0xE6, 0x06, 0xE0, 0x7C, 0x03, + 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x07, 0x60, 0x67, 0x0E, 0x3F, 0xC0, 0xF0, + 0xFF, 0x8F, 0xFE, 0x30, 0x73, 0x03, 0x30, 0x33, 0x03, 0x30, 0x73, 0xFE, + 0x3F, 0x83, 0x00, 0x30, 0x03, 0x00, 0xFF, 0x0F, 0xF0, 0x0F, 0x03, 0xFC, + 0x70, 0xE6, 0x06, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x07, + 0x60, 0x67, 0x0E, 0x3F, 0xC1, 0xF0, 0x18, 0x33, 0xFF, 0x3F, 0xE0, 0xFF, + 0x83, 0xFF, 0x83, 0x07, 0x0C, 0x0C, 0x30, 0x30, 0xC1, 0xC3, 0xFE, 0x0F, + 0xF0, 0x31, 0xE0, 0xC3, 0x83, 0x07, 0x0C, 0x0C, 0xFE, 0x3F, 0xF8, 0x70, + 0x3F, 0xDF, 0xFE, 0x1F, 0x03, 0xC0, 0xF8, 0x07, 0xE0, 0x7E, 0x01, 0xF0, + 0x3C, 0x0F, 0x87, 0xFF, 0xBF, 0xC0, 0xFF, 0xFF, 0xFF, 0xC6, 0x3C, 0x63, + 0xC6, 0x3C, 0x63, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x3F, 0xC3, 0xFC, 0xFF, 0xFF, 0xFF, 0x60, 0x66, 0x06, 0x60, 0x66, 0x06, + 0x60, 0x66, 0x06, 0x60, 0x66, 0x06, 0x60, 0x63, 0x9C, 0x1F, 0xC0, 0xF0, + 0xFC, 0x3F, 0xFC, 0x3F, 0x30, 0x0C, 0x38, 0x1C, 0x18, 0x18, 0x1C, 0x38, + 0x1C, 0x38, 0x0E, 0x70, 0x0E, 0x70, 0x0F, 0x60, 0x07, 0xE0, 0x07, 0xE0, + 0x03, 0xC0, 0x03, 0xC0, 0xFC, 0xFF, 0xF3, 0xF6, 0x01, 0xDC, 0xC6, 0x77, + 0x99, 0xDE, 0x67, 0x79, 0x8D, 0xFE, 0x3F, 0xF8, 0xF3, 0xE3, 0xCF, 0x8F, + 0x3C, 0x38, 0x70, 0xE1, 0xC0, 0xF8, 0xFB, 0xE3, 0xE3, 0x86, 0x0F, 0x38, + 0x1F, 0xC0, 0x3E, 0x00, 0x70, 0x03, 0xE0, 0x0F, 0x80, 0x77, 0x03, 0x8E, + 0x1E, 0x1C, 0xFC, 0xFF, 0xF3, 0xF0, 0xF9, 0xFF, 0x9F, 0x30, 0xC3, 0x9C, + 0x19, 0x81, 0xF8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x3F, 0xC3, 0xFC, 0xFF, 0xBF, 0xEC, 0x3B, 0x0C, 0xC6, 0x33, 0x80, 0xC0, + 0x60, 0x38, 0xCC, 0x36, 0x0F, 0x03, 0xFF, 0xFF, 0xF0, 0xFF, 0xF1, 0x8C, + 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC7, 0xFE, 0x40, 0x30, 0x0E, + 0x01, 0x80, 0x70, 0x0C, 0x03, 0x80, 0x60, 0x1C, 0x03, 0x00, 0xE0, 0x18, + 0x07, 0x00, 0xC0, 0x38, 0x0E, 0x01, 0xC0, 0x70, 0x0C, 0x01, 0xFF, 0xC6, + 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1F, 0xFE, 0x04, 0x03, + 0x01, 0xE0, 0xFC, 0x7B, 0x9C, 0x7E, 0x1F, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, + 0xCE, 0x73, 0x3F, 0x07, 0xF8, 0x00, 0xC0, 0x0C, 0x1F, 0xC7, 0xFC, 0x60, + 0xCC, 0x0C, 0xC1, 0xCF, 0xFF, 0x3F, 0xF0, 0xF0, 0x07, 0x80, 0x0C, 0x00, + 0x60, 0x03, 0x7C, 0x1F, 0xF8, 0xF1, 0xC7, 0x07, 0x30, 0x19, 0x80, 0xCC, + 0x06, 0x60, 0x73, 0xC7, 0x7F, 0xFB, 0xDF, 0x00, 0x1F, 0xB3, 0xFF, 0x70, + 0xFE, 0x07, 0xC0, 0x3C, 0x00, 0xC0, 0x0C, 0x00, 0x70, 0x77, 0xFF, 0x1F, + 0xC0, 0x01, 0xE0, 0x0F, 0x00, 0x18, 0x00, 0xC1, 0xF6, 0x3F, 0xF1, 0xC7, + 0x9C, 0x1C, 0xC0, 0x66, 0x03, 0x30, 0x19, 0x81, 0xC7, 0x1E, 0x3F, 0xFC, + 0x7D, 0xE0, 0x1F, 0x83, 0xFC, 0x70, 0xEE, 0x07, 0xFF, 0xFF, 0xFF, 0xE0, + 0x0E, 0x00, 0x70, 0x73, 0xFF, 0x1F, 0xC0, 0x07, 0xC3, 0xFC, 0x60, 0x0C, + 0x0F, 0xFD, 0xFF, 0x86, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, + 0x81, 0xFF, 0xBF, 0xF0, 0x1F, 0x79, 0xFF, 0xDC, 0x79, 0x81, 0xCC, 0x06, + 0x60, 0x33, 0x01, 0x9C, 0x1C, 0x71, 0xE1, 0xFF, 0x07, 0xD8, 0x00, 0xC0, + 0x06, 0x00, 0x70, 0x7F, 0x03, 0xF0, 0xF0, 0x03, 0xC0, 0x03, 0x00, 0x0C, + 0x00, 0x37, 0xC0, 0xFF, 0x83, 0xC7, 0x0C, 0x0C, 0x30, 0x30, 0xC0, 0xC3, + 0x03, 0x0C, 0x0C, 0x30, 0x33, 0xF3, 0xFF, 0xCF, 0xC0, 0x06, 0x00, 0xC0, + 0x00, 0x3F, 0x07, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, + 0x03, 0x0F, 0xFF, 0xFF, 0xC0, 0x06, 0x06, 0x00, 0xFF, 0xFF, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0xFE, 0xFC, + 0xF0, 0x07, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x3F, 0x19, 0xF8, 0xDE, 0x07, + 0xE0, 0x3E, 0x01, 0xF0, 0x0F, 0xC0, 0x6F, 0x03, 0x1C, 0x78, 0xFF, 0xC7, + 0xE0, 0x7E, 0x0F, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, + 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x61, 0xFF, 0xFF, 0xF8, 0xFE, 0xF1, + 0xFF, 0xF1, 0xCE, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, + 0x8C, 0x63, 0x19, 0xF7, 0xBF, 0xEF, 0x78, 0x77, 0xC1, 0xFF, 0x83, 0xC7, + 0x0C, 0x0C, 0x30, 0x30, 0xC0, 0xC3, 0x03, 0x0C, 0x0C, 0x30, 0x33, 0xF1, + 0xFF, 0xC7, 0xC0, 0x1F, 0x83, 0xFC, 0x70, 0xEE, 0x07, 0xC0, 0x3C, 0x03, + 0xC0, 0x3E, 0x07, 0x70, 0xE3, 0xFC, 0x1F, 0x80, 0xF7, 0xE3, 0xFF, 0xC3, + 0xC3, 0x8E, 0x07, 0x30, 0x0C, 0xC0, 0x33, 0x00, 0xCE, 0x07, 0x3C, 0x38, + 0xFF, 0xC3, 0x7E, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x0F, 0xE0, 0x3F, 0x80, + 0x1F, 0xBC, 0xFF, 0xF7, 0x0F, 0x38, 0x1C, 0xC0, 0x33, 0x00, 0xCC, 0x03, + 0x38, 0x1C, 0x70, 0xF0, 0xFF, 0xC1, 0xFB, 0x00, 0x0C, 0x00, 0x30, 0x00, + 0xC0, 0x1F, 0xC0, 0x7F, 0x79, 0xE7, 0xFF, 0x1F, 0x31, 0xC0, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x0F, 0xFC, 0xFF, 0xC0, 0x3F, 0x9F, 0xFE, + 0x1F, 0x82, 0xFE, 0x1F, 0xE0, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0xF0, 0x30, + 0x06, 0x00, 0xC0, 0x7F, 0xEF, 0xFC, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, + 0x00, 0xC0, 0x18, 0x71, 0xFE, 0x1F, 0x00, 0xF1, 0xF7, 0x8F, 0x8C, 0x0C, + 0x60, 0x63, 0x03, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x38, 0xF0, 0xFF, 0xC3, + 0xEE, 0xFC, 0xFF, 0xF3, 0xF3, 0x87, 0x0E, 0x1C, 0x1C, 0x60, 0x73, 0x80, + 0xEC, 0x03, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x00, 0xF8, 0x7F, 0xE1, + 0xF7, 0x39, 0x8C, 0xE6, 0x37, 0xB0, 0xFF, 0xC3, 0xFF, 0x07, 0xBC, 0x1C, + 0xF0, 0x73, 0x81, 0x86, 0x00, 0x7C, 0xF9, 0xF3, 0xE3, 0xCF, 0x07, 0xF8, + 0x0F, 0xC0, 0x1E, 0x00, 0xFC, 0x07, 0x38, 0x38, 0x73, 0xF3, 0xFF, 0xCF, + 0xC0, 0xF9, 0xFF, 0x9F, 0x70, 0xE3, 0x0C, 0x39, 0xC1, 0x98, 0x19, 0x81, + 0xF8, 0x0F, 0x00, 0xF0, 0x06, 0x00, 0x60, 0x0E, 0x00, 0xC0, 0xFF, 0x0F, + 0xF0, 0x7F, 0xCF, 0xF9, 0x8E, 0x33, 0x80, 0x70, 0x1C, 0x07, 0x01, 0xC6, + 0x70, 0xFF, 0xFF, 0xFF, 0x80, 0x0E, 0x3C, 0x60, 0xC1, 0x83, 0x06, 0x0C, + 0x39, 0xE3, 0xC0, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x3C, 0x38, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0xE1, 0xC0, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x3C, + 0x79, 0x83, 0x06, 0x0C, 0x18, 0x31, 0xE3, 0x80, 0x3C, 0x37, 0xE7, 0x67, + 0xE6, 0x1C}; + +const GFXglyph FreeMonoBold12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 14, 0, 1}, // 0x20 ' ' + {0, 4, 15, 14, 5, -14}, // 0x21 '!' + {8, 8, 7, 14, 3, -13}, // 0x22 '"' + {15, 11, 18, 14, 2, -15}, // 0x23 '#' + {40, 10, 20, 14, 2, -16}, // 0x24 '$' + {65, 10, 15, 14, 2, -14}, // 0x25 '%' + {84, 10, 13, 14, 2, -12}, // 0x26 '&' + {101, 3, 7, 14, 5, -13}, // 0x27 ''' + {104, 5, 19, 14, 6, -14}, // 0x28 '(' + {116, 5, 19, 14, 3, -14}, // 0x29 ')' + {128, 10, 10, 14, 2, -14}, // 0x2A '*' + {141, 12, 13, 14, 1, -12}, // 0x2B '+' + {161, 5, 7, 14, 4, -2}, // 0x2C ',' + {166, 12, 2, 14, 1, -7}, // 0x2D '-' + {169, 3, 3, 14, 5, -2}, // 0x2E '.' + {171, 10, 20, 14, 2, -16}, // 0x2F '/' + {196, 10, 15, 14, 2, -14}, // 0x30 '0' + {215, 10, 15, 14, 2, -14}, // 0x31 '1' + {234, 10, 15, 14, 2, -14}, // 0x32 '2' + {253, 11, 15, 14, 1, -14}, // 0x33 '3' + {274, 9, 14, 14, 2, -13}, // 0x34 '4' + {290, 10, 15, 14, 2, -14}, // 0x35 '5' + {309, 10, 15, 14, 2, -14}, // 0x36 '6' + {328, 10, 15, 14, 2, -14}, // 0x37 '7' + {347, 10, 15, 14, 2, -14}, // 0x38 '8' + {366, 10, 15, 14, 3, -14}, // 0x39 '9' + {385, 3, 11, 14, 5, -10}, // 0x3A ':' + {390, 4, 15, 14, 4, -10}, // 0x3B ';' + {398, 12, 11, 14, 1, -11}, // 0x3C '<' + {415, 12, 7, 14, 1, -9}, // 0x3D '=' + {426, 12, 11, 14, 1, -11}, // 0x3E '>' + {443, 9, 14, 14, 3, -13}, // 0x3F '?' + {459, 11, 19, 14, 2, -14}, // 0x40 '@' + {486, 15, 14, 14, -1, -13}, // 0x41 'A' + {513, 13, 14, 14, 0, -13}, // 0x42 'B' + {536, 12, 14, 14, 1, -13}, // 0x43 'C' + {557, 12, 14, 14, 1, -13}, // 0x44 'D' + {578, 13, 14, 14, 0, -13}, // 0x45 'E' + {601, 13, 14, 14, 0, -13}, // 0x46 'F' + {624, 13, 14, 14, 1, -13}, // 0x47 'G' + {647, 14, 14, 14, 0, -13}, // 0x48 'H' + {672, 10, 14, 14, 2, -13}, // 0x49 'I' + {690, 13, 14, 14, 1, -13}, // 0x4A 'J' + {713, 14, 14, 14, 0, -13}, // 0x4B 'K' + {738, 12, 14, 14, 1, -13}, // 0x4C 'L' + {759, 14, 14, 14, 0, -13}, // 0x4D 'M' + {784, 13, 14, 14, 0, -13}, // 0x4E 'N' + {807, 12, 14, 14, 1, -13}, // 0x4F 'O' + {828, 12, 14, 14, 0, -13}, // 0x50 'P' + {849, 12, 17, 14, 1, -13}, // 0x51 'Q' + {875, 14, 14, 14, 0, -13}, // 0x52 'R' + {900, 10, 14, 14, 2, -13}, // 0x53 'S' + {918, 12, 14, 14, 1, -13}, // 0x54 'T' + {939, 12, 14, 14, 1, -13}, // 0x55 'U' + {960, 16, 14, 14, -1, -13}, // 0x56 'V' + {988, 14, 14, 14, 0, -13}, // 0x57 'W' + {1013, 14, 14, 14, 0, -13}, // 0x58 'X' + {1038, 12, 14, 14, 1, -13}, // 0x59 'Y' + {1059, 10, 14, 14, 2, -13}, // 0x5A 'Z' + {1077, 5, 19, 14, 6, -14}, // 0x5B '[' + {1089, 10, 20, 14, 2, -16}, // 0x5C '\' + {1114, 5, 19, 14, 3, -14}, // 0x5D ']' + {1126, 10, 8, 14, 2, -15}, // 0x5E '^' + {1136, 14, 2, 14, 0, 4}, // 0x5F '_' + {1140, 4, 4, 14, 4, -15}, // 0x60 '`' + {1142, 12, 11, 14, 1, -10}, // 0x61 'a' + {1159, 13, 15, 14, 0, -14}, // 0x62 'b' + {1184, 12, 11, 14, 1, -10}, // 0x63 'c' + {1201, 13, 15, 14, 1, -14}, // 0x64 'd' + {1226, 12, 11, 14, 1, -10}, // 0x65 'e' + {1243, 11, 15, 14, 2, -14}, // 0x66 'f' + {1264, 13, 16, 14, 1, -10}, // 0x67 'g' + {1290, 14, 15, 14, 0, -14}, // 0x68 'h' + {1317, 11, 14, 14, 1, -13}, // 0x69 'i' + {1337, 8, 19, 15, 3, -13}, // 0x6A 'j' + {1356, 13, 15, 14, 1, -14}, // 0x6B 'k' + {1381, 11, 15, 14, 1, -14}, // 0x6C 'l' + {1402, 15, 11, 14, 0, -10}, // 0x6D 'm' + {1423, 14, 11, 14, 0, -10}, // 0x6E 'n' + {1443, 12, 11, 14, 1, -10}, // 0x6F 'o' + {1460, 14, 16, 14, 0, -10}, // 0x70 'p' + {1488, 14, 16, 14, 0, -10}, // 0x71 'q' + {1516, 12, 11, 14, 1, -10}, // 0x72 'r' + {1533, 10, 11, 14, 2, -10}, // 0x73 's' + {1547, 11, 14, 14, 1, -13}, // 0x74 't' + {1567, 13, 11, 14, 0, -10}, // 0x75 'u' + {1585, 14, 11, 14, 0, -10}, // 0x76 'v' + {1605, 14, 11, 14, 0, -10}, // 0x77 'w' + {1625, 14, 11, 14, 0, -10}, // 0x78 'x' + {1645, 12, 16, 14, 1, -10}, // 0x79 'y' + {1669, 11, 11, 14, 1, -10}, // 0x7A 'z' + {1685, 7, 19, 14, 3, -14}, // 0x7B '{' + {1702, 2, 19, 14, 6, -14}, // 0x7C '|' + {1707, 7, 19, 14, 4, -14}, // 0x7D '}' + {1724, 12, 4, 14, 1, -7}}; // 0x7E '~' + +const GFXfont FreeMonoBold12pt7b PROGMEM = { + (uint8_t *)FreeMonoBold12pt7bBitmaps, (GFXglyph *)FreeMonoBold12pt7bGlyphs, + 0x20, 0x7E, 24}; + +// Approx. 2402 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold18pt7b.h new file mode 100644 index 0000000..fee446b --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold18pt7b.h @@ -0,0 +1,422 @@ +const uint8_t FreeMonoBold18pt7bBitmaps[] PROGMEM = { + 0x77, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x9C, 0xE7, 0x39, 0xC4, 0x03, 0xBF, + 0xFF, 0xB8, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1E, 0xC1, 0x98, 0x33, + 0x06, 0x60, 0xCC, 0x18, 0x0E, 0x1C, 0x0F, 0x3C, 0x1F, 0x3C, 0x1E, 0x3C, + 0x1E, 0x3C, 0x1E, 0x78, 0x1E, 0x78, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x7F, 0xFE, 0x7F, 0xFE, + 0x7F, 0xFE, 0x7F, 0xFE, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0xF0, + 0x3C, 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, 0x03, 0x00, 0x1E, 0x00, 0x78, 0x01, + 0xE0, 0x1F, 0xF1, 0xFF, 0xE7, 0xFF, 0xBE, 0x1E, 0xF0, 0x3B, 0xC0, 0xCF, + 0xE0, 0x3F, 0xF8, 0x7F, 0xF0, 0x7F, 0xE0, 0x1F, 0xF0, 0x0F, 0xE0, 0x3F, + 0x80, 0xFF, 0x87, 0xFF, 0xFE, 0xFF, 0xF3, 0x7F, 0x80, 0x78, 0x01, 0xE0, + 0x07, 0x80, 0x1E, 0x00, 0x78, 0x00, 0xC0, 0x1E, 0x00, 0xFF, 0x03, 0x86, + 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x38, 0x70, 0x3F, 0xC2, 0x1E, 0x3E, + 0x03, 0xF8, 0x3F, 0x83, 0xF8, 0x0F, 0x8F, 0x18, 0x7F, 0x01, 0xC7, 0x03, + 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x1C, 0x70, 0x1F, 0xC0, 0x0F, 0x00, 0x03, + 0xD0, 0x1F, 0xF0, 0x7F, 0xE1, 0xFF, 0xC3, 0xE6, 0x07, 0x80, 0x0F, 0x00, + 0x0F, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0xFE, 0x03, 0xFE, 0xFF, 0xBD, 0xFE, + 0x3F, 0xFC, 0x3F, 0x7C, 0x7C, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF8, 0x7E, + 0xF0, 0xFF, 0xFF, 0xF6, 0x66, 0x66, 0x07, 0x0F, 0x1F, 0x1E, 0x3E, 0x3C, + 0x78, 0x78, 0x78, 0x70, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x78, 0x78, 0x78, 0x3C, 0x3C, 0x1E, 0x1F, 0x0F, 0x07, 0xE0, 0xF0, 0xF8, + 0x78, 0x7C, 0x3C, 0x3E, 0x1E, 0x1E, 0x1E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0E, 0x1E, 0x1E, 0x1E, 0x3C, 0x3C, 0x78, 0xF8, 0xF0, 0xE0, + 0x01, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7F, 0xFE, 0x1F, 0xF8, 0x07, 0xE0, 0x0F, 0xF0, 0x1F, 0xF8, + 0x1E, 0x78, 0x1C, 0x38, 0x18, 0x18, 0x01, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0x80, 0x3E, 0x78, 0xF3, 0xC7, + 0x8E, 0x1C, 0x70, 0xE1, 0x80, 0x7F, 0xFF, 0xDF, 0xFF, 0xF9, 0xFF, 0xFF, + 0x3F, 0xFF, 0xE0, 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x0E, 0x00, 0x3C, 0x00, + 0x78, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x1E, 0x00, 0x38, 0x00, 0xF0, + 0x01, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x3C, 0x00, 0x78, 0x01, 0xE0, 0x03, + 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x78, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x80, + 0x1E, 0x00, 0x3C, 0x00, 0x70, 0x01, 0xE0, 0x03, 0x80, 0x03, 0x00, 0x00, + 0x07, 0xE0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC, 0x7C, 0x3E, 0x78, 0x1E, + 0xF8, 0x1F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, + 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF8, 0x1F, 0x78, 0x1E, + 0x7C, 0x3E, 0x3F, 0xFC, 0x3F, 0xFC, 0x1F, 0xF8, 0x07, 0xE0, 0x07, 0xC0, + 0x1F, 0x80, 0xFF, 0x03, 0xFE, 0x0F, 0xBC, 0x0C, 0x78, 0x00, 0xF0, 0x01, + 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, + 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x81, 0xFF, 0xFB, 0xFF, 0xF7, + 0xFF, 0xE7, 0xFF, 0x80, 0x0F, 0xC0, 0x7F, 0xE1, 0xFF, 0xE3, 0xFF, 0xEF, + 0x87, 0xDE, 0x07, 0xF8, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x7C, 0x01, + 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, + 0x78, 0x03, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, + 0x0F, 0xC0, 0x7F, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 0x70, 0x3E, 0x00, 0x1E, + 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x3C, 0x03, 0xFC, 0x03, 0xF0, 0x03, 0xF0, + 0x03, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, + 0xE0, 0x3F, 0xFF, 0xFE, 0xFF, 0xFC, 0x7F, 0xF8, 0x1F, 0xE0, 0x00, 0xF8, + 0x03, 0xF0, 0x07, 0xE0, 0x1F, 0xC0, 0x77, 0x80, 0xEF, 0x03, 0x9E, 0x0F, + 0x3C, 0x1C, 0x78, 0x70, 0xF1, 0xE1, 0xE3, 0x83, 0xCF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x78, 0x07, 0xFC, 0x0F, 0xF8, 0x1F, 0xF0, + 0x1F, 0xC0, 0x3F, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x07, 0xFF, 0x83, 0xC0, + 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x7B, 0xE0, 0x3F, 0xFC, 0x1F, 0xFF, 0x0F, + 0xFF, 0xC3, 0x83, 0xE0, 0x00, 0xF8, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, + 0x00, 0x0F, 0xB8, 0x0F, 0xBF, 0xFF, 0xCF, 0xFF, 0xC3, 0xFF, 0xC0, 0x7F, + 0x80, 0x00, 0xFC, 0x07, 0xFC, 0x3F, 0xF8, 0xFF, 0xF1, 0xF8, 0x07, 0xC0, + 0x1F, 0x00, 0x3C, 0x00, 0xF0, 0x01, 0xE7, 0xC3, 0xDF, 0xC7, 0x7F, 0xCF, + 0xFF, 0xDF, 0x8F, 0xFC, 0x07, 0xF0, 0x0F, 0xF0, 0x1F, 0xE0, 0x3D, 0xE0, + 0xFB, 0xFF, 0xE3, 0xFF, 0xC3, 0xFF, 0x01, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xE0, 0x03, 0x80, 0x0F, 0x00, 0x1E, + 0x00, 0x38, 0x00, 0xF0, 0x01, 0xE0, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, + 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x38, + 0x00, 0x70, 0x00, 0x07, 0xC0, 0x3F, 0xE0, 0xFF, 0xE3, 0xFF, 0xEF, 0x83, + 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1E, 0xF0, 0x78, 0xFF, 0xE0, + 0xFF, 0x81, 0xFF, 0x0F, 0xFF, 0x9E, 0x0F, 0x78, 0x0F, 0xF0, 0x1F, 0xE0, + 0x3F, 0xE0, 0xFB, 0xFF, 0xE7, 0xFF, 0xC7, 0xFF, 0x03, 0xF8, 0x00, 0x0F, + 0xC0, 0x3F, 0xE0, 0xFF, 0xE3, 0xFF, 0xEF, 0xC3, 0xDF, 0x03, 0xBC, 0x07, + 0xF8, 0x0F, 0xF0, 0x1F, 0xF0, 0x3D, 0xF1, 0xFB, 0xFF, 0xF3, 0xFE, 0xE3, + 0xFB, 0xC3, 0xE7, 0x80, 0x1E, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xE7, 0xFF, + 0x8F, 0xFE, 0x1F, 0xF0, 0x1F, 0x80, 0x00, 0x77, 0xFF, 0xF7, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0xFF, 0xEE, 0x1C, 0x7C, 0xF9, 0xF1, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF3, 0xC7, 0x8E, 0x3C, 0x70, 0xE1, 0x87, 0x0C, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0xFE, + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x07, 0xF0, 0x00, + 0x7F, 0x00, 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x07, 0xF0, 0x00, 0x7C, 0x00, + 0x07, 0x7F, 0xFF, 0xDF, 0xFF, 0xF9, 0xFF, 0xFF, 0x3F, 0xFF, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF7, 0xFF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFF, + 0xF8, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0xC0, 0x01, 0xFC, 0x00, 0x1F, 0xC0, + 0x01, 0xFC, 0x00, 0x1F, 0xC0, 0x01, 0xFC, 0x00, 0x3F, 0x80, 0x3F, 0x80, + 0x3F, 0x80, 0x3F, 0x80, 0x3F, 0x80, 0x3F, 0x80, 0x0F, 0x80, 0x03, 0x80, + 0x00, 0x1F, 0xC0, 0xFF, 0xE3, 0xFF, 0xF7, 0xFF, 0xEF, 0x07, 0xFE, 0x03, + 0xDC, 0x07, 0x80, 0x0F, 0x00, 0x7C, 0x03, 0xF8, 0x1F, 0xC0, 0x1E, 0x00, + 0x30, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x1F, 0x00, 0x3E, + 0x00, 0x7C, 0x00, 0x70, 0x00, 0x07, 0xE0, 0x1F, 0xE0, 0x7F, 0xE1, 0xE1, + 0xC7, 0x83, 0xCE, 0x03, 0xBC, 0x07, 0x70, 0x0E, 0xE0, 0x7D, 0xC3, 0xFB, + 0x8F, 0xF7, 0x3C, 0xEE, 0x71, 0xDC, 0xE3, 0xB9, 0xC7, 0x73, 0xCE, 0xE3, + 0xFF, 0xC3, 0xFF, 0x83, 0xFF, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x1E, 0x02, + 0x1E, 0x1E, 0x3F, 0xFC, 0x1F, 0xF0, 0x1F, 0x80, 0x0F, 0xF8, 0x00, 0x7F, + 0xF0, 0x01, 0xFF, 0xC0, 0x03, 0xFF, 0x00, 0x01, 0xFE, 0x00, 0x07, 0xF8, + 0x00, 0x1C, 0xF0, 0x00, 0xF3, 0xC0, 0x03, 0xCF, 0x00, 0x1E, 0x1E, 0x00, + 0x78, 0x78, 0x03, 0xC0, 0xF0, 0x0F, 0xFF, 0xC0, 0x3F, 0xFF, 0x01, 0xFF, + 0xFE, 0x07, 0xFF, 0xF8, 0x3C, 0x00, 0xF3, 0xFC, 0x1F, 0xEF, 0xF8, 0x7F, + 0xFF, 0xE1, 0xFF, 0x7F, 0x03, 0xF8, 0x7F, 0xFC, 0x0F, 0xFF, 0xF0, 0xFF, + 0xFF, 0x8F, 0xFF, 0xF8, 0x3C, 0x07, 0xC3, 0xC0, 0x3C, 0x3C, 0x03, 0xC3, + 0xC0, 0x7C, 0x3F, 0xFF, 0x83, 0xFF, 0xF0, 0x3F, 0xFF, 0x83, 0xFF, 0xFE, + 0x3C, 0x03, 0xE3, 0xC0, 0x1F, 0x3C, 0x00, 0xF3, 0xC0, 0x0F, 0x3C, 0x01, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xEF, 0xFF, 0xFC, 0x7F, 0xFF, 0x00, 0x01, + 0xF8, 0xC1, 0xFF, 0xFC, 0x7F, 0xFF, 0x9F, 0xFF, 0xF7, 0xE0, 0x7E, 0xF8, + 0x07, 0xFE, 0x00, 0x7F, 0x80, 0x0E, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, + 0x00, 0x78, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xE0, + 0x07, 0x7F, 0x03, 0xE7, 0xFF, 0xFC, 0x7F, 0xFF, 0x03, 0xFF, 0xC0, 0x1F, + 0xE0, 0xFF, 0xF0, 0x3F, 0xFF, 0x0F, 0xFF, 0xE3, 0xFF, 0xFC, 0x78, 0x1F, + 0x9E, 0x03, 0xE7, 0x80, 0x79, 0xE0, 0x0F, 0x78, 0x03, 0xDE, 0x00, 0xF7, + 0x80, 0x3D, 0xE0, 0x0F, 0x78, 0x03, 0xDE, 0x00, 0xF7, 0x80, 0x7D, 0xE0, + 0x1E, 0x78, 0x1F, 0xBF, 0xFF, 0xCF, 0xFF, 0xF3, 0xFF, 0xF0, 0x7F, 0xF0, + 0x00, 0x7F, 0xFF, 0xDF, 0xFF, 0xFB, 0xFF, 0xFF, 0x7F, 0xFF, 0xE3, 0xC0, + 0x3C, 0x78, 0x07, 0x8F, 0x1C, 0xF1, 0xE3, 0xCC, 0x3F, 0xF8, 0x07, 0xFF, + 0x00, 0xFF, 0xE0, 0x1F, 0xFC, 0x03, 0xC7, 0x80, 0x78, 0xF1, 0x8F, 0x0C, + 0x79, 0xE0, 0x0F, 0x3C, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF7, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF3, 0xC0, 0x1E, 0x78, 0x63, 0xCF, 0x1E, 0x79, 0xE3, 0xC6, 0x3F, 0xF8, + 0x07, 0xFF, 0x00, 0xFF, 0xE0, 0x1F, 0xFC, 0x03, 0xC7, 0x80, 0x78, 0xE0, + 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x1F, 0xFC, 0x03, 0xFF, 0x80, + 0x7F, 0xF0, 0x07, 0xFC, 0x00, 0x01, 0xFC, 0xE0, 0x7F, 0xFE, 0x1F, 0xFF, + 0xE3, 0xFF, 0xFE, 0x7F, 0x03, 0xE7, 0xC0, 0x1E, 0xF8, 0x00, 0xEF, 0x00, + 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x03, 0xFE, 0xF0, + 0x3F, 0xFF, 0x03, 0xFF, 0xF8, 0x3F, 0xF7, 0x80, 0x1E, 0x7E, 0x01, 0xE3, + 0xFF, 0xFE, 0x1F, 0xFF, 0xE0, 0xFF, 0xF8, 0x01, 0xFE, 0x00, 0x7F, 0x0F, + 0xE3, 0xFC, 0x7F, 0x9F, 0xE3, 0xFC, 0x7F, 0x1F, 0xC1, 0xE0, 0x3C, 0x0F, + 0x01, 0xE0, 0x78, 0x0F, 0x03, 0xC0, 0x78, 0x1E, 0x03, 0xC0, 0xFF, 0xFE, + 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0x81, 0xFF, 0xFC, 0x0F, 0x01, 0xE0, 0x78, + 0x0F, 0x03, 0xC0, 0x78, 0x1E, 0x03, 0xC3, 0xFC, 0x7F, 0xBF, 0xE3, 0xFF, + 0xFF, 0x1F, 0xF7, 0xF0, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, + 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x83, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xF8, 0x01, 0xFF, 0xE0, 0x3F, 0xFC, + 0x07, 0xFF, 0x80, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, + 0x00, 0x78, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x38, 0x07, 0x8F, + 0x00, 0xF1, 0xE0, 0x1E, 0x3C, 0x03, 0xC7, 0x80, 0xF8, 0xF8, 0x3F, 0x1F, + 0xFF, 0xC3, 0xFF, 0xF0, 0x1F, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x0F, 0xCF, + 0xF9, 0xFE, 0xFF, 0x9F, 0xEF, 0xF8, 0xFC, 0x3C, 0x1F, 0x03, 0xC3, 0xE0, + 0x3C, 0x7C, 0x03, 0xCF, 0x80, 0x3D, 0xF0, 0x03, 0xFE, 0x00, 0x3F, 0xF8, + 0x03, 0xFF, 0x80, 0x3E, 0x7C, 0x03, 0xC3, 0xE0, 0x3C, 0x1E, 0x03, 0xC0, + 0xF0, 0x3C, 0x0F, 0x0F, 0xF8, 0x7E, 0xFF, 0x87, 0xFF, 0xF8, 0x7F, 0x7F, + 0x03, 0xE0, 0xFF, 0xC0, 0x3F, 0xF0, 0x0F, 0xFC, 0x03, 0xFF, 0x00, 0x1E, + 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, + 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x01, 0x87, 0x80, 0xF1, 0xE0, 0x3C, + 0x78, 0x0F, 0x1E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + 0xFF, 0xC0, 0x3E, 0x00, 0xF8, 0xFC, 0x01, 0xF9, 0xFC, 0x07, 0xF3, 0xF8, + 0x0F, 0xE3, 0xF8, 0x3F, 0x87, 0xF0, 0x7F, 0x0F, 0xF1, 0xFE, 0x1F, 0xE3, + 0xFC, 0x3D, 0xE7, 0x78, 0x7B, 0xDE, 0xF0, 0xF7, 0xBD, 0xE1, 0xE7, 0xF3, + 0xC3, 0xCF, 0xE7, 0x87, 0x8F, 0x8F, 0x0F, 0x1F, 0x1E, 0x1E, 0x1E, 0x3C, + 0x3C, 0x00, 0x79, 0xFF, 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xFC, 0x1F, 0xF7, + 0xF0, 0x1F, 0xC0, 0xFC, 0x1F, 0xEF, 0xE1, 0xFF, 0xFE, 0x1F, 0xFF, 0xF1, + 0xFF, 0x3F, 0x83, 0xC3, 0xF8, 0x3C, 0x3F, 0xC3, 0xC3, 0xFC, 0x3C, 0x3D, + 0xE3, 0xC3, 0xDE, 0x3C, 0x3C, 0xF3, 0xC3, 0xC7, 0xBC, 0x3C, 0x7B, 0xC3, + 0xC3, 0xFC, 0x3C, 0x3F, 0xC3, 0xC1, 0xFC, 0x3C, 0x1F, 0xCF, 0xF8, 0xFC, + 0xFF, 0x87, 0xCF, 0xF8, 0x7C, 0x7F, 0x03, 0xC0, 0x01, 0xF8, 0x00, 0x7F, + 0xE0, 0x0F, 0xFF, 0x81, 0xFF, 0xFC, 0x3F, 0x0F, 0xC7, 0xC0, 0x3E, 0x78, + 0x01, 0xEF, 0x80, 0x1F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, + 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x80, 0x1F, 0x78, 0x01, 0xE7, 0xC0, 0x3E, + 0x3F, 0x0F, 0xC1, 0xFF, 0xF8, 0x1F, 0xFF, 0x00, 0x7F, 0xE0, 0x01, 0xF8, + 0x00, 0x7F, 0xF8, 0x3F, 0xFF, 0x8F, 0xFF, 0xF3, 0xFF, 0xFE, 0x3C, 0x0F, + 0xCF, 0x00, 0xF3, 0xC0, 0x3C, 0xF0, 0x0F, 0x3C, 0x03, 0xCF, 0x03, 0xF3, + 0xFF, 0xF8, 0xFF, 0xFC, 0x3F, 0xFE, 0x0F, 0xFE, 0x03, 0xC0, 0x00, 0xF0, + 0x00, 0x3C, 0x00, 0x3F, 0xF8, 0x0F, 0xFE, 0x03, 0xFF, 0x80, 0x7F, 0xC0, + 0x00, 0x01, 0xF8, 0x00, 0x7F, 0xE0, 0x0F, 0xFF, 0x01, 0xFF, 0xF8, 0x3F, + 0x0F, 0xC7, 0xC0, 0x3E, 0x78, 0x01, 0xEF, 0x80, 0x1F, 0xF0, 0x00, 0xFF, + 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x80, 0x1F, + 0x78, 0x01, 0xE7, 0xC0, 0x3E, 0x3F, 0x0F, 0xC1, 0xFF, 0xF8, 0x0F, 0xFF, + 0x00, 0x7F, 0xE0, 0x03, 0xF8, 0x00, 0x3F, 0x8E, 0x07, 0xFF, 0xF0, 0xFF, + 0xFF, 0x0F, 0xFF, 0xE0, 0x60, 0x78, 0x7F, 0xF8, 0x07, 0xFF, 0xF0, 0x3F, + 0xFF, 0xE0, 0xFF, 0xFF, 0x01, 0xE0, 0x7C, 0x0F, 0x01, 0xE0, 0x78, 0x0F, + 0x03, 0xC0, 0x78, 0x1E, 0x0F, 0xC0, 0xFF, 0xFC, 0x07, 0xFF, 0xC0, 0x3F, + 0xF8, 0x01, 0xFF, 0xE0, 0x0F, 0x0F, 0x80, 0x78, 0x3C, 0x03, 0xC0, 0xF0, + 0x1E, 0x07, 0xC3, 0xFE, 0x1F, 0xBF, 0xF0, 0x7F, 0xFF, 0x83, 0xF7, 0xF8, + 0x0F, 0x00, 0x07, 0xE7, 0x07, 0xFF, 0x8F, 0xFF, 0xC7, 0xFF, 0xE7, 0xC1, + 0xF3, 0xC0, 0x79, 0xE0, 0x3C, 0xF8, 0x00, 0x7F, 0x80, 0x1F, 0xFC, 0x07, + 0xFF, 0x81, 0xFF, 0xE0, 0x0F, 0xFB, 0x00, 0x7F, 0xC0, 0x1F, 0xE0, 0x0F, + 0xFC, 0x1F, 0xFF, 0xFF, 0xBF, 0xFF, 0x8D, 0xFF, 0x80, 0x3F, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x1F, 0xE1, + 0xE3, 0xFC, 0x3C, 0x7F, 0x87, 0x8F, 0x60, 0xF0, 0xC0, 0x1E, 0x00, 0x03, + 0xC0, 0x00, 0x78, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x07, + 0x80, 0x00, 0xF0, 0x01, 0xFF, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0x80, 0x7F, + 0xE0, 0xFF, 0x0F, 0xF7, 0xFC, 0x7F, 0xFF, 0xE3, 0xFE, 0xFF, 0x1F, 0xF3, + 0xC0, 0x1E, 0x1E, 0x00, 0xF0, 0xF0, 0x07, 0x87, 0x80, 0x3C, 0x3C, 0x01, + 0xE1, 0xE0, 0x0F, 0x0F, 0x00, 0x78, 0x78, 0x03, 0xC3, 0xC0, 0x1E, 0x1E, + 0x00, 0xF0, 0xF0, 0x07, 0x87, 0xC0, 0x7C, 0x1F, 0x07, 0xC0, 0xFF, 0xFE, + 0x03, 0xFF, 0xE0, 0x0F, 0xFE, 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0x03, 0xFD, + 0xFF, 0x07, 0xFF, 0xFE, 0x0F, 0xFB, 0xF8, 0x1F, 0xE1, 0xC0, 0x07, 0x03, + 0xC0, 0x1E, 0x07, 0x80, 0x3C, 0x07, 0x80, 0xF0, 0x0F, 0x01, 0xE0, 0x0F, + 0x03, 0x80, 0x1E, 0x0F, 0x00, 0x3E, 0x1E, 0x00, 0x3C, 0x78, 0x00, 0x78, + 0xF0, 0x00, 0x7B, 0xC0, 0x00, 0xF7, 0x80, 0x01, 0xFF, 0x00, 0x01, 0xFC, + 0x00, 0x03, 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0xFF, 0x0F, + 0xF7, 0xFC, 0x7F, 0xFF, 0xE3, 0xFF, 0xFE, 0x0F, 0xF7, 0x80, 0x0F, 0x3C, + 0x38, 0x78, 0xE3, 0xE3, 0x87, 0x1F, 0x1C, 0x38, 0xF8, 0xE1, 0xEF, 0xE7, + 0x0F, 0x7F, 0x78, 0x7B, 0xBB, 0xC3, 0xFD, 0xFE, 0x0F, 0xEF, 0xF0, 0x7E, + 0x3F, 0x03, 0xF1, 0xF8, 0x1F, 0x8F, 0xC0, 0xFC, 0x3E, 0x07, 0xC1, 0xF0, + 0x3E, 0x0F, 0x81, 0xF0, 0x7C, 0x00, 0x7E, 0x0F, 0xDF, 0xE3, 0xFF, 0xFC, + 0x7F, 0xBF, 0x07, 0xE1, 0xE0, 0xF8, 0x3E, 0x3E, 0x03, 0xEF, 0x80, 0x3D, + 0xE0, 0x03, 0xF8, 0x00, 0x3E, 0x00, 0x03, 0xC0, 0x00, 0xF8, 0x00, 0x3F, + 0x80, 0x0F, 0x78, 0x03, 0xC7, 0x80, 0xF8, 0x78, 0x3E, 0x0F, 0x8F, 0xE3, + 0xFF, 0xFC, 0x7F, 0xFF, 0x8F, 0xF7, 0xE0, 0xFC, 0x7E, 0x07, 0xEF, 0xF0, + 0xFF, 0xFF, 0x0F, 0xF7, 0xE0, 0x7E, 0x1E, 0x07, 0x81, 0xF0, 0xF8, 0x0F, + 0x0F, 0x00, 0x79, 0xE0, 0x07, 0xFE, 0x00, 0x3F, 0xC0, 0x01, 0xF8, 0x00, + 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, + 0x00, 0xF0, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0x00, 0xFF, 0xF0, 0x07, 0xFE, + 0x00, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xF0, 0x3C, 0xF0, + 0x78, 0xF0, 0xF0, 0x70, 0xE0, 0x01, 0xE0, 0x03, 0xC0, 0x03, 0x80, 0x07, + 0x00, 0x0F, 0x00, 0x1E, 0x0E, 0x1C, 0x0F, 0x38, 0x0F, 0x78, 0x0F, 0x7F, + 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFE, 0xFF, 0xFF, 0xFE, 0xE0, 0x01, + 0xE0, 0x03, 0xC0, 0x03, 0xC0, 0x07, 0x80, 0x07, 0x00, 0x0F, 0x00, 0x0E, + 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x78, 0x00, 0xF0, + 0x00, 0xF0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xC0, 0x03, 0xC0, 0x07, 0x80, + 0x07, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x3C, 0x00, + 0x38, 0x00, 0x70, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x7F, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x07, 0x00, 0x1F, 0x00, + 0x7F, 0x00, 0xFE, 0x03, 0xDE, 0x0F, 0x1E, 0x3E, 0x3E, 0xF8, 0x3F, 0xE0, + 0x3F, 0x80, 0x38, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0xC3, 0x87, 0x0E, 0x1C, 0x30, 0x01, 0xFC, 0x01, 0xFF, 0xC0, + 0x3F, 0xFC, 0x07, 0xFF, 0xC0, 0x00, 0x78, 0x0F, 0xFF, 0x07, 0xFF, 0xE1, + 0xFF, 0xFC, 0x7F, 0xFF, 0x9F, 0x80, 0xF3, 0xC0, 0x1E, 0x78, 0x0F, 0xCF, + 0xFF, 0xFE, 0xFF, 0xFF, 0xCF, 0xFF, 0xF8, 0x7F, 0x3E, 0x7C, 0x00, 0x1F, + 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x0F, + 0x3F, 0x01, 0xFF, 0xF8, 0x3F, 0xFF, 0x87, 0xFF, 0xF0, 0xFC, 0x1F, 0x1F, + 0x01, 0xF3, 0xC0, 0x1E, 0x78, 0x03, 0xCF, 0x00, 0x79, 0xE0, 0x0F, 0x3E, + 0x03, 0xE7, 0xE0, 0xFB, 0xFF, 0xFF, 0x7F, 0xFF, 0xCF, 0xFF, 0xF0, 0xF9, + 0xF8, 0x00, 0x03, 0xF3, 0x87, 0xFF, 0xCF, 0xFF, 0xEF, 0xFF, 0xF7, 0xE0, + 0xFF, 0xC0, 0x3F, 0xC0, 0x0F, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3E, + 0x00, 0x4F, 0x80, 0xF7, 0xFF, 0xF9, 0xFF, 0xF8, 0x7F, 0xF8, 0x0F, 0xF0, + 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x03, + 0xC0, 0x00, 0x3C, 0x03, 0xF3, 0xC0, 0xFF, 0xBC, 0x1F, 0xFF, 0xC3, 0xFF, + 0xFC, 0x7E, 0x0F, 0xC7, 0x80, 0x7C, 0xF0, 0x03, 0xCF, 0x00, 0x3C, 0xF0, + 0x03, 0xCF, 0x00, 0x3C, 0xF8, 0x07, 0xC7, 0xE0, 0xFC, 0x7F, 0xFF, 0xF3, + 0xFF, 0xFF, 0x0F, 0xFF, 0xF0, 0x3F, 0x3E, 0x03, 0xF0, 0x03, 0xFF, 0x01, + 0xFF, 0xE0, 0xFF, 0xFC, 0x7E, 0x0F, 0x9E, 0x01, 0xEF, 0x00, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xE0, 0x00, 0x7F, 0xFF, + 0xCF, 0xFF, 0xF1, 0xFF, 0xF8, 0x0F, 0xF0, 0x03, 0xFC, 0x07, 0xFF, 0x0F, + 0xFF, 0x1F, 0xFF, 0x1E, 0x00, 0x1E, 0x00, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, + 0xFC, 0xFF, 0xF8, 0x1E, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1E, + 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0xFF, 0xF8, 0xFF, 0xF8, 0xFF, + 0xF8, 0xFF, 0xF8, 0x07, 0xE7, 0xC3, 0xFF, 0xFC, 0xFF, 0xFF, 0xBF, 0xFF, + 0xF7, 0xC1, 0xF9, 0xF0, 0x1F, 0x3C, 0x01, 0xE7, 0x80, 0x3C, 0xF0, 0x07, + 0x9E, 0x00, 0xF3, 0xE0, 0x3E, 0x3E, 0x0F, 0xC7, 0xFF, 0xF8, 0x7F, 0xFF, + 0x07, 0xFD, 0xE0, 0x3F, 0x3C, 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x3E, + 0x03, 0xFF, 0x80, 0x7F, 0xF0, 0x0F, 0xFC, 0x00, 0xFE, 0x00, 0x3E, 0x00, + 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x01, 0xE0, 0x00, 0x0F, + 0x00, 0x00, 0x78, 0xF8, 0x03, 0xDF, 0xE0, 0x1F, 0xFF, 0x80, 0xFF, 0xFE, + 0x07, 0xE1, 0xF0, 0x3E, 0x07, 0x81, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x78, + 0x0F, 0x03, 0xC0, 0x78, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x1F, 0xC1, 0xFD, + 0xFE, 0x0F, 0xFF, 0xF0, 0x7F, 0xBF, 0x01, 0xF8, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x3F, 0xC0, + 0x3F, 0xC0, 0x3F, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7F, 0xFE, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0xF0, 0x0F, 0x00, 0xF0, + 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, + 0x0F, 0x00, 0xF0, 0x0F, 0x01, 0xFF, 0xFE, 0xFF, 0xEF, 0xFC, 0x7F, 0x00, + 0x7C, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0x3C, 0x00, 0x0F, + 0x00, 0x03, 0xC7, 0xF0, 0xF3, 0xFC, 0x3C, 0xFF, 0x0F, 0x3F, 0x83, 0xDF, + 0x00, 0xFF, 0x80, 0x3F, 0xC0, 0x0F, 0xE0, 0x03, 0xFC, 0x00, 0xF7, 0x80, + 0x3C, 0xF0, 0x0F, 0x1F, 0x0F, 0xC3, 0xFB, 0xF1, 0xFF, 0xFC, 0x7F, 0xDF, + 0x0F, 0xE0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0x3D, 0xE3, + 0xC1, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0x1F, 0xFF, 0xFE, 0x3E, 0x3C, 0x78, + 0xF0, 0xF1, 0xE3, 0xC3, 0xC7, 0x8F, 0x0F, 0x1E, 0x3C, 0x3C, 0x78, 0xF0, + 0xF1, 0xE3, 0xC3, 0xC7, 0x8F, 0x0F, 0x1E, 0xFE, 0x3E, 0x7F, 0xF8, 0xF9, + 0xFF, 0xE3, 0xE7, 0xDF, 0x0F, 0x1E, 0x1E, 0x7C, 0x03, 0xEF, 0xF0, 0x3F, + 0xFF, 0x83, 0xFF, 0xFC, 0x1F, 0x87, 0xC1, 0xE0, 0x3C, 0x1E, 0x03, 0xC1, + 0xE0, 0x3C, 0x1E, 0x03, 0xC1, 0xE0, 0x3C, 0x1E, 0x03, 0xC1, 0xE0, 0x3C, + 0x7F, 0x0F, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xF7, 0xE0, 0x7E, 0x03, 0xF8, + 0x01, 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xC7, 0xE0, 0xFD, 0xF0, 0x07, + 0xFC, 0x00, 0x7F, 0x80, 0x0F, 0xF0, 0x01, 0xFE, 0x00, 0x3F, 0xE0, 0x0F, + 0xBF, 0x07, 0xE3, 0xFF, 0xF8, 0x3F, 0xFE, 0x03, 0xFF, 0x80, 0x1F, 0xC0, + 0x3E, 0x7E, 0x03, 0xF7, 0xFC, 0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0xC1, 0xF8, + 0x3F, 0x0F, 0x80, 0x7C, 0x78, 0x01, 0xE3, 0xC0, 0x0F, 0x1E, 0x00, 0x78, + 0xF0, 0x03, 0xC7, 0xC0, 0x3E, 0x3F, 0x07, 0xE1, 0xFF, 0xFE, 0x0F, 0xFF, + 0xE0, 0x7B, 0xFE, 0x03, 0xCF, 0xC0, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, + 0x80, 0x00, 0xFF, 0x80, 0x0F, 0xFC, 0x00, 0x7F, 0xE0, 0x01, 0xFE, 0x00, + 0x00, 0x03, 0xF3, 0xE0, 0x7F, 0xDF, 0x87, 0xFF, 0xFC, 0x7F, 0xFF, 0xE7, + 0xE0, 0xFC, 0x7C, 0x03, 0xE3, 0xC0, 0x0F, 0x1E, 0x00, 0x78, 0xF0, 0x03, + 0xC7, 0x80, 0x1E, 0x3E, 0x01, 0xF0, 0xFC, 0x1F, 0x83, 0xFF, 0xFC, 0x1F, + 0xFF, 0xE0, 0x3F, 0xEF, 0x00, 0x7E, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, + 0x00, 0x00, 0xF0, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x80, 0x0F, 0xFC, 0x00, + 0x3F, 0xC0, 0x7E, 0x1E, 0x7F, 0x3F, 0xFF, 0xBF, 0xFF, 0xFF, 0xF1, 0xFE, + 0x00, 0xFC, 0x00, 0x7C, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, + 0x80, 0x03, 0xC0, 0x0F, 0xFF, 0x87, 0xFF, 0xC3, 0xFF, 0xE1, 0xFF, 0xE0, + 0x07, 0xE6, 0x1F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x78, 0x1E, 0x78, 0x0E, + 0x7F, 0xE0, 0x3F, 0xFC, 0x03, 0xFE, 0x60, 0x1F, 0xE0, 0x0F, 0xF8, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFC, 0x07, 0xE0, 0x0C, 0x00, 0x0F, 0x00, + 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x07, 0xFF, 0xF3, 0xFF, 0xF9, 0xFF, + 0xFC, 0xFF, 0xFC, 0x0F, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, + 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x07, 0x8F, 0xFF, 0xC3, 0xFF, + 0xC1, 0xFF, 0xC0, 0x3F, 0x80, 0xFC, 0x1F, 0xBF, 0x0F, 0xEF, 0xC3, 0xFB, + 0xF0, 0xFE, 0x3C, 0x07, 0x8F, 0x01, 0xE3, 0xC0, 0x78, 0xF0, 0x1E, 0x3C, + 0x07, 0x8F, 0x01, 0xE3, 0xC0, 0x78, 0xF8, 0x7E, 0x3F, 0xFF, 0xC7, 0xFF, + 0xF0, 0xFF, 0x7C, 0x0F, 0x9E, 0x7F, 0x07, 0xF7, 0xFC, 0x7F, 0xFF, 0xE3, + 0xFE, 0xFE, 0x0F, 0xE1, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x3C, 0x1E, 0x01, + 0xE0, 0xF0, 0x07, 0x8F, 0x00, 0x3E, 0x78, 0x00, 0xF7, 0x80, 0x07, 0xFC, + 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x7E, + 0x03, 0xF7, 0xF8, 0x3F, 0xFF, 0xC1, 0xFE, 0xFC, 0x07, 0xF3, 0xC7, 0x0F, + 0x1E, 0x7C, 0xF0, 0x73, 0xE7, 0x83, 0x9F, 0x7C, 0x1F, 0xFF, 0xC0, 0xFF, + 0xFE, 0x03, 0xF7, 0xF0, 0x1F, 0xBF, 0x80, 0xFC, 0xF8, 0x07, 0xC7, 0xC0, + 0x1E, 0x3E, 0x00, 0xE0, 0xE0, 0x7E, 0x0F, 0xDF, 0xE3, 0xFF, 0xFC, 0x7F, + 0xBF, 0x07, 0xE1, 0xF1, 0xF0, 0x1F, 0xFC, 0x01, 0xFF, 0x00, 0x1F, 0xC0, + 0x07, 0xF8, 0x01, 0xFF, 0xC0, 0x7E, 0xFC, 0x1F, 0x8F, 0xC7, 0xE0, 0xFD, + 0xFE, 0x3F, 0xFF, 0xC7, 0xFF, 0xF0, 0x7F, 0x7E, 0x0F, 0xDF, 0xE3, 0xFF, + 0xFC, 0x7F, 0xBF, 0x07, 0xE3, 0xC0, 0x78, 0x3C, 0x0E, 0x07, 0x83, 0xC0, + 0x78, 0x70, 0x0F, 0x1E, 0x00, 0xE3, 0x80, 0x1E, 0xF0, 0x01, 0xDC, 0x00, + 0x3F, 0x80, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0x00, 0x01, 0xE0, 0x00, + 0x38, 0x00, 0x0F, 0x00, 0x3F, 0xF0, 0x0F, 0xFF, 0x01, 0xFF, 0xE0, 0x1F, + 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF9, 0xC7, + 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x78, 0x03, 0xC0, 0x1E, 0x07, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x81, 0xF0, 0xFC, 0x7E, 0x1F, + 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0xF8, 0xFC, 0x3E, 0x0F, + 0x83, 0xF0, 0x3E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xF0, 0x7E, + 0x0F, 0xC3, 0xF0, 0x38, 0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x70, 0x3E, 0x0F, 0xC1, 0xF8, 0x3E, + 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x7C, 0x0F, 0xC1, 0xF0, + 0x7C, 0x3F, 0x1F, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x83, 0xE1, 0xF8, + 0xFC, 0x3F, 0x07, 0x00, 0x1E, 0x00, 0x1F, 0xC0, 0x1F, 0xF0, 0xDF, 0xFC, + 0xFF, 0x3F, 0xFB, 0x0F, 0xF8, 0x03, 0xF8, 0x00, 0x78}; + +const GFXglyph FreeMonoBold18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 21, 0, 1}, // 0x20 ' ' + {0, 5, 22, 21, 8, -21}, // 0x21 '!' + {14, 11, 10, 21, 5, -20}, // 0x22 '"' + {28, 16, 25, 21, 3, -22}, // 0x23 '#' + {78, 14, 28, 21, 4, -23}, // 0x24 '$' + {127, 15, 21, 21, 3, -20}, // 0x25 '%' + {167, 15, 20, 21, 3, -19}, // 0x26 '&' + {205, 4, 10, 21, 8, -20}, // 0x27 ''' + {210, 8, 27, 21, 9, -21}, // 0x28 '(' + {237, 8, 27, 21, 4, -21}, // 0x29 ')' + {264, 16, 15, 21, 3, -21}, // 0x2A '*' + {294, 16, 19, 21, 3, -18}, // 0x2B '+' + {332, 7, 10, 21, 5, -3}, // 0x2C ',' + {341, 19, 4, 21, 1, -11}, // 0x2D '-' + {351, 5, 5, 21, 8, -4}, // 0x2E '.' + {355, 15, 28, 21, 3, -23}, // 0x2F '/' + {408, 16, 23, 21, 3, -22}, // 0x30 '0' + {454, 15, 22, 21, 3, -21}, // 0x31 '1' + {496, 15, 23, 21, 3, -22}, // 0x32 '2' + {540, 16, 23, 21, 3, -22}, // 0x33 '3' + {586, 15, 21, 21, 3, -20}, // 0x34 '4' + {626, 17, 22, 21, 2, -21}, // 0x35 '5' + {673, 15, 23, 21, 4, -22}, // 0x36 '6' + {717, 15, 22, 21, 3, -21}, // 0x37 '7' + {759, 15, 23, 21, 3, -22}, // 0x38 '8' + {803, 15, 23, 21, 4, -22}, // 0x39 '9' + {847, 5, 16, 21, 8, -15}, // 0x3A ':' + {857, 7, 22, 21, 5, -15}, // 0x3B ';' + {877, 18, 16, 21, 1, -17}, // 0x3C '<' + {913, 19, 10, 21, 1, -14}, // 0x3D '=' + {937, 18, 16, 21, 2, -17}, // 0x3E '>' + {973, 15, 21, 21, 4, -20}, // 0x3F '?' + {1013, 15, 27, 21, 3, -21}, // 0x40 '@' + {1064, 22, 21, 21, -1, -20}, // 0x41 'A' + {1122, 20, 21, 21, 1, -20}, // 0x42 'B' + {1175, 19, 21, 21, 1, -20}, // 0x43 'C' + {1225, 18, 21, 21, 2, -20}, // 0x44 'D' + {1273, 19, 21, 21, 1, -20}, // 0x45 'E' + {1323, 19, 21, 21, 1, -20}, // 0x46 'F' + {1373, 20, 21, 21, 1, -20}, // 0x47 'G' + {1426, 21, 21, 21, 0, -20}, // 0x48 'H' + {1482, 14, 21, 21, 4, -20}, // 0x49 'I' + {1519, 19, 21, 21, 2, -20}, // 0x4A 'J' + {1569, 20, 21, 21, 1, -20}, // 0x4B 'K' + {1622, 18, 21, 21, 2, -20}, // 0x4C 'L' + {1670, 23, 21, 21, -1, -20}, // 0x4D 'M' + {1731, 20, 21, 21, 1, -20}, // 0x4E 'N' + {1784, 20, 21, 21, 1, -20}, // 0x4F 'O' + {1837, 18, 21, 21, 1, -20}, // 0x50 'P' + {1885, 20, 26, 21, 1, -20}, // 0x51 'Q' + {1950, 21, 21, 21, 0, -20}, // 0x52 'R' + {2006, 17, 21, 21, 2, -20}, // 0x53 'S' + {2051, 19, 21, 21, 1, -20}, // 0x54 'T' + {2101, 21, 21, 21, 0, -20}, // 0x55 'U' + {2157, 23, 21, 21, -1, -20}, // 0x56 'V' + {2218, 21, 21, 21, 0, -20}, // 0x57 'W' + {2274, 19, 21, 21, 1, -20}, // 0x58 'X' + {2324, 20, 21, 21, 1, -20}, // 0x59 'Y' + {2377, 16, 21, 21, 3, -20}, // 0x5A 'Z' + {2419, 8, 27, 21, 9, -21}, // 0x5B '[' + {2446, 15, 28, 21, 3, -23}, // 0x5C '\' + {2499, 8, 27, 21, 4, -21}, // 0x5D ']' + {2526, 15, 11, 21, 3, -21}, // 0x5E '^' + {2547, 21, 4, 21, 0, 4}, // 0x5F '_' + {2558, 6, 6, 21, 6, -22}, // 0x60 '`' + {2563, 19, 16, 21, 1, -15}, // 0x61 'a' + {2601, 19, 22, 21, 1, -21}, // 0x62 'b' + {2654, 17, 16, 21, 2, -15}, // 0x63 'c' + {2688, 20, 22, 21, 1, -21}, // 0x64 'd' + {2743, 18, 16, 21, 1, -15}, // 0x65 'e' + {2779, 16, 22, 21, 4, -21}, // 0x66 'f' + {2823, 19, 23, 21, 1, -15}, // 0x67 'g' + {2878, 21, 22, 21, 0, -21}, // 0x68 'h' + {2936, 16, 22, 21, 3, -21}, // 0x69 'i' + {2980, 12, 29, 21, 5, -21}, // 0x6A 'j' + {3024, 18, 22, 21, 2, -21}, // 0x6B 'k' + {3074, 16, 22, 21, 3, -21}, // 0x6C 'l' + {3118, 22, 16, 21, -1, -15}, // 0x6D 'm' + {3162, 20, 16, 21, 0, -15}, // 0x6E 'n' + {3202, 19, 16, 21, 1, -15}, // 0x6F 'o' + {3240, 21, 23, 21, 0, -15}, // 0x70 'p' + {3301, 21, 23, 22, 1, -15}, // 0x71 'q' + {3362, 17, 16, 21, 3, -15}, // 0x72 'r' + {3396, 16, 16, 21, 3, -15}, // 0x73 's' + {3428, 17, 21, 21, 1, -20}, // 0x74 't' + {3473, 18, 16, 21, 1, -15}, // 0x75 'u' + {3509, 21, 16, 21, 0, -15}, // 0x76 'v' + {3551, 21, 16, 21, 0, -15}, // 0x77 'w' + {3593, 19, 16, 21, 1, -15}, // 0x78 'x' + {3631, 19, 23, 21, 1, -15}, // 0x79 'y' + {3686, 14, 16, 21, 3, -15}, // 0x7A 'z' + {3714, 10, 27, 21, 6, -21}, // 0x7B '{' + {3748, 4, 27, 21, 9, -21}, // 0x7C '|' + {3762, 10, 27, 21, 6, -21}, // 0x7D '}' + {3796, 17, 8, 21, 2, -13}}; // 0x7E '~' + +const GFXfont FreeMonoBold18pt7b PROGMEM = { + (uint8_t *)FreeMonoBold18pt7bBitmaps, (GFXglyph *)FreeMonoBold18pt7bGlyphs, + 0x20, 0x7E, 35}; + +// Approx. 4485 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold24pt7b.h new file mode 100644 index 0000000..5ff07d4 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold24pt7b.h @@ -0,0 +1,671 @@ +const uint8_t FreeMonoBold24pt7bBitmaps[] PROGMEM = { + 0x38, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF3, 0xE7, 0xCF, + 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE3, 0x82, 0x00, 0x00, 0x00, 0x71, 0xF7, + 0xFF, 0xEF, 0x9E, 0x00, 0xFC, 0x7E, 0xF8, 0x7D, 0xF0, 0xFB, 0xE1, 0xF7, + 0xC3, 0xEF, 0x87, 0xDF, 0x0F, 0xBE, 0x1F, 0x38, 0x1C, 0x70, 0x38, 0xE0, + 0x71, 0xC0, 0xE3, 0x81, 0xC7, 0x03, 0x80, 0x01, 0xC1, 0xC0, 0x0F, 0x8F, + 0x80, 0x3E, 0x3E, 0x00, 0xF8, 0xF8, 0x03, 0xE3, 0xE0, 0x0F, 0x8F, 0x80, + 0x7E, 0x3E, 0x01, 0xF0, 0xF8, 0x07, 0xC7, 0xC0, 0x1F, 0x1F, 0x03, 0xFF, + 0xFF, 0x9F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, + 0x81, 0xF1, 0xF0, 0x07, 0xC7, 0xC0, 0x1F, 0x1F, 0x00, 0x7C, 0x7C, 0x1F, + 0xFF, 0xFC, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0x9F, 0xFF, + 0xFC, 0x0F, 0x8F, 0x80, 0x3E, 0x3E, 0x00, 0xF8, 0xF8, 0x03, 0xE3, 0xE0, + 0x0F, 0x8F, 0x80, 0x3E, 0x3E, 0x00, 0xF8, 0xF8, 0x03, 0xE3, 0xE0, 0x0F, + 0x8F, 0x80, 0x3C, 0x3C, 0x00, 0x00, 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x1F, 0xFF, 0x07, 0xFF, 0xF1, 0xFF, 0xFE, + 0x7F, 0xFF, 0xDF, 0xC1, 0xFB, 0xF0, 0x1F, 0x7C, 0x01, 0xEF, 0x80, 0x39, + 0xF8, 0x00, 0x3F, 0xF8, 0x03, 0xFF, 0xE0, 0x3F, 0xFF, 0x03, 0xFF, 0xF0, + 0x0F, 0xFF, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0xC0, 0x07, 0xF8, 0x00, 0xFF, + 0x80, 0x1F, 0xF8, 0x07, 0xFF, 0x81, 0xFB, 0xFF, 0xFF, 0x7F, 0xFF, 0xCF, + 0xFF, 0xF1, 0xDF, 0xFC, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, + 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x01, 0xC0, 0x00, + 0x0F, 0x80, 0x00, 0xFF, 0x00, 0x1F, 0xFC, 0x00, 0xF0, 0xE0, 0x0F, 0x07, + 0x80, 0x70, 0x1C, 0x03, 0x80, 0xE0, 0x1C, 0x07, 0x00, 0xF0, 0x78, 0x03, + 0xC3, 0x80, 0x1F, 0xFC, 0x00, 0x7F, 0xC1, 0xF0, 0xF8, 0x7F, 0x00, 0x3F, + 0xF0, 0x0F, 0xFC, 0x03, 0xFF, 0x00, 0xFF, 0xC0, 0x07, 0xE0, 0xF8, 0x38, + 0x1F, 0xE0, 0x01, 0xFF, 0x80, 0x0F, 0x1E, 0x00, 0xF0, 0x78, 0x07, 0x01, + 0xC0, 0x38, 0x0E, 0x01, 0xC0, 0x70, 0x0F, 0x07, 0x80, 0x38, 0x78, 0x01, + 0xFF, 0xC0, 0x07, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x1F, 0xFC, + 0x01, 0xFF, 0xE0, 0x1F, 0xFF, 0x00, 0xFF, 0xF8, 0x0F, 0xC7, 0x00, 0x7C, + 0x10, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, + 0x1F, 0x80, 0x00, 0xFE, 0x00, 0x0F, 0xF8, 0x00, 0xFF, 0xC7, 0xCF, 0xFF, + 0x3F, 0x7E, 0xFF, 0xFF, 0xE7, 0xFF, 0xBE, 0x1F, 0xF9, 0xF0, 0x7F, 0x8F, + 0x83, 0xFC, 0x7C, 0x0F, 0xE3, 0xF0, 0x7F, 0xCF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xF9, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x0F, 0xE0, 0x00, 0xFD, 0xF7, 0xDF, + 0x7D, 0xF7, 0xDF, 0x38, 0xE3, 0x8E, 0x38, 0xE0, 0x01, 0x80, 0xF0, 0x7C, + 0x3F, 0x0F, 0xC7, 0xE1, 0xF8, 0xFC, 0x3E, 0x0F, 0x87, 0xC1, 0xF0, 0x7C, + 0x1F, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, + 0x81, 0xF0, 0x7C, 0x1F, 0x07, 0xC0, 0xF8, 0x3E, 0x0F, 0xC1, 0xF0, 0x7E, + 0x0F, 0x83, 0xF0, 0x7C, 0x1F, 0x03, 0xC0, 0x60, 0x3C, 0x0F, 0x83, 0xF0, + 0xFC, 0x1F, 0x83, 0xE0, 0xFC, 0x1F, 0x07, 0xC1, 0xF8, 0x3E, 0x0F, 0x83, + 0xE0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, + 0x1E, 0x0F, 0x83, 0xE0, 0xF8, 0x7C, 0x1F, 0x0F, 0xC3, 0xE1, 0xF8, 0x7C, + 0x3F, 0x0F, 0x83, 0xE0, 0xF0, 0x00, 0x00, 0x70, 0x00, 0x07, 0xC0, 0x00, + 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x10, 0x7C, 0x11, 0xF3, 0xE7, + 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0x87, 0xFF, 0xF0, 0x07, + 0xFC, 0x00, 0x3F, 0xE0, 0x03, 0xFF, 0x80, 0x3F, 0x7E, 0x01, 0xFB, 0xF0, + 0x1F, 0x8F, 0xC0, 0xF8, 0x3E, 0x03, 0x80, 0xE0, 0x00, 0x38, 0x00, 0x00, + 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, + 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, + 0x01, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDF, 0xFF, 0xFF, 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, + 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, + 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x70, 0x00, 0x1F, + 0x8F, 0x87, 0xC7, 0xC3, 0xE1, 0xE1, 0xF0, 0xF0, 0x78, 0x38, 0x3C, 0x1C, + 0x0E, 0x06, 0x00, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0x7D, 0xFF, 0xFF, 0xFF, 0xEF, 0x80, + 0x00, 0x00, 0x60, 0x00, 0x0F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, + 0xF0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, + 0xF8, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, + 0x3E, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x0F, 0xC0, 0x00, 0xF8, 0x00, + 0x1F, 0x80, 0x01, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, + 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x1F, 0x00, + 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0xC0, + 0x00, 0xFC, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x3F, 0xF8, 0x03, 0xFF, 0xE0, 0x3F, 0xFF, 0x83, 0xFF, 0xFE, + 0x1F, 0x83, 0xF1, 0xF8, 0x0F, 0xCF, 0x80, 0x3E, 0x7C, 0x01, 0xF7, 0xC0, + 0x07, 0xFE, 0x00, 0x3F, 0xF0, 0x01, 0xFF, 0x80, 0x0F, 0xFC, 0x00, 0x7F, + 0xE0, 0x03, 0xFF, 0x00, 0x1F, 0xF8, 0x00, 0xFF, 0xC0, 0x07, 0xFE, 0x00, + 0x3F, 0xF0, 0x01, 0xFF, 0x80, 0x0F, 0xFC, 0x00, 0x7D, 0xF0, 0x07, 0xCF, + 0x80, 0x3E, 0x7E, 0x03, 0xF1, 0xF8, 0x3F, 0x0F, 0xFF, 0xF8, 0x3F, 0xFF, + 0x80, 0xFF, 0xF8, 0x03, 0xFF, 0x80, 0x07, 0xF0, 0x00, 0x01, 0xF8, 0x00, + 0x3F, 0x80, 0x0F, 0xF8, 0x01, 0xFF, 0x80, 0x7F, 0xF8, 0x0F, 0xEF, 0x80, + 0xFC, 0xF8, 0x07, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, + 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, + 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, + 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x3F, 0xFF, 0xE7, + 0xFF, 0xFF, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0x3F, 0xFF, 0xE0, 0x01, 0xFC, + 0x00, 0x3F, 0xF8, 0x07, 0xFF, 0xF0, 0x7F, 0xFF, 0xC7, 0xFF, 0xFF, 0x3F, + 0x03, 0xFB, 0xF0, 0x07, 0xFF, 0x00, 0x1F, 0xF8, 0x00, 0xFB, 0x80, 0x07, + 0xC0, 0x00, 0x3E, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xF8, 0x00, + 0x3F, 0x80, 0x03, 0xF8, 0x00, 0x3F, 0x80, 0x03, 0xF8, 0x00, 0x3F, 0x00, + 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x07, 0xE0, + 0x0E, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xF8, 0x00, 0xFF, 0xF8, 0x0F, 0xFF, + 0xE0, 0xFF, 0xFF, 0x8F, 0xFF, 0xFE, 0x7E, 0x03, 0xF1, 0xC0, 0x0F, 0xC0, + 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, 0x0F, + 0xC0, 0x0F, 0xFC, 0x00, 0xFF, 0xC0, 0x07, 0xFC, 0x00, 0x3F, 0xF0, 0x00, + 0xFF, 0xC0, 0x00, 0x7F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, + 0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x01, 0xFF, 0xC0, + 0x3F, 0xBF, 0xFF, 0xFD, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x1F, 0xFF, 0xC0, + 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0x80, 0x03, 0xF8, 0x00, 0x7F, 0x80, 0x07, + 0xF8, 0x00, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xEF, 0x80, 0x3E, 0xF8, 0x03, + 0xCF, 0x80, 0x7C, 0xF8, 0x0F, 0x8F, 0x80, 0xF0, 0xF8, 0x1F, 0x0F, 0x81, + 0xE0, 0xF8, 0x3E, 0x0F, 0x87, 0xC0, 0xF8, 0x78, 0x0F, 0x8F, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x0F, + 0x80, 0x07, 0xFE, 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0x00, 0xFF, 0xF0, 0x07, + 0xFE, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0x0F, 0xFF, 0xF8, 0x7F, 0xFF, 0xC3, + 0xFF, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, + 0x01, 0xF0, 0x00, 0x0F, 0xBF, 0x00, 0x7F, 0xFF, 0x03, 0xFF, 0xFC, 0x1F, + 0xFF, 0xF0, 0xFF, 0xFF, 0x83, 0xC0, 0xFE, 0x00, 0x01, 0xF0, 0x00, 0x0F, + 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, + 0x03, 0xE0, 0x00, 0x3F, 0xF0, 0x03, 0xF7, 0xE0, 0x3F, 0xBF, 0xFF, 0xF9, + 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x1F, 0xFF, 0x80, 0x1F, 0xF0, 0x00, 0x00, + 0x1F, 0xC0, 0x0F, 0xFF, 0x01, 0xFF, 0xF0, 0x7F, 0xFF, 0x0F, 0xFF, 0xE1, + 0xFF, 0x00, 0x1F, 0xC0, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x07, 0xE0, 0x00, + 0x7C, 0x00, 0x0F, 0x8F, 0xC0, 0xF9, 0xFF, 0x0F, 0xFF, 0xF8, 0xFF, 0xFF, + 0xCF, 0xFF, 0xFC, 0xFF, 0x0F, 0xEF, 0xE0, 0x3E, 0xFC, 0x03, 0xFF, 0x80, + 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xF7, 0xC0, 0x3F, 0x7E, + 0x03, 0xF3, 0xF0, 0x7E, 0x3F, 0xFF, 0xE1, 0xFF, 0xFC, 0x0F, 0xFF, 0x80, + 0x7F, 0xF0, 0x01, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xF0, 0x03, 0xE0, 0x00, + 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, + 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x1F, 0x00, + 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, + 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x06, 0x00, 0x01, 0xF8, 0x00, 0xFF, + 0xF0, 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x7F, 0xFF, 0xE7, 0xE0, 0x7E, 0xFC, + 0x03, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xF7, + 0xC0, 0x3E, 0x7E, 0x07, 0xE3, 0xFF, 0xFC, 0x0F, 0xFF, 0x00, 0xFF, 0xF0, + 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x7F, 0x0F, 0xE7, 0xC0, 0x3E, 0xF8, 0x01, + 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xFC, 0x03, 0xF7, 0xE0, + 0x7E, 0x7F, 0xFF, 0xE3, 0xFF, 0xFC, 0x1F, 0xFF, 0x80, 0xFF, 0xF0, 0x03, + 0xFC, 0x00, 0x03, 0xF8, 0x00, 0xFF, 0xE0, 0x1F, 0xFF, 0x83, 0xFF, 0xF8, + 0x7F, 0xFF, 0xC7, 0xE0, 0xFE, 0xFC, 0x03, 0xEF, 0x80, 0x3E, 0xF8, 0x01, + 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x3F, 0xFC, 0x07, 0xF7, 0xE0, + 0xFF, 0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x1F, 0xFF, 0xF0, 0xFF, 0x9F, 0x03, + 0xF1, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xE0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, + 0x01, 0xFC, 0x00, 0x3F, 0x80, 0x0F, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xC0, + 0xFF, 0xF8, 0x0F, 0xFF, 0x00, 0x3F, 0x80, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, + 0xEF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xFF, + 0xFF, 0xFF, 0xEF, 0x80, 0x0F, 0x87, 0xF1, 0xFC, 0x7F, 0x1F, 0xC3, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x1F, 0x87, 0xE1, 0xF0, 0xFC, 0x3E, 0x0F, 0x03, 0xC1, 0xE0, 0x78, 0x1C, + 0x07, 0x01, 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x7F, + 0x00, 0x01, 0xFE, 0x00, 0x07, 0xFC, 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0xC0, + 0x01, 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x1F, 0xF8, 0x00, 0x7F, 0xE0, 0x00, + 0xFF, 0xE0, 0x00, 0x1F, 0xF8, 0x00, 0x07, 0xFE, 0x00, 0x01, 0xFF, 0x80, + 0x00, 0x7F, 0xE0, 0x00, 0x1F, 0xF8, 0x00, 0x07, 0xFC, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x1E, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x07, + 0xFC, 0x00, 0x03, 0xFE, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x7F, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xF0, 0x01, + 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, + 0x00, 0xFF, 0x80, 0x03, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x01, 0xFF, 0xF0, 0xFF, 0xFF, 0x8F, + 0xFF, 0xFC, 0xFF, 0xFF, 0xEF, 0xC0, 0x7E, 0xF8, 0x03, 0xFF, 0x80, 0x1F, + 0x70, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x3F, + 0xE0, 0x0F, 0xFC, 0x01, 0xFF, 0x00, 0x0F, 0xC0, 0x00, 0xF0, 0x00, 0x0F, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x00, 0x03, 0xF8, 0x00, 0x3F, 0x80, 0x03, 0xF8, 0x00, + 0x3F, 0x80, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0xFF, 0x80, 0x3F, 0xF8, + 0x0F, 0xFF, 0x83, 0xE0, 0xF8, 0x78, 0x07, 0x1E, 0x00, 0xF3, 0x80, 0x0E, + 0x70, 0x01, 0xDE, 0x00, 0x3B, 0x80, 0x3F, 0x70, 0x1F, 0xEE, 0x07, 0xFD, + 0xC1, 0xFF, 0xB8, 0x7E, 0x77, 0x0F, 0x0E, 0xE3, 0xC1, 0xDC, 0x70, 0x3B, + 0x8E, 0x07, 0x71, 0xC0, 0xEE, 0x3C, 0x1D, 0xC3, 0xC3, 0xB8, 0x7F, 0xF7, + 0x07, 0xFF, 0xE0, 0x7F, 0xFC, 0x03, 0xFB, 0xC0, 0x00, 0x38, 0x00, 0x07, + 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x61, 0xF0, 0x3E, 0x1F, 0xFF, 0xC3, + 0xFF, 0xF0, 0x1F, 0xFC, 0x01, 0xFC, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x7F, + 0xFE, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x7F, 0xFE, + 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x01, 0xF7, 0xC0, 0x00, 0x0F, 0xBE, 0x00, + 0x00, 0x7D, 0xF8, 0x00, 0x07, 0xC7, 0xC0, 0x00, 0x3E, 0x3E, 0x00, 0x03, + 0xE0, 0xF8, 0x00, 0x1F, 0x07, 0xC0, 0x00, 0xF0, 0x3F, 0x00, 0x0F, 0x80, + 0xF8, 0x00, 0x7F, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xF8, + 0x03, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0x00, 0xF8, 0x00, 0xF8, 0x0F, + 0x80, 0x03, 0xE1, 0xFF, 0x80, 0xFF, 0xDF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, + 0x7F, 0xFF, 0xFF, 0x83, 0xFF, 0xDF, 0xF8, 0x0F, 0xFC, 0x7F, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0xFE, 0x07, 0xC0, 0x1F, 0xC1, 0xF0, 0x01, 0xF0, 0x7C, 0x00, 0x7C, 0x1F, + 0x00, 0x1F, 0x07, 0xC0, 0x0F, 0xC1, 0xF0, 0x07, 0xE0, 0x7F, 0xFF, 0xF0, + 0x1F, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, + 0xFC, 0x1F, 0x00, 0x3F, 0x87, 0xC0, 0x03, 0xF1, 0xF0, 0x00, 0x7C, 0x7C, + 0x00, 0x1F, 0x1F, 0x00, 0x07, 0xC7, 0xC0, 0x03, 0xF7, 0xFF, 0xFF, 0xFB, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0xE7, 0x01, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFF, 0xF1, 0xFE, 0x07, 0xF8, 0xFC, 0x01, 0xFC, 0xFC, + 0x00, 0x7E, 0x7C, 0x00, 0x1F, 0x7E, 0x00, 0x0F, 0xBE, 0x00, 0x03, 0x9F, + 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x39, 0xFC, 0x00, + 0x7C, 0x7F, 0x80, 0xFF, 0x1F, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0x81, 0xFF, + 0xFF, 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x7F, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC1, + 0xF0, 0x0F, 0xF0, 0xF8, 0x01, 0xF8, 0x7C, 0x00, 0x7E, 0x3E, 0x00, 0x1F, + 0x1F, 0x00, 0x0F, 0xCF, 0x80, 0x03, 0xE7, 0xC0, 0x01, 0xF3, 0xE0, 0x00, + 0xF9, 0xF0, 0x00, 0x7C, 0xF8, 0x00, 0x3E, 0x7C, 0x00, 0x1F, 0x3E, 0x00, + 0x0F, 0x9F, 0x00, 0x07, 0xCF, 0x80, 0x07, 0xE7, 0xC0, 0x03, 0xE3, 0xE0, + 0x03, 0xF1, 0xF0, 0x07, 0xF1, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF8, 0xFF, + 0xFF, 0xF8, 0x7F, 0xFF, 0xF0, 0x1F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xFF, + 0x7F, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, + 0xF0, 0xF8, 0x00, 0xF8, 0x7C, 0x00, 0x7C, 0x3E, 0x0E, 0x3E, 0x1F, 0x0F, + 0x9F, 0x0F, 0x87, 0xC7, 0x07, 0xC3, 0xE0, 0x03, 0xFF, 0xF0, 0x01, 0xFF, + 0xF8, 0x00, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x3F, 0xFF, 0x00, 0x1F, + 0x0F, 0x80, 0x0F, 0x87, 0xC3, 0x87, 0xC1, 0xC3, 0xE3, 0xE0, 0x01, 0xF1, + 0xF0, 0x00, 0xF8, 0xF8, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, + 0xFF, 0xF8, 0xF8, 0x00, 0x7C, 0x7C, 0x00, 0x3E, 0x3E, 0x00, 0x1F, 0x1F, + 0x07, 0x0F, 0x8F, 0x87, 0xC3, 0x87, 0xC3, 0xE0, 0x03, 0xFF, 0xF0, 0x01, + 0xFF, 0xF8, 0x00, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x3F, 0xFF, 0x00, + 0x1F, 0x0F, 0x80, 0x0F, 0x87, 0xC0, 0x07, 0xC3, 0xE0, 0x03, 0xE0, 0xE0, + 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xFF, 0xF0, 0x01, 0xFF, 0xFC, + 0x00, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, + 0x7F, 0x8E, 0x00, 0xFF, 0xF7, 0x81, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xE1, + 0xFF, 0xFF, 0xF1, 0xFE, 0x03, 0xF8, 0xFC, 0x00, 0xFC, 0xFC, 0x00, 0x3E, + 0x7C, 0x00, 0x1F, 0x7E, 0x00, 0x07, 0x3E, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF0, 0x0F, + 0xFE, 0xF8, 0x0F, 0xFF, 0xFC, 0x07, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0xC0, 0x01, 0xF3, 0xF0, 0x00, 0xF9, 0xFC, 0x00, 0x7C, 0x7F, + 0x80, 0xFE, 0x3F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x80, + 0x7F, 0xFF, 0x00, 0x07, 0xFC, 0x00, 0x3F, 0xE1, 0xFF, 0x1F, 0xFC, 0xFF, + 0xE7, 0xFF, 0x3F, 0xF9, 0xFF, 0xCF, 0xFE, 0x3F, 0xE1, 0xFF, 0x07, 0xC0, + 0x0F, 0x81, 0xF0, 0x03, 0xE0, 0x7C, 0x00, 0xF8, 0x1F, 0x00, 0x3E, 0x07, + 0xC0, 0x0F, 0x81, 0xF0, 0x03, 0xE0, 0x7F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFE, + 0x07, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xF8, 0x1F, 0x00, + 0x3E, 0x07, 0xC0, 0x0F, 0x81, 0xF0, 0x03, 0xE0, 0x7C, 0x00, 0xF8, 0x1F, + 0x00, 0x3E, 0x07, 0xC0, 0x0F, 0x87, 0xFE, 0x1F, 0xFB, 0xFF, 0xCF, 0xFF, + 0xFF, 0xF3, 0xFF, 0xFF, 0xFC, 0xFF, 0xF7, 0xFE, 0x1F, 0xF8, 0x7F, 0xFF, + 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFC, 0x03, 0xE0, + 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, + 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, + 0x03, 0xE0, 0x1F, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, + 0xE0, 0x3F, 0xFF, 0xF0, 0x0F, 0xFF, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x07, + 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x07, 0xC0, 0xE0, 0x03, 0xE0, 0xF8, 0x01, 0xF0, 0x7C, 0x00, 0xF8, 0x3E, + 0x00, 0x7C, 0x1F, 0x00, 0x3E, 0x0F, 0x80, 0x1F, 0x07, 0xC0, 0x1F, 0x83, + 0xF8, 0x3F, 0x81, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xC0, + 0x07, 0xFF, 0xC0, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0xE0, 0xFF, 0x9F, 0xFE, + 0x3F, 0xFB, 0xFF, 0xC7, 0xFF, 0x7F, 0xF8, 0xFF, 0xE7, 0xFE, 0x0F, 0xF8, + 0x3E, 0x01, 0xF8, 0x07, 0xC0, 0xFE, 0x00, 0xF8, 0x3F, 0x80, 0x1F, 0x0F, + 0xE0, 0x03, 0xE3, 0xF8, 0x00, 0x7D, 0xFC, 0x00, 0x0F, 0xFF, 0x00, 0x01, + 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0xFE, 0x7F, + 0x00, 0x1F, 0x87, 0xF0, 0x03, 0xE0, 0x7E, 0x00, 0x7C, 0x07, 0xE0, 0x0F, + 0x80, 0x7E, 0x01, 0xF0, 0x0F, 0xC0, 0x3E, 0x00, 0xF8, 0x1F, 0xF8, 0x1F, + 0xF7, 0xFF, 0x81, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFE, 0x07, 0xFD, 0xFF, + 0x80, 0x7F, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x3F, 0xFF, 0x80, + 0x1F, 0xFF, 0xC0, 0x07, 0xFF, 0xC0, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x0F, 0x80, 0x0E, 0x07, 0xC0, 0x0F, 0x83, 0xE0, 0x07, 0xC1, + 0xF0, 0x03, 0xE0, 0xF8, 0x01, 0xF0, 0x7C, 0x00, 0xF8, 0x3E, 0x00, 0x7D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDF, 0xFF, 0xFF, 0xE0, 0x3F, 0x80, 0x03, 0xF8, 0xFF, 0x80, 0x0F, 0xF9, + 0xFF, 0x00, 0x1F, 0xF3, 0xFF, 0x00, 0x7F, 0xE3, 0xFE, 0x00, 0xFF, 0x83, + 0xFE, 0x03, 0xFE, 0x07, 0xFC, 0x07, 0xFC, 0x0F, 0xFC, 0x1F, 0xF8, 0x1F, + 0xF8, 0x3F, 0xF0, 0x3F, 0xF0, 0x7F, 0xE0, 0x7D, 0xF1, 0xF7, 0xC0, 0xFB, + 0xE3, 0xEF, 0x81, 0xF7, 0xEF, 0xDF, 0x03, 0xE7, 0xDF, 0x3E, 0x07, 0xCF, + 0xFE, 0x7C, 0x0F, 0x8F, 0xF8, 0xF8, 0x1F, 0x1F, 0xF1, 0xF0, 0x3E, 0x1F, + 0xE3, 0xE0, 0x7C, 0x3F, 0x87, 0xC0, 0xF8, 0x3F, 0x0F, 0x81, 0xF0, 0x00, + 0x1F, 0x03, 0xE0, 0x00, 0x3E, 0x1F, 0xF8, 0x03, 0xFF, 0x7F, 0xF8, 0x0F, + 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFD, 0xFF, 0x80, 0x3F, + 0xF0, 0x7F, 0x00, 0x7F, 0xEF, 0xF8, 0x0F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, + 0xFC, 0x0F, 0xFF, 0x7F, 0xE0, 0x7F, 0xE1, 0xFF, 0x00, 0xF8, 0x1F, 0xF0, + 0x0F, 0x81, 0xFF, 0x80, 0xF8, 0x1F, 0xFC, 0x0F, 0x81, 0xFF, 0xC0, 0xF8, + 0x1F, 0x7E, 0x0F, 0x81, 0xF3, 0xF0, 0xF8, 0x1F, 0x3F, 0x0F, 0x81, 0xF1, + 0xF8, 0xF8, 0x1F, 0x0F, 0xCF, 0x81, 0xF0, 0xFC, 0xF8, 0x1F, 0x07, 0xEF, + 0x81, 0xF0, 0x3F, 0xF8, 0x1F, 0x03, 0xFF, 0x81, 0xF0, 0x1F, 0xF8, 0x1F, + 0x00, 0xFF, 0x81, 0xF0, 0x0F, 0xF8, 0x7F, 0xE0, 0x7F, 0x8F, 0xFF, 0x03, + 0xF8, 0xFF, 0xF0, 0x3F, 0x8F, 0xFF, 0x01, 0xF8, 0x7F, 0xE0, 0x0F, 0x80, + 0x00, 0x3F, 0x80, 0x00, 0x3F, 0xFC, 0x00, 0x0F, 0xFF, 0xE0, 0x03, 0xFF, + 0xFE, 0x00, 0xFF, 0xFF, 0xE0, 0x3F, 0xC1, 0xFE, 0x0F, 0xE0, 0x0F, 0xE1, + 0xF8, 0x00, 0xFC, 0x7E, 0x00, 0x0F, 0xCF, 0x80, 0x00, 0xFB, 0xF0, 0x00, + 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x3F, 0xF0, 0x00, 0x07, 0xFE, + 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x1F, 0xF8, 0x00, 0x03, 0xFF, 0x00, 0x00, + 0x7F, 0xF0, 0x00, 0x1F, 0xBE, 0x00, 0x03, 0xE7, 0xE0, 0x00, 0xFC, 0x7E, + 0x00, 0x3F, 0x0F, 0xE0, 0x0F, 0xE0, 0xFF, 0x07, 0xF8, 0x0F, 0xFF, 0xFE, + 0x00, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xE0, 0x00, 0xFF, 0xF8, 0x00, 0x03, + 0xF8, 0x00, 0x7F, 0xFF, 0x80, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, + 0xFF, 0xFC, 0x7F, 0xFF, 0xFE, 0x1F, 0x00, 0xFE, 0x1F, 0x00, 0x3F, 0x1F, + 0x00, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, + 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFC, 0x1F, + 0xFF, 0xF8, 0x1F, 0xFF, 0xF0, 0x1F, 0xFF, 0x80, 0x1F, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0xFF, + 0xFE, 0x00, 0xFF, 0xFE, 0x00, 0xFF, 0xFE, 0x00, 0x7F, 0xFC, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x3F, 0xFC, 0x00, 0x0F, 0xFF, 0xE0, 0x03, 0xFF, 0xFE, + 0x00, 0xFF, 0xFF, 0xE0, 0x3F, 0xC1, 0xFE, 0x0F, 0xE0, 0x0F, 0xE1, 0xF8, + 0x00, 0xFC, 0x7E, 0x00, 0x0F, 0xCF, 0x80, 0x00, 0xFB, 0xF0, 0x00, 0x1F, + 0xFC, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x3F, 0xF0, 0x00, 0x07, 0xFE, 0x00, + 0x00, 0xFF, 0xC0, 0x00, 0x1F, 0xF8, 0x00, 0x03, 0xFF, 0x80, 0x00, 0xFD, + 0xF0, 0x00, 0x1F, 0x3F, 0x00, 0x07, 0xE7, 0xF0, 0x01, 0xF8, 0x7F, 0x00, + 0x7F, 0x07, 0xF8, 0x3F, 0xC0, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFC, 0x00, + 0x7F, 0xFF, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x7F, 0xC0, 0x00, 0x0F, 0x00, + 0x00, 0x03, 0xFF, 0x87, 0x80, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0x07, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x0F, 0x01, 0xF8, 0x00, 0x7F, 0xFF, + 0x80, 0x0F, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xC0, + 0x7F, 0xFF, 0xFE, 0x00, 0xF8, 0x07, 0xE0, 0x0F, 0x80, 0x3F, 0x00, 0xF8, + 0x01, 0xF0, 0x0F, 0x80, 0x1F, 0x00, 0xF8, 0x01, 0xF0, 0x0F, 0x80, 0x3F, + 0x00, 0xF8, 0x0F, 0xE0, 0x0F, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xC0, 0x0F, + 0xFF, 0xF0, 0x00, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0xF8, 0x3F, + 0x80, 0x0F, 0x81, 0xFC, 0x00, 0xF8, 0x0F, 0xE0, 0x0F, 0x80, 0x7E, 0x00, + 0xF8, 0x03, 0xF0, 0x7F, 0xF0, 0x1F, 0xEF, 0xFF, 0x81, 0xFF, 0xFF, 0xF8, + 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0x7F, 0xF0, 0x07, 0xE0, 0x01, 0xFC, 0x70, + 0x1F, 0xFD, 0xE0, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, 0xFC, + 0x0F, 0xE7, 0xE0, 0x1F, 0x9F, 0x00, 0x3E, 0x7C, 0x00, 0xF9, 0xF0, 0x01, + 0xC7, 0xF0, 0x00, 0x0F, 0xF8, 0x00, 0x3F, 0xFF, 0x00, 0x7F, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, 0xFF, 0xFC, 0x00, 0x1F, 0xF8, 0x00, 0x07, 0xE0, 0x00, + 0x0F, 0xDC, 0x00, 0x1F, 0xF8, 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0xC0, 0x0F, + 0xFF, 0xC0, 0xFE, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xCF, 0xFF, 0xFE, 0x1C, + 0xFF, 0xF0, 0x00, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x7F, + 0xE0, 0xF8, 0x3F, 0xF0, 0x7C, 0x1F, 0xF8, 0x3E, 0x0F, 0xFC, 0x1F, 0x07, + 0xFE, 0x0F, 0x83, 0xEE, 0x07, 0xC0, 0xE0, 0x03, 0xE0, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, + 0xF0, 0x00, 0x0F, 0xFF, 0x80, 0x0F, 0xFF, 0xE0, 0x07, 0xFF, 0xF0, 0x03, + 0xFF, 0xF8, 0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xE0, 0x7F, 0xEF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0x7F, 0xE0, 0x7F, 0xE1, + 0xF0, 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0xF8, 0x1F, 0x00, + 0x0F, 0x81, 0xF0, 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0xF8, + 0x1F, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0x81, 0xF0, + 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0xF8, 0x1F, 0x00, 0x0F, + 0x81, 0xF0, 0x00, 0xF8, 0x1F, 0x80, 0x1F, 0x80, 0xF8, 0x01, 0xF0, 0x0F, + 0xE0, 0x7F, 0x00, 0x7F, 0xFF, 0xE0, 0x03, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0xFF, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x7F, 0xE0, 0x1F, 0xFB, + 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0x7F, + 0xE0, 0x1F, 0xF8, 0x7C, 0x00, 0x0F, 0x80, 0xF8, 0x00, 0x7C, 0x03, 0xE0, + 0x01, 0xF0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7E, 0x00, + 0xF8, 0x00, 0xF8, 0x07, 0xC0, 0x03, 0xF0, 0x1F, 0x00, 0x07, 0xC0, 0xF8, + 0x00, 0x1F, 0x03, 0xE0, 0x00, 0x7E, 0x1F, 0x00, 0x00, 0xF8, 0x7C, 0x00, + 0x03, 0xF3, 0xF0, 0x00, 0x07, 0xCF, 0x80, 0x00, 0x1F, 0xBE, 0x00, 0x00, + 0x3F, 0xF0, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, + 0xF8, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x7F, 0xE0, 0x7F, 0xEF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, + 0xFF, 0xFF, 0x0F, 0xFF, 0x7F, 0xE0, 0x7F, 0xE3, 0xE0, 0x00, 0x3C, 0x3E, + 0x0F, 0x83, 0xC3, 0xE1, 0xF8, 0x3C, 0x3E, 0x1F, 0x87, 0xC3, 0xE1, 0xFC, + 0x7C, 0x3E, 0x3F, 0xC7, 0xC1, 0xE3, 0xFC, 0x7C, 0x1F, 0x3F, 0xE7, 0xC1, + 0xF7, 0xFE, 0x78, 0x1F, 0x7F, 0xE7, 0x81, 0xF7, 0x9F, 0xF8, 0x1F, 0xF9, + 0xFF, 0x81, 0xFF, 0x9F, 0xF8, 0x0F, 0xF9, 0xFF, 0x80, 0xFF, 0x0F, 0xF8, + 0x0F, 0xF0, 0xFF, 0x80, 0xFF, 0x0F, 0xF0, 0x0F, 0xE0, 0x7F, 0x00, 0xFE, + 0x07, 0xF0, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0xF0, 0x07, 0xC0, 0x3F, + 0x00, 0x7F, 0x80, 0xFF, 0x3F, 0xF0, 0x7F, 0xEF, 0xFC, 0x1F, 0xFB, 0xFF, + 0x07, 0xFE, 0x7F, 0x80, 0xFF, 0x07, 0xE0, 0x3F, 0x00, 0xFC, 0x0F, 0x80, + 0x1F, 0x87, 0xC0, 0x03, 0xF3, 0xE0, 0x00, 0xFF, 0xF8, 0x00, 0x1F, 0xFC, + 0x00, 0x03, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x07, + 0xF0, 0x00, 0x03, 0xFE, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0xFC, 0xF8, 0x00, + 0x7E, 0x3F, 0x00, 0x3F, 0x07, 0xE0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x1F, + 0x07, 0xFC, 0x0F, 0xFB, 0xFF, 0x87, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xF8, + 0x7F, 0xF7, 0xFC, 0x0F, 0xF8, 0x7F, 0x80, 0x7F, 0xBF, 0xF0, 0x3F, 0xFF, + 0xFC, 0x0F, 0xFF, 0xFF, 0x03, 0xFF, 0x7F, 0x80, 0x7F, 0x87, 0xE0, 0x1F, + 0x80, 0xFC, 0x07, 0xC0, 0x1F, 0x03, 0xE0, 0x03, 0xE1, 0xF8, 0x00, 0xFC, + 0x7C, 0x00, 0x1F, 0xBE, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x7F, 0xC0, 0x00, + 0x1F, 0xE0, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x00, + 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x1F, 0xFF, 0x00, 0x0F, 0xFF, 0xE0, 0x03, + 0xFF, 0xF8, 0x00, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0x00, 0x7F, 0xFF, 0xF3, + 0xFF, 0xFF, 0x9F, 0xFF, 0xFC, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0x3E, 0x03, + 0xF1, 0xF0, 0x1F, 0x8F, 0x81, 0xF8, 0x7C, 0x1F, 0x83, 0xE1, 0xF8, 0x0E, + 0x1F, 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, + 0x00, 0xFC, 0x00, 0x0F, 0xE0, 0x70, 0x7E, 0x07, 0xC7, 0xE0, 0x3E, 0x7E, + 0x01, 0xF7, 0xE0, 0x0F, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xBF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xBE, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, + 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, + 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, + 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x60, 0x00, 0x0F, 0x00, 0x00, + 0xF8, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7E, 0x00, + 0x03, 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xF8, + 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x03, + 0xE0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, + 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xF0, + 0x00, 0x1F, 0x00, 0x01, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x07, + 0xC0, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, + 0x1F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, + 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, + 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x7F, 0xC0, 0x00, 0x40, 0x00, 0x06, 0x00, 0x00, 0xF0, + 0x00, 0x1F, 0x80, 0x03, 0xFC, 0x00, 0x7F, 0xE0, 0x0F, 0xFF, 0x00, 0xFF, + 0xF8, 0x1F, 0x9F, 0x83, 0xF0, 0xFC, 0x7E, 0x07, 0xEF, 0xC0, 0x3F, 0xF8, + 0x01, 0xFF, 0x80, 0x0F, 0x70, 0x00, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xE0, 0x78, 0x3E, 0x0F, 0xC3, 0xF0, 0x7C, 0x1E, 0x06, 0x01, 0xFF, + 0x00, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0xE0, 0x1F, 0xFF, 0xF0, 0x0F, 0xFF, + 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x01, 0xFF, + 0xF8, 0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xF8, 0x3F, 0xFF, 0xF8, 0x7F, 0xFF, + 0xF8, 0x7F, 0x00, 0xF8, 0xFC, 0x00, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x03, + 0xF8, 0xFC, 0x0F, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x3F, 0xFF, + 0xFF, 0x1F, 0xFE, 0xFE, 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x00, 0x1F, 0xE0, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x0F, + 0xE0, 0x03, 0xEF, 0xFF, 0x00, 0x7F, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0x81, + 0xFF, 0xFF, 0xF8, 0x3F, 0xE0, 0x7F, 0x07, 0xF0, 0x03, 0xF0, 0xFC, 0x00, + 0x3E, 0x1F, 0x80, 0x07, 0xE3, 0xE0, 0x00, 0x7C, 0x7C, 0x00, 0x0F, 0x8F, + 0x80, 0x01, 0xF1, 0xF0, 0x00, 0x3E, 0x3E, 0x00, 0x07, 0xC7, 0xE0, 0x01, + 0xF8, 0xFC, 0x00, 0x3E, 0x1F, 0xC0, 0x0F, 0xCF, 0xFE, 0x07, 0xF3, 0xFF, + 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xE0, 0xFE, 0x7F, 0xF0, + 0x00, 0x03, 0xF8, 0x00, 0x00, 0xFF, 0x18, 0x03, 0xFF, 0xFC, 0x0F, 0xFF, + 0xFC, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, 0x81, 0xFC, 0x7E, 0x00, + 0x7C, 0x7C, 0x00, 0x7C, 0xFC, 0x00, 0x3C, 0xF8, 0x00, 0x38, 0xF8, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x7C, 0x00, 0x06, 0x7E, 0x00, 0x1F, 0x7F, 0x80, 0x7F, 0x3F, 0xFF, + 0xFF, 0x1F, 0xFF, 0xFE, 0x0F, 0xFF, 0xFC, 0x07, 0xFF, 0xF8, 0x00, 0xFF, + 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xE0, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0xF8, 0x00, 0xFE, 0x3E, 0x00, 0xFF, 0xEF, 0x80, 0xFF, 0xFF, + 0xE0, 0x7F, 0xFF, 0xF8, 0x3F, 0xFF, 0xFE, 0x1F, 0xE0, 0xFF, 0x87, 0xE0, + 0x0F, 0xE1, 0xF0, 0x01, 0xF8, 0xFC, 0x00, 0x7E, 0x3E, 0x00, 0x0F, 0x8F, + 0x80, 0x03, 0xE3, 0xE0, 0x00, 0xF8, 0xF8, 0x00, 0x3E, 0x3E, 0x00, 0x0F, + 0x8F, 0xC0, 0x07, 0xE1, 0xF0, 0x01, 0xF8, 0x7E, 0x00, 0xFE, 0x0F, 0xE0, + 0x7F, 0xE3, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xC0, + 0xFF, 0xEF, 0xE0, 0x0F, 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x03, 0xFF, 0xC0, + 0x0F, 0xFF, 0xE0, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, 0x7F, 0x81, 0xFC, + 0x7E, 0x00, 0x7E, 0xFC, 0x00, 0x3E, 0xF8, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7F, 0x80, 0x7E, + 0x3F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFE, 0x07, 0xFF, 0xF8, + 0x00, 0xFF, 0x80, 0x00, 0x3F, 0xE0, 0x03, 0xFF, 0xE0, 0x1F, 0xFF, 0xC0, + 0xFF, 0xFF, 0x07, 0xFF, 0xF8, 0x1F, 0x80, 0x00, 0x7C, 0x00, 0x01, 0xF0, + 0x00, 0x07, 0xC0, 0x01, 0xFF, 0xFF, 0x0F, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, + 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x07, + 0xC0, 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xC0, + 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x01, + 0xFF, 0xFF, 0x0F, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0xE1, 0xFF, + 0xFF, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xFF, 0xBF, 0x83, 0xFF, 0xFF, 0xE3, + 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFB, 0xFC, 0x3F, 0xF9, 0xF8, 0x07, 0xF0, + 0xF8, 0x01, 0xF8, 0xFC, 0x00, 0xFC, 0x7C, 0x00, 0x3E, 0x3E, 0x00, 0x1F, + 0x1F, 0x00, 0x0F, 0x8F, 0x80, 0x07, 0xC7, 0xC0, 0x03, 0xE3, 0xF0, 0x03, + 0xF0, 0xF8, 0x01, 0xF8, 0x7E, 0x01, 0xFC, 0x3F, 0xC3, 0xFE, 0x0F, 0xFF, + 0xFF, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xC0, 0x3F, 0xFB, 0xE0, 0x07, + 0xF1, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xFE, 0x00, + 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x00, 0x3F, 0xFE, 0x00, + 0x0F, 0xFC, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x0F, 0xC0, 0x07, 0xCF, 0xFC, 0x01, + 0xF7, 0xFF, 0x80, 0x7F, 0xFF, 0xF0, 0x1F, 0xFF, 0xFC, 0x07, 0xFC, 0x1F, + 0x81, 0xFC, 0x03, 0xE0, 0x7E, 0x00, 0xF8, 0x1F, 0x00, 0x3E, 0x07, 0xC0, + 0x0F, 0x81, 0xF0, 0x03, 0xE0, 0x7C, 0x00, 0xF8, 0x1F, 0x00, 0x3E, 0x07, + 0xC0, 0x0F, 0x81, 0xF0, 0x03, 0xE0, 0x7C, 0x00, 0xF8, 0x1F, 0x00, 0x3E, + 0x1F, 0xF0, 0x3F, 0xEF, 0xFE, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xE1, + 0xFF, 0xDF, 0xF0, 0x3F, 0xE0, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, + 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xF8, 0x01, 0xFF, 0xC0, 0x0F, 0xFE, 0x00, 0x7F, 0xF0, + 0x01, 0xFF, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, + 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, + 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x7F, 0xFF, 0xF7, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0x00, 0x00, 0x7C, + 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, + 0xFF, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, + 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, + 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x3F, 0xBF, 0xFF, 0xBF, 0xFF, + 0x9F, 0xFF, 0xCF, 0xFF, 0x83, 0xFF, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x80, + 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, + 0x87, 0xFC, 0x07, 0xC7, 0xFF, 0x03, 0xE3, 0xFF, 0x81, 0xF1, 0xFF, 0xC0, + 0xF8, 0x7F, 0xC0, 0x7C, 0xFE, 0x00, 0x3E, 0xFE, 0x00, 0x1F, 0xFE, 0x00, + 0x0F, 0xFE, 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x80, 0x01, 0xFF, 0xE0, + 0x00, 0xFF, 0xF8, 0x00, 0x7C, 0xFE, 0x00, 0x3E, 0x3F, 0x80, 0x1F, 0x0F, + 0xE0, 0x3F, 0x81, 0xFF, 0xBF, 0xC1, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF0, + 0x7F, 0xFB, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x01, 0xFF, 0xC0, 0x0F, 0xFE, + 0x00, 0x7F, 0xF0, 0x01, 0xFF, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, + 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, + 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, + 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, + 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0xFF, + 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xF8, + 0x00, 0x3C, 0x1F, 0x00, 0xFD, 0xFC, 0xFF, 0x07, 0xFF, 0xFF, 0xFE, 0x1F, + 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0xFF, 0x1F, 0x87, 0xC1, 0xF8, + 0x7E, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, + 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, + 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, + 0x1F, 0x07, 0xC1, 0xF1, 0xFE, 0x1F, 0x87, 0xEF, 0xFC, 0x7F, 0x1F, 0xFF, + 0xF1, 0xFC, 0x7F, 0xFF, 0xC7, 0xF1, 0xFD, 0xFE, 0x1F, 0x87, 0xE0, 0x00, + 0x1F, 0x80, 0x1F, 0x9F, 0xF8, 0x1F, 0xDF, 0xFE, 0x0F, 0xFF, 0xFF, 0x87, + 0xFF, 0xFF, 0xC1, 0xFF, 0x07, 0xF0, 0x7F, 0x01, 0xF8, 0x3F, 0x00, 0x7C, + 0x1F, 0x00, 0x3E, 0x0F, 0x80, 0x1F, 0x07, 0xC0, 0x0F, 0x83, 0xE0, 0x07, + 0xC1, 0xF0, 0x03, 0xE0, 0xF8, 0x01, 0xF0, 0x7C, 0x00, 0xF8, 0x3E, 0x00, + 0x7C, 0x1F, 0x00, 0x3E, 0x3F, 0xE0, 0x7F, 0xBF, 0xF8, 0x7F, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFE, 0x1F, 0xFB, 0xFE, 0x07, 0xF8, 0x00, 0x7F, 0x00, 0x01, + 0xFF, 0xF0, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, + 0xFE, 0x0F, 0xF1, 0xFC, 0x01, 0xFC, 0xFC, 0x00, 0x7E, 0xFC, 0x00, 0x1F, + 0xFC, 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, + 0xFF, 0xC0, 0x00, 0x7F, 0xF0, 0x00, 0x7E, 0xF8, 0x00, 0x7E, 0x7F, 0x00, + 0x7F, 0x1F, 0xC0, 0xFF, 0x07, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x80, 0x7F, + 0xFF, 0x00, 0x1F, 0xFF, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x07, 0xE0, 0x03, + 0xF9, 0xFF, 0xC0, 0x7F, 0xBF, 0xFE, 0x07, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0xFF, 0xC3, 0xFF, 0x83, 0xFC, 0x0F, 0xE0, 0x0F, 0xE0, 0xFC, 0x00, 0x7E, + 0x0F, 0xC0, 0x03, 0xF0, 0xF8, 0x00, 0x1F, 0x0F, 0x80, 0x01, 0xF0, 0xF8, + 0x00, 0x1F, 0x0F, 0x80, 0x01, 0xF0, 0xF8, 0x00, 0x3F, 0x0F, 0xC0, 0x03, + 0xF0, 0xFE, 0x00, 0x7E, 0x0F, 0xF8, 0x1F, 0xE0, 0xFF, 0xFF, 0xFC, 0x0F, + 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xF0, 0x0F, 0x9F, 0xFC, 0x00, 0xF8, 0x7F, + 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0xFF, 0xFC, + 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0x3F, 0xF9, 0xFC, 0x0F, 0xFF, 0xDF, 0xE1, 0xFF, 0xFF, 0xFE, 0x3F, + 0xFF, 0xFF, 0xE3, 0xF8, 0x1F, 0xFC, 0x7F, 0x00, 0x7F, 0x07, 0xC0, 0x03, + 0xF0, 0xFC, 0x00, 0x3F, 0x0F, 0x80, 0x01, 0xF0, 0xF8, 0x00, 0x1F, 0x0F, + 0x80, 0x01, 0xF0, 0xF8, 0x00, 0x1F, 0x0F, 0xC0, 0x01, 0xF0, 0xFC, 0x00, + 0x3F, 0x07, 0xE0, 0x07, 0xF0, 0x7F, 0x81, 0xFF, 0x03, 0xFF, 0xFF, 0xF0, + 0x1F, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0x9F, 0x00, 0x0F, + 0xE1, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0x00, + 0x03, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x01, + 0xF0, 0x3F, 0xC7, 0xFC, 0x7F, 0xCF, 0xFE, 0x7F, 0xDF, 0xFF, 0x7F, 0xFF, + 0xFF, 0x3F, 0xFF, 0x0E, 0x07, 0xFC, 0x00, 0x07, 0xF8, 0x00, 0x07, 0xF0, + 0x00, 0x07, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xC0, + 0x00, 0x07, 0xC0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xC0, + 0x00, 0x7F, 0xFF, 0xC0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, + 0xE0, 0x7F, 0xFF, 0xC0, 0x03, 0xFC, 0x60, 0x7F, 0xFF, 0x87, 0xFF, 0xFC, + 0x7F, 0xFF, 0xE7, 0xFF, 0xFF, 0x3F, 0x01, 0xF9, 0xF0, 0x07, 0xCF, 0xC0, + 0x1C, 0x7F, 0xF0, 0x03, 0xFF, 0xF8, 0x0F, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, + 0x3F, 0xFF, 0x00, 0x0F, 0xFD, 0xC0, 0x07, 0xFE, 0x00, 0x1F, 0xF8, 0x00, + 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0x3F, + 0xFF, 0xC0, 0x07, 0xF8, 0x00, 0x07, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0xFF, + 0xF8, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, + 0x80, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, + 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x03, 0x83, 0xF0, 0x1F, 0x87, + 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xF8, 0x07, 0xFF, 0xC0, 0x03, + 0xFC, 0x00, 0x7F, 0x01, 0xFE, 0x7F, 0x81, 0xFF, 0x3F, 0xC0, 0xFF, 0x9F, + 0xE0, 0x7F, 0xC7, 0xF0, 0x1F, 0xE0, 0xF8, 0x01, 0xF0, 0x7C, 0x00, 0xF8, + 0x3E, 0x00, 0x7C, 0x1F, 0x00, 0x3E, 0x0F, 0x80, 0x1F, 0x07, 0xC0, 0x0F, + 0x83, 0xE0, 0x07, 0xC1, 0xF0, 0x03, 0xE0, 0xF8, 0x01, 0xF0, 0x7C, 0x01, + 0xF8, 0x3F, 0x01, 0xFC, 0x1F, 0xC1, 0xFF, 0x07, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFF, 0xE0, 0xFF, 0xF7, 0xF0, 0x3F, 0xF3, 0xF0, 0x03, 0xF0, 0x00, 0x7F, + 0xE0, 0x7F, 0xEF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0x7F, 0xE0, 0x7F, 0xE0, 0xF8, 0x01, 0xF0, 0x0F, 0xC0, 0x1F, 0x00, + 0x7C, 0x03, 0xE0, 0x07, 0xE0, 0x3E, 0x00, 0x3E, 0x07, 0xC0, 0x03, 0xF0, + 0x7C, 0x00, 0x1F, 0x0F, 0x80, 0x01, 0xF8, 0xF8, 0x00, 0x0F, 0x9F, 0x00, + 0x00, 0xFD, 0xF0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x03, + 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x1F, 0x80, + 0x00, 0x7F, 0x80, 0x1F, 0xEF, 0xFC, 0x03, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, + 0xFC, 0x03, 0xFF, 0x7F, 0x80, 0x1F, 0xE1, 0xF0, 0xF8, 0x7C, 0x1F, 0x1F, + 0x87, 0xC1, 0xF1, 0xF8, 0xFC, 0x1F, 0x1F, 0xCF, 0x80, 0xFB, 0xFC, 0xF8, + 0x0F, 0xBF, 0xDF, 0x80, 0xFB, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x7F, + 0xDF, 0xF0, 0x07, 0xF9, 0xFF, 0x00, 0x7F, 0x9F, 0xE0, 0x07, 0xF0, 0xFE, + 0x00, 0x3F, 0x0F, 0xE0, 0x03, 0xF0, 0x7E, 0x00, 0x3E, 0x07, 0xC0, 0x03, + 0xE0, 0x3C, 0x00, 0x3F, 0xC0, 0xFF, 0x1F, 0xF8, 0x7F, 0xE7, 0xFE, 0x1F, + 0xF9, 0xFF, 0x87, 0xFE, 0x3F, 0xC0, 0xFF, 0x03, 0xF8, 0x7F, 0x00, 0x7F, + 0x3F, 0x80, 0x0F, 0xFF, 0xC0, 0x01, 0xFF, 0xE0, 0x00, 0x3F, 0xE0, 0x00, + 0x07, 0xF8, 0x00, 0x07, 0xFF, 0x00, 0x03, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, + 0x00, 0xFE, 0x1F, 0xC0, 0x7F, 0x03, 0xF8, 0x7F, 0xC0, 0xFF, 0xBF, 0xF8, + 0x7F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, 0x7F, 0xC0, 0xFF, 0x80, + 0x7F, 0x80, 0x7F, 0xBF, 0xF0, 0x3F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0x03, + 0xFF, 0x7F, 0x80, 0x7F, 0x8F, 0xC0, 0x07, 0x81, 0xF0, 0x03, 0xE0, 0x7E, + 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xF0, 0x3E, 0x00, 0x7C, 0x0F, 0x80, + 0x0F, 0x87, 0xC0, 0x03, 0xE1, 0xF0, 0x00, 0x7C, 0xF8, 0x00, 0x1F, 0xFE, + 0x00, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x07, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x1F, 0xFF, 0x80, + 0x0F, 0xFF, 0xF0, 0x03, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x3F, 0xBE, 0x0F, 0xC3, 0x83, 0xF0, 0x00, 0xFC, 0x00, + 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, + 0xC0, 0x3B, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x78, 0x03, 0xF0, 0x1F, 0xC0, 0xFF, 0x07, + 0xF8, 0x1F, 0x80, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, + 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x0F, 0x81, 0xFE, 0x0F, + 0xF0, 0x3F, 0x80, 0xFF, 0x01, 0xFE, 0x00, 0xFC, 0x01, 0xF0, 0x07, 0xC0, + 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF8, + 0x07, 0xF8, 0x0F, 0xF0, 0x3F, 0xC0, 0x7F, 0x00, 0x78, 0x77, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xE0, 0x78, 0x03, 0xF0, 0x0F, + 0xE0, 0x3F, 0xC0, 0x7F, 0x00, 0x7E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, + 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, + 0x07, 0xC0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x03, 0xFC, 0x1F, 0xE0, 0xFC, + 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, + 0x00, 0xF8, 0x07, 0xE0, 0x7F, 0x83, 0xFC, 0x0F, 0xF0, 0x3F, 0x80, 0x78, + 0x00, 0x07, 0x80, 0x00, 0x7F, 0x80, 0x03, 0xFF, 0x03, 0x9F, 0xFE, 0x1F, + 0xFF, 0xFC, 0xFF, 0xF3, 0xFF, 0xFF, 0x87, 0xFF, 0x9C, 0x0F, 0xFC, 0x00, + 0x0F, 0xE0, 0x00, 0x1F, 0x00}; + +const GFXglyph FreeMonoBold24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 28, 0, 1}, // 0x20 ' ' + {0, 7, 31, 28, 10, -29}, // 0x21 '!' + {28, 15, 14, 28, 6, -28}, // 0x22 '"' + {55, 22, 34, 28, 3, -30}, // 0x23 '#' + {149, 19, 38, 28, 5, -31}, // 0x24 '$' + {240, 21, 30, 28, 4, -28}, // 0x25 '%' + {319, 21, 28, 28, 4, -26}, // 0x26 '&' + {393, 6, 14, 28, 11, -28}, // 0x27 ''' + {404, 10, 37, 28, 12, -29}, // 0x28 '(' + {451, 10, 37, 28, 6, -29}, // 0x29 ')' + {498, 21, 19, 28, 4, -28}, // 0x2A '*' + {548, 23, 26, 28, 3, -25}, // 0x2B '+' + {623, 9, 14, 28, 7, -6}, // 0x2C ',' + {639, 24, 5, 28, 2, -15}, // 0x2D '-' + {654, 7, 6, 28, 11, -4}, // 0x2E '.' + {660, 20, 38, 28, 4, -32}, // 0x2F '/' + {755, 21, 31, 28, 4, -29}, // 0x30 '0' + {837, 20, 29, 28, 4, -28}, // 0x31 '1' + {910, 21, 30, 28, 3, -29}, // 0x32 '2' + {989, 21, 31, 28, 4, -29}, // 0x33 '3' + {1071, 20, 28, 28, 4, -27}, // 0x34 '4' + {1141, 21, 31, 28, 4, -29}, // 0x35 '5' + {1223, 20, 31, 28, 5, -29}, // 0x36 '6' + {1301, 20, 30, 28, 4, -29}, // 0x37 '7' + {1376, 20, 31, 28, 4, -29}, // 0x38 '8' + {1454, 20, 31, 28, 5, -29}, // 0x39 '9' + {1532, 7, 22, 28, 11, -20}, // 0x3A ':' + {1552, 10, 28, 28, 6, -20}, // 0x3B ';' + {1587, 24, 21, 28, 2, -23}, // 0x3C '<' + {1650, 24, 14, 28, 2, -19}, // 0x3D '=' + {1692, 23, 22, 28, 3, -23}, // 0x3E '>' + {1756, 20, 29, 28, 5, -27}, // 0x3F '?' + {1829, 19, 36, 28, 4, -28}, // 0x40 '@' + {1915, 29, 27, 28, -1, -26}, // 0x41 'A' + {2013, 26, 27, 28, 1, -26}, // 0x42 'B' + {2101, 25, 29, 28, 2, -27}, // 0x43 'C' + {2192, 25, 27, 28, 1, -26}, // 0x44 'D' + {2277, 25, 27, 28, 1, -26}, // 0x45 'E' + {2362, 25, 27, 28, 1, -26}, // 0x46 'F' + {2447, 25, 29, 28, 2, -27}, // 0x47 'G' + {2538, 26, 27, 28, 1, -26}, // 0x48 'H' + {2626, 19, 27, 28, 5, -26}, // 0x49 'I' + {2691, 25, 28, 28, 3, -26}, // 0x4A 'J' + {2779, 27, 27, 28, 1, -26}, // 0x4B 'K' + {2871, 25, 27, 28, 2, -26}, // 0x4C 'L' + {2956, 31, 27, 28, -1, -26}, // 0x4D 'M' + {3061, 28, 27, 28, 0, -26}, // 0x4E 'N' + {3156, 27, 29, 28, 1, -27}, // 0x4F 'O' + {3254, 24, 27, 28, 1, -26}, // 0x50 'P' + {3335, 27, 35, 28, 1, -27}, // 0x51 'Q' + {3454, 28, 27, 28, 0, -26}, // 0x52 'R' + {3549, 22, 29, 28, 3, -27}, // 0x53 'S' + {3629, 25, 27, 28, 2, -26}, // 0x54 'T' + {3714, 28, 28, 28, 0, -26}, // 0x55 'U' + {3812, 30, 27, 28, -1, -26}, // 0x56 'V' + {3914, 28, 27, 28, 0, -26}, // 0x57 'W' + {4009, 26, 27, 28, 1, -26}, // 0x58 'X' + {4097, 26, 27, 28, 1, -26}, // 0x59 'Y' + {4185, 21, 27, 28, 4, -26}, // 0x5A 'Z' + {4256, 10, 37, 28, 12, -29}, // 0x5B '[' + {4303, 20, 38, 28, 4, -32}, // 0x5C '\' + {4398, 10, 37, 28, 6, -29}, // 0x5D ']' + {4445, 20, 15, 28, 4, -29}, // 0x5E '^' + {4483, 28, 5, 28, 0, 5}, // 0x5F '_' + {4501, 9, 8, 28, 8, -30}, // 0x60 '`' + {4510, 24, 23, 28, 2, -21}, // 0x61 'a' + {4579, 27, 31, 28, 0, -29}, // 0x62 'b' + {4684, 24, 23, 28, 3, -21}, // 0x63 'c' + {4753, 26, 31, 28, 2, -29}, // 0x64 'd' + {4854, 24, 23, 28, 2, -21}, // 0x65 'e' + {4923, 22, 30, 28, 4, -29}, // 0x66 'f' + {5006, 25, 31, 28, 2, -21}, // 0x67 'g' + {5103, 26, 30, 28, 1, -29}, // 0x68 'h' + {5201, 21, 29, 28, 4, -28}, // 0x69 'i' + {5278, 17, 38, 28, 5, -28}, // 0x6A 'j' + {5359, 25, 30, 28, 2, -29}, // 0x6B 'k' + {5453, 21, 30, 28, 4, -29}, // 0x6C 'l' + {5532, 30, 22, 28, -1, -21}, // 0x6D 'm' + {5615, 25, 22, 28, 1, -21}, // 0x6E 'n' + {5684, 25, 23, 28, 2, -21}, // 0x6F 'o' + {5756, 28, 31, 28, 0, -21}, // 0x70 'p' + {5865, 28, 31, 28, 1, -21}, // 0x71 'q' + {5974, 24, 22, 28, 3, -21}, // 0x72 'r' + {6040, 21, 23, 28, 4, -21}, // 0x73 's' + {6101, 23, 28, 28, 1, -26}, // 0x74 't' + {6182, 25, 22, 28, 1, -20}, // 0x75 'u' + {6251, 28, 21, 28, 0, -20}, // 0x76 'v' + {6325, 28, 21, 28, 0, -20}, // 0x77 'w' + {6399, 26, 21, 28, 1, -20}, // 0x78 'x' + {6468, 26, 30, 28, 1, -20}, // 0x79 'y' + {6566, 19, 21, 28, 5, -20}, // 0x7A 'z' + {6616, 14, 37, 28, 7, -29}, // 0x7B '{' + {6681, 5, 36, 28, 12, -28}, // 0x7C '|' + {6704, 14, 37, 28, 8, -29}, // 0x7D '}' + {6769, 22, 10, 28, 3, -17}}; // 0x7E '~' + +const GFXfont FreeMonoBold24pt7b PROGMEM = { + (uint8_t *)FreeMonoBold24pt7bBitmaps, (GFXglyph *)FreeMonoBold24pt7bGlyphs, + 0x20, 0x7E, 47}; + +// Approx. 7469 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold9pt7b.h new file mode 100644 index 0000000..3fb82aa --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBold9pt7b.h @@ -0,0 +1,188 @@ +const uint8_t FreeMonoBold9pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xD2, 0x1F, 0x80, 0xEC, 0x89, 0x12, 0x24, 0x40, 0x36, 0x36, + 0x36, 0x7F, 0x7F, 0x36, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x00, 0x18, 0xFF, + 0xFE, 0x3C, 0x1F, 0x1F, 0x83, 0x46, 0x8D, 0xF0, 0xC1, 0x83, 0x00, 0x61, + 0x22, 0x44, 0x86, 0x67, 0x37, 0x11, 0x22, 0x4C, 0x70, 0x3C, 0x7E, 0x60, + 0x60, 0x30, 0x7B, 0xDF, 0xCE, 0xFF, 0x7F, 0xC9, 0x24, 0x37, 0x66, 0xCC, + 0xCC, 0xCC, 0x66, 0x31, 0xCE, 0x66, 0x33, 0x33, 0x33, 0x66, 0xC8, 0x18, + 0x18, 0xFF, 0xFF, 0x3C, 0x3C, 0x66, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, + 0x18, 0x18, 0x18, 0x6B, 0x48, 0xFF, 0xFF, 0xC0, 0xF0, 0x02, 0x0C, 0x18, + 0x60, 0xC3, 0x06, 0x0C, 0x30, 0x61, 0x83, 0x0C, 0x18, 0x20, 0x00, 0x38, + 0xFB, 0xBE, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0xDD, 0xF1, 0xC0, 0x38, 0xF3, + 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0xFD, 0xF8, 0x3C, 0xFE, 0xC7, 0x03, + 0x03, 0x06, 0x0C, 0x18, 0x70, 0xE3, 0xFF, 0xFF, 0x7C, 0xFE, 0x03, 0x03, + 0x03, 0x1E, 0x1E, 0x07, 0x03, 0x03, 0xFE, 0x7C, 0x1C, 0x38, 0xB1, 0x64, + 0xD9, 0xBF, 0xFF, 0x3E, 0x7C, 0x7E, 0x3F, 0x18, 0x0F, 0xC7, 0xF3, 0x1C, + 0x06, 0x03, 0xC3, 0xFF, 0x9F, 0x80, 0x0F, 0x3F, 0x30, 0x60, 0x60, 0xDC, + 0xFE, 0xE3, 0xC3, 0x63, 0x7E, 0x3C, 0xFF, 0xFF, 0xC3, 0x03, 0x06, 0x06, + 0x06, 0x0C, 0x0C, 0x0C, 0x18, 0x38, 0xFB, 0x1E, 0x3C, 0x6F, 0x9F, 0x63, + 0xC7, 0x8F, 0xF1, 0xC0, 0x3C, 0x7E, 0xE6, 0xC3, 0xC3, 0xE7, 0x7F, 0x3B, + 0x06, 0x0E, 0xFC, 0xF0, 0xF0, 0x0F, 0x6C, 0x00, 0x1A, 0xD2, 0x00, 0x01, + 0x83, 0x87, 0x0E, 0x0F, 0x80, 0xE0, 0x1C, 0x03, 0xFF, 0xFF, 0xC0, 0x00, + 0x0F, 0xFF, 0xFC, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0xF9, 0xE3, 0xC1, 0x80, + 0x7C, 0xFE, 0xC7, 0x03, 0x0E, 0x1C, 0x00, 0x00, 0x00, 0x30, 0x30, 0x1E, + 0x1F, 0x1C, 0xDC, 0x6C, 0x76, 0x7B, 0x6D, 0xB6, 0xDB, 0x6F, 0xF3, 0xFC, + 0x06, 0x33, 0xF8, 0x78, 0x3C, 0x07, 0xC0, 0x38, 0x05, 0x81, 0xB0, 0x36, + 0x0F, 0xE1, 0xFC, 0x71, 0xDF, 0x7F, 0xEF, 0x80, 0xFF, 0x3F, 0xE6, 0x19, + 0x86, 0x7F, 0x1F, 0xE6, 0x1D, 0x83, 0x60, 0xFF, 0xFF, 0xF0, 0x1F, 0xBF, + 0xD8, 0xF8, 0x3C, 0x06, 0x03, 0x01, 0x80, 0x61, 0xBF, 0xC7, 0xC0, 0xFE, + 0x3F, 0xE6, 0x19, 0x83, 0x60, 0xD8, 0x36, 0x0D, 0x83, 0x61, 0xBF, 0xEF, + 0xE0, 0xFF, 0xFF, 0xD8, 0x6D, 0xB7, 0xC3, 0xE1, 0xB0, 0xC3, 0x61, 0xFF, + 0xFF, 0xE0, 0xFF, 0xFF, 0xD8, 0x6D, 0xB7, 0xC3, 0xE1, 0xB0, 0xC0, 0x60, + 0x7C, 0x3E, 0x00, 0x1F, 0x9F, 0xE6, 0x1B, 0x06, 0xC0, 0x30, 0x0C, 0x7F, + 0x1F, 0xE1, 0x9F, 0xE3, 0xF0, 0xF7, 0xFB, 0xD8, 0xCC, 0x66, 0x33, 0xF9, + 0xFC, 0xC6, 0x63, 0x7B, 0xFD, 0xE0, 0xFF, 0xF3, 0x0C, 0x30, 0xC3, 0x0C, + 0x33, 0xFF, 0xC0, 0x1F, 0xC7, 0xF0, 0x30, 0x0C, 0x03, 0x00, 0xCC, 0x33, + 0x0C, 0xC7, 0x3F, 0x87, 0xC0, 0xF7, 0xBD, 0xE6, 0x61, 0xB0, 0x78, 0x1F, + 0x06, 0xE1, 0x98, 0x63, 0x3C, 0xFF, 0x3C, 0xFC, 0x7E, 0x0C, 0x06, 0x03, + 0x01, 0x80, 0xC6, 0x63, 0x31, 0xFF, 0xFF, 0xE0, 0xE0, 0xFE, 0x3D, 0xC7, + 0x3D, 0xE7, 0xBC, 0xD7, 0x9B, 0xB3, 0x76, 0x60, 0xDE, 0x3F, 0xC7, 0x80, + 0xE1, 0xFE, 0x3D, 0xE3, 0x3C, 0x66, 0xCC, 0xDD, 0x99, 0xB3, 0x1E, 0x63, + 0xDE, 0x3B, 0xC3, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, + 0xF0, 0x1F, 0x07, 0x71, 0xC7, 0xF0, 0x7C, 0x00, 0xFE, 0x7F, 0x98, 0x6C, + 0x36, 0x1B, 0xF9, 0xF8, 0xC0, 0x60, 0x7C, 0x3E, 0x00, 0x1F, 0x07, 0xF1, + 0xC7, 0x70, 0x7C, 0x07, 0x80, 0xF0, 0x1F, 0x07, 0x71, 0xC7, 0xF0, 0x7C, + 0x0C, 0x33, 0xFE, 0x7F, 0x80, 0xFC, 0x7F, 0x18, 0xCC, 0x66, 0x73, 0xF1, + 0xF0, 0xCC, 0x63, 0x7D, 0xFE, 0x60, 0x3F, 0xBF, 0xF0, 0x78, 0x0F, 0x03, + 0xF8, 0x3F, 0x83, 0xC3, 0xFF, 0xBF, 0x80, 0xFF, 0xFF, 0xF6, 0x7B, 0x3D, + 0x98, 0xC0, 0x60, 0x30, 0x18, 0x3F, 0x1F, 0x80, 0xF1, 0xFE, 0x3D, 0x83, + 0x30, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x60, 0xC7, 0xF0, 0x7C, 0x00, + 0xFB, 0xFF, 0x7D, 0xC3, 0x18, 0xC3, 0x18, 0x36, 0x06, 0xC0, 0x50, 0x0E, + 0x01, 0xC0, 0x10, 0x00, 0xFB, 0xFE, 0xF6, 0x0D, 0x93, 0x6E, 0xDB, 0xB7, + 0xAD, 0xEE, 0x7B, 0x8E, 0xE3, 0x18, 0xF3, 0xFC, 0xF7, 0x38, 0xFC, 0x1E, + 0x03, 0x01, 0xE0, 0xCC, 0x73, 0xBC, 0xFF, 0x3C, 0xF3, 0xFC, 0xF7, 0x38, + 0xCC, 0x1E, 0x07, 0x80, 0xC0, 0x30, 0x0C, 0x0F, 0xC3, 0xF0, 0xFE, 0xFE, + 0xC6, 0xCC, 0x18, 0x18, 0x30, 0x63, 0xC3, 0xFF, 0xFF, 0xFF, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xFF, 0x01, 0x03, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, + 0x30, 0x30, 0x60, 0x60, 0xC0, 0x80, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, + 0xFF, 0x10, 0x71, 0xE3, 0x6C, 0x70, 0x40, 0xFF, 0xFF, 0xFC, 0x88, 0x80, + 0x7E, 0x3F, 0x8F, 0xCF, 0xEE, 0x36, 0x1B, 0xFE, 0xFF, 0xE0, 0x38, 0x06, + 0x01, 0xBC, 0x7F, 0x9C, 0x76, 0x0D, 0x83, 0x71, 0xFF, 0xEE, 0xF0, 0x3F, + 0xBF, 0xF8, 0x78, 0x3C, 0x07, 0x05, 0xFE, 0x7E, 0x03, 0x80, 0xE0, 0x18, + 0xF6, 0x7F, 0xB8, 0xEC, 0x1B, 0x06, 0xE3, 0x9F, 0xF3, 0xFC, 0x3E, 0x3F, + 0xB0, 0xFF, 0xFF, 0xFE, 0x01, 0xFE, 0x7E, 0x1F, 0x3F, 0x30, 0x7E, 0x7E, + 0x30, 0x30, 0x30, 0x30, 0xFE, 0xFE, 0x3F, 0xBF, 0xF9, 0xD8, 0x6C, 0x37, + 0x39, 0xFC, 0x76, 0x03, 0x01, 0x8F, 0xC7, 0xC0, 0xE0, 0x70, 0x18, 0x0D, + 0xC7, 0xF3, 0x99, 0x8C, 0xC6, 0x63, 0x7B, 0xFD, 0xE0, 0x18, 0x18, 0x00, + 0x78, 0x78, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x60, 0x3F, 0xFC, + 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0F, 0xFF, 0x80, 0xE0, 0x70, 0x18, 0x0D, + 0xE6, 0xF3, 0xE1, 0xE0, 0xF8, 0x6E, 0x73, 0xF9, 0xE0, 0x78, 0x78, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0xFD, 0x9F, 0xF9, 0x9B, + 0x33, 0x66, 0x6C, 0xCD, 0xBD, 0xFF, 0xBF, 0xEE, 0x7F, 0x98, 0xCC, 0x66, + 0x33, 0x1B, 0xDF, 0xEF, 0x3E, 0x3F, 0xB8, 0xF8, 0x3C, 0x1F, 0x1D, 0xFC, + 0x7C, 0xEF, 0x1F, 0xF9, 0xC3, 0xB0, 0x36, 0x06, 0xE1, 0xDF, 0xF3, 0x78, + 0x60, 0x0C, 0x03, 0xE0, 0x7C, 0x00, 0x1E, 0xEF, 0xFF, 0x87, 0x60, 0x6C, + 0x0D, 0xC3, 0x9F, 0xF0, 0xF6, 0x00, 0xC0, 0x18, 0x0F, 0x81, 0xF0, 0x77, + 0xBF, 0xCF, 0x06, 0x03, 0x01, 0x83, 0xF9, 0xFC, 0x3F, 0xFF, 0xC3, 0xFC, + 0x3F, 0xC3, 0xFF, 0xFC, 0x60, 0x60, 0x60, 0xFE, 0xFE, 0x60, 0x60, 0x60, + 0x61, 0x7F, 0x3E, 0xE7, 0x73, 0x98, 0xCC, 0x66, 0x33, 0x19, 0xFE, 0x7F, + 0xFB, 0xFF, 0x7C, 0xC6, 0x18, 0xC1, 0xB0, 0x36, 0x03, 0x80, 0x70, 0xF1, + 0xFE, 0x3D, 0xBB, 0x37, 0x63, 0xF8, 0x77, 0x0E, 0xE1, 0x8C, 0xF7, 0xFB, + 0xCD, 0x83, 0x83, 0xC3, 0xBB, 0xDF, 0xEF, 0xF3, 0xFC, 0xF6, 0x18, 0xCC, + 0x33, 0x07, 0x81, 0xE0, 0x30, 0x0C, 0x06, 0x0F, 0xC3, 0xF0, 0xFF, 0xFF, + 0x30, 0xC3, 0x0C, 0x7F, 0xFF, 0x37, 0x66, 0x66, 0xCC, 0x66, 0x66, 0x73, + 0xFF, 0xFF, 0xFF, 0xF0, 0xCE, 0x66, 0x66, 0x33, 0x66, 0x66, 0xEC, 0x70, + 0x7C, 0xF3, 0xC0, 0xC0}; + +const GFXglyph FreeMonoBold9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 11, 0, 1}, // 0x20 ' ' + {0, 3, 11, 11, 4, -10}, // 0x21 '!' + {5, 7, 5, 11, 2, -10}, // 0x22 '"' + {10, 8, 12, 11, 1, -10}, // 0x23 '#' + {22, 7, 14, 11, 2, -11}, // 0x24 '$' + {35, 7, 11, 11, 2, -10}, // 0x25 '%' + {45, 8, 10, 11, 1, -9}, // 0x26 '&' + {55, 3, 5, 11, 4, -10}, // 0x27 ''' + {57, 4, 14, 11, 5, -10}, // 0x28 '(' + {64, 4, 14, 11, 2, -10}, // 0x29 ')' + {71, 8, 7, 11, 2, -10}, // 0x2A '*' + {78, 8, 9, 11, 2, -8}, // 0x2B '+' + {87, 3, 5, 11, 3, -1}, // 0x2C ',' + {89, 9, 2, 11, 1, -5}, // 0x2D '-' + {92, 2, 2, 11, 4, -1}, // 0x2E '.' + {93, 7, 15, 11, 2, -12}, // 0x2F '/' + {107, 7, 12, 11, 2, -11}, // 0x30 '0' + {118, 7, 11, 11, 2, -10}, // 0x31 '1' + {128, 8, 12, 11, 1, -11}, // 0x32 '2' + {140, 8, 12, 11, 2, -11}, // 0x33 '3' + {152, 7, 10, 11, 2, -9}, // 0x34 '4' + {161, 9, 11, 11, 1, -10}, // 0x35 '5' + {174, 8, 12, 11, 2, -11}, // 0x36 '6' + {186, 8, 11, 11, 1, -10}, // 0x37 '7' + {197, 7, 12, 11, 2, -11}, // 0x38 '8' + {208, 8, 12, 11, 2, -11}, // 0x39 '9' + {220, 2, 8, 11, 4, -7}, // 0x3A ':' + {222, 3, 11, 11, 3, -7}, // 0x3B ';' + {227, 9, 8, 11, 1, -8}, // 0x3C '<' + {236, 9, 6, 11, 1, -7}, // 0x3D '=' + {243, 9, 8, 11, 1, -8}, // 0x3E '>' + {252, 8, 11, 11, 2, -10}, // 0x3F '?' + {263, 9, 15, 11, 1, -11}, // 0x40 '@' + {280, 11, 11, 11, 0, -10}, // 0x41 'A' + {296, 10, 11, 11, 1, -10}, // 0x42 'B' + {310, 9, 11, 11, 1, -10}, // 0x43 'C' + {323, 10, 11, 11, 0, -10}, // 0x44 'D' + {337, 9, 11, 11, 1, -10}, // 0x45 'E' + {350, 9, 11, 11, 1, -10}, // 0x46 'F' + {363, 10, 11, 11, 1, -10}, // 0x47 'G' + {377, 9, 11, 11, 1, -10}, // 0x48 'H' + {390, 6, 11, 11, 3, -10}, // 0x49 'I' + {399, 10, 11, 11, 1, -10}, // 0x4A 'J' + {413, 10, 11, 11, 1, -10}, // 0x4B 'K' + {427, 9, 11, 11, 1, -10}, // 0x4C 'L' + {440, 11, 11, 11, 0, -10}, // 0x4D 'M' + {456, 11, 11, 11, 0, -10}, // 0x4E 'N' + {472, 11, 11, 11, 0, -10}, // 0x4F 'O' + {488, 9, 11, 11, 1, -10}, // 0x50 'P' + {501, 11, 14, 11, 0, -10}, // 0x51 'Q' + {521, 9, 11, 11, 1, -10}, // 0x52 'R' + {534, 9, 11, 11, 1, -10}, // 0x53 'S' + {547, 9, 11, 11, 1, -10}, // 0x54 'T' + {560, 11, 11, 11, 0, -10}, // 0x55 'U' + {576, 11, 11, 11, 0, -10}, // 0x56 'V' + {592, 10, 11, 11, 0, -10}, // 0x57 'W' + {606, 10, 11, 11, 0, -10}, // 0x58 'X' + {620, 10, 11, 11, 0, -10}, // 0x59 'Y' + {634, 8, 11, 11, 2, -10}, // 0x5A 'Z' + {645, 4, 14, 11, 5, -10}, // 0x5B '[' + {652, 7, 15, 11, 2, -12}, // 0x5C '\' + {666, 4, 14, 11, 2, -10}, // 0x5D ']' + {673, 7, 6, 11, 2, -11}, // 0x5E '^' + {679, 11, 2, 11, 0, 3}, // 0x5F '_' + {682, 3, 3, 11, 3, -11}, // 0x60 '`' + {684, 9, 8, 11, 1, -7}, // 0x61 'a' + {693, 10, 11, 11, 0, -10}, // 0x62 'b' + {707, 9, 8, 11, 1, -7}, // 0x63 'c' + {716, 10, 11, 11, 1, -10}, // 0x64 'd' + {730, 9, 8, 11, 1, -7}, // 0x65 'e' + {739, 8, 11, 11, 2, -10}, // 0x66 'f' + {750, 9, 12, 11, 1, -7}, // 0x67 'g' + {764, 9, 11, 11, 1, -10}, // 0x68 'h' + {777, 8, 11, 11, 2, -10}, // 0x69 'i' + {788, 6, 15, 11, 2, -10}, // 0x6A 'j' + {800, 9, 11, 11, 1, -10}, // 0x6B 'k' + {813, 8, 11, 11, 2, -10}, // 0x6C 'l' + {824, 11, 8, 11, 0, -7}, // 0x6D 'm' + {835, 9, 8, 11, 1, -7}, // 0x6E 'n' + {844, 9, 8, 11, 1, -7}, // 0x6F 'o' + {853, 11, 12, 11, 0, -7}, // 0x70 'p' + {870, 11, 12, 11, 0, -7}, // 0x71 'q' + {887, 9, 8, 11, 1, -7}, // 0x72 'r' + {896, 8, 8, 11, 2, -7}, // 0x73 's' + {904, 8, 11, 11, 1, -10}, // 0x74 't' + {915, 9, 8, 11, 1, -7}, // 0x75 'u' + {924, 11, 8, 11, 0, -7}, // 0x76 'v' + {935, 11, 8, 11, 0, -7}, // 0x77 'w' + {946, 9, 8, 11, 1, -7}, // 0x78 'x' + {955, 10, 12, 11, 0, -7}, // 0x79 'y' + {970, 7, 8, 11, 2, -7}, // 0x7A 'z' + {977, 4, 14, 11, 3, -10}, // 0x7B '{' + {984, 2, 14, 11, 5, -10}, // 0x7C '|' + {988, 4, 14, 11, 4, -10}, // 0x7D '}' + {995, 9, 4, 11, 1, -6}}; // 0x7E '~' + +const GFXfont FreeMonoBold9pt7b PROGMEM = {(uint8_t *)FreeMonoBold9pt7bBitmaps, + (GFXglyph *)FreeMonoBold9pt7bGlyphs, + 0x20, 0x7E, 18}; + +// Approx. 1672 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique12pt7b.h new file mode 100644 index 0000000..6f18c47 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique12pt7b.h @@ -0,0 +1,268 @@ +const uint8_t FreeMonoBoldOblique12pt7bBitmaps[] PROGMEM = { + 0x1C, 0xF3, 0xCE, 0x38, 0xE7, 0x1C, 0x61, 0x86, 0x00, 0x63, 0x8C, 0x00, + 0xE7, 0xE7, 0xE6, 0xC6, 0xC6, 0xC4, 0x84, 0x03, 0x30, 0x19, 0x81, 0xDC, + 0x0C, 0xE0, 0x66, 0x1F, 0xFC, 0xFF, 0xE1, 0x98, 0x0C, 0xC0, 0xEE, 0x06, + 0x70, 0xFF, 0xCF, 0xFE, 0x1D, 0xC0, 0xCC, 0x06, 0x60, 0x77, 0x03, 0x30, + 0x00, 0x01, 0x00, 0x70, 0x0C, 0x07, 0xF1, 0xFE, 0x71, 0xCC, 0x11, 0x80, + 0x3F, 0x03, 0xF0, 0x0F, 0x20, 0x6E, 0x0D, 0xC3, 0x3F, 0xE7, 0xF8, 0x1C, + 0x03, 0x00, 0x60, 0x0C, 0x00, 0x0E, 0x03, 0xE0, 0xC4, 0x10, 0x82, 0x30, + 0x7C, 0x07, 0x78, 0x7C, 0x7F, 0x19, 0xF0, 0x62, 0x08, 0x41, 0x18, 0x3E, + 0x03, 0x80, 0x07, 0xC1, 0xF8, 0x62, 0x0C, 0x01, 0x80, 0x38, 0x0F, 0x03, + 0xF7, 0x6F, 0xD8, 0xF3, 0x1E, 0x7F, 0xE7, 0xF8, 0xFF, 0x6D, 0x20, 0x06, + 0x1C, 0x70, 0xC3, 0x06, 0x18, 0x30, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, + 0x70, 0x60, 0xC1, 0x00, 0x0C, 0x18, 0x38, 0x30, 0x60, 0xC1, 0x83, 0x06, + 0x0C, 0x30, 0x61, 0xC3, 0x0E, 0x38, 0x61, 0xC2, 0x00, 0x06, 0x00, 0xC0, + 0x18, 0x3F, 0x7F, 0xFE, 0xFF, 0x07, 0x81, 0xF8, 0x77, 0x0C, 0x60, 0x03, + 0x00, 0x70, 0x07, 0x00, 0x60, 0x06, 0x0F, 0xFF, 0xFF, 0xF0, 0xE0, 0x0C, + 0x00, 0xC0, 0x0C, 0x01, 0xC0, 0x18, 0x00, 0x1C, 0xE3, 0x1C, 0x63, 0x08, + 0x00, 0x7F, 0xFF, 0xFF, 0xC0, 0x7F, 0x00, 0x00, 0x08, 0x00, 0x70, 0x01, + 0x80, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x0C, 0x00, 0x70, 0x03, 0x80, 0x0C, + 0x00, 0x70, 0x03, 0x80, 0x0C, 0x00, 0x70, 0x03, 0x80, 0x0C, 0x00, 0x70, + 0x03, 0x80, 0x0C, 0x00, 0x20, 0x00, 0x07, 0x83, 0xF8, 0xE3, 0x98, 0x37, + 0x06, 0xC0, 0xD8, 0x1B, 0x03, 0xE0, 0xF8, 0x1B, 0x03, 0x60, 0xEE, 0x38, + 0xFE, 0x0F, 0x00, 0x03, 0xC1, 0xF0, 0x7E, 0x0C, 0xC0, 0x38, 0x07, 0x00, + 0xC0, 0x18, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0x61, 0xFF, 0xFF, 0xF0, + 0x03, 0xE0, 0x3F, 0x83, 0x8E, 0x38, 0x31, 0x81, 0x80, 0x18, 0x01, 0xC0, + 0x1C, 0x01, 0xC0, 0x38, 0x03, 0x80, 0x38, 0x47, 0x87, 0x3F, 0xF3, 0xFF, + 0x80, 0x07, 0xC1, 0xFF, 0x18, 0x70, 0x03, 0x00, 0x30, 0x06, 0x07, 0xC0, + 0x7C, 0x00, 0xE0, 0x06, 0x00, 0x60, 0x06, 0xC1, 0xCF, 0xF8, 0x7E, 0x00, + 0x01, 0xE0, 0x3C, 0x0F, 0x03, 0x60, 0xCC, 0x3B, 0x8E, 0x63, 0x8C, 0x61, + 0x9F, 0xFB, 0xFF, 0x01, 0x81, 0xF8, 0x3F, 0x00, 0x0F, 0xF1, 0xFE, 0x18, + 0x01, 0x80, 0x18, 0x03, 0xF8, 0x3F, 0xC3, 0x8E, 0x00, 0x60, 0x06, 0x00, + 0x60, 0x0C, 0xC1, 0xCF, 0xF8, 0x7E, 0x00, 0x03, 0xE1, 0xFC, 0x70, 0x1C, + 0x03, 0x00, 0xC0, 0x1B, 0xC7, 0xFC, 0xF3, 0x98, 0x33, 0x06, 0x60, 0xCE, + 0x30, 0xFC, 0x0F, 0x00, 0xFF, 0xFF, 0xFB, 0x07, 0x60, 0xC0, 0x38, 0x06, + 0x01, 0xC0, 0x30, 0x0E, 0x01, 0x80, 0x70, 0x1C, 0x03, 0x80, 0x60, 0x08, + 0x00, 0x07, 0x83, 0xF8, 0xE3, 0xB0, 0x36, 0x06, 0xC0, 0xDC, 0x31, 0xFC, + 0x3F, 0x8C, 0x3B, 0x03, 0x60, 0x6C, 0x39, 0xFE, 0x1F, 0x00, 0x07, 0x81, + 0xF8, 0x63, 0x98, 0x33, 0x06, 0x60, 0xCE, 0x79, 0xFF, 0x1E, 0xC0, 0x18, + 0x06, 0x01, 0xC0, 0x71, 0xFC, 0x3E, 0x00, 0x19, 0xCC, 0x00, 0x00, 0x00, + 0x67, 0x30, 0x06, 0x1C, 0x30, 0x00, 0x00, 0x00, 0x00, 0x38, 0x71, 0xC3, + 0x0E, 0x18, 0x20, 0x00, 0x00, 0x18, 0x03, 0xC0, 0x7C, 0x1F, 0x03, 0xE0, + 0x3E, 0x00, 0x7C, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0x80, 0x08, 0x7F, 0xFB, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFB, 0xFF, 0xC0, 0x30, 0x01, + 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0x7C, 0x1F, 0x03, 0xE0, 0x7C, + 0x07, 0x80, 0x20, 0x00, 0x3E, 0x7F, 0xB0, 0xF8, 0x30, 0x18, 0x1C, 0x1C, + 0x3C, 0x38, 0x18, 0x00, 0x06, 0x07, 0x03, 0x00, 0x03, 0xC0, 0x7E, 0x0C, + 0x71, 0x83, 0x30, 0x33, 0x0F, 0x33, 0xE6, 0x76, 0x6C, 0x66, 0xC6, 0x6C, + 0x6C, 0xFC, 0xC7, 0xEC, 0x00, 0xC0, 0x0C, 0x00, 0xE3, 0x07, 0xF0, 0x3C, + 0x00, 0x07, 0xF0, 0x1F, 0xE0, 0x07, 0xC0, 0x1F, 0x80, 0x3B, 0x00, 0xE7, + 0x01, 0x8E, 0x07, 0x1C, 0x1F, 0xF8, 0x3F, 0xF0, 0xE0, 0x71, 0x80, 0xEF, + 0xC7, 0xFF, 0x8F, 0xC0, 0x3F, 0xF1, 0xFF, 0xC3, 0x06, 0x38, 0x31, 0xC1, + 0x8C, 0x18, 0x7F, 0xC3, 0xFE, 0x38, 0x39, 0xC0, 0xCC, 0x06, 0x60, 0x6F, + 0xFF, 0x7F, 0xE0, 0x03, 0xEC, 0x3F, 0xF1, 0xC3, 0x8C, 0x06, 0x60, 0x19, + 0x80, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x03, 0x3C, 0x1C, + 0x7F, 0xE0, 0x7E, 0x00, 0x3F, 0xE1, 0xFF, 0x87, 0x0C, 0x30, 0x31, 0x81, + 0x8C, 0x0C, 0xE0, 0x67, 0x03, 0x30, 0x31, 0x81, 0x8C, 0x0C, 0xE1, 0xCF, + 0xFC, 0x7F, 0x80, 0x1F, 0xFE, 0x3F, 0xFC, 0x38, 0x38, 0x70, 0x70, 0xCC, + 0xC1, 0x98, 0x03, 0xF0, 0x0F, 0xE0, 0x1D, 0x80, 0x31, 0x18, 0x60, 0x70, + 0xC0, 0xE7, 0xFF, 0x9F, 0xFF, 0x00, 0x1F, 0xFF, 0x1F, 0xFE, 0x0E, 0x06, + 0x0C, 0x0E, 0x0C, 0xC4, 0x0C, 0xC0, 0x1F, 0xC0, 0x1F, 0xC0, 0x19, 0xC0, + 0x19, 0x80, 0x18, 0x00, 0x38, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x07, 0xEC, + 0x7F, 0xF3, 0x83, 0x9C, 0x06, 0x60, 0x19, 0x80, 0x0C, 0x00, 0x30, 0xFE, + 0xC3, 0xFB, 0x01, 0xCC, 0x07, 0x3C, 0x38, 0x7F, 0xE0, 0x7E, 0x00, 0x0F, + 0xBF, 0x1F, 0xBE, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x0C, 0x1C, 0x1F, + 0xF8, 0x1F, 0xF8, 0x18, 0x18, 0x18, 0x38, 0x18, 0x38, 0x38, 0x30, 0x7C, + 0xFC, 0xFC, 0xF8, 0x3F, 0xF3, 0xFF, 0x03, 0x00, 0x70, 0x07, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xC0, 0xFF, 0xCF, 0xFC, + 0x03, 0xFF, 0x03, 0xFF, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x70, 0x20, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0xE0, 0xE1, 0xC0, + 0xFF, 0x80, 0x3F, 0x00, 0x1F, 0x9F, 0x1F, 0x9E, 0x0E, 0x38, 0x0C, 0x70, + 0x0C, 0xE0, 0x0F, 0xC0, 0x1F, 0xC0, 0x1F, 0xE0, 0x1C, 0xE0, 0x18, 0x60, + 0x18, 0x70, 0x38, 0x70, 0xFE, 0x3C, 0xFC, 0x3C, 0x3F, 0xC1, 0xFE, 0x01, + 0x80, 0x1C, 0x00, 0xE0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x1C, 0x18, 0xE0, + 0xC6, 0x06, 0x30, 0x7F, 0xFF, 0xFF, 0xF8, 0x1E, 0x07, 0x87, 0x81, 0xE0, + 0xF0, 0xF0, 0x7C, 0x7C, 0x1F, 0x1F, 0x06, 0xCF, 0x81, 0xBF, 0x60, 0xEF, + 0x98, 0x3B, 0xEE, 0x0C, 0x73, 0x83, 0x1C, 0xC0, 0xC0, 0x30, 0xFC, 0x7E, + 0x3F, 0x1F, 0x80, 0x3C, 0x3F, 0x3E, 0x3F, 0x1E, 0x0C, 0x1F, 0x1C, 0x1F, + 0x1C, 0x1B, 0x98, 0x3B, 0x98, 0x3B, 0x98, 0x31, 0xF8, 0x31, 0xF8, 0x30, + 0xF0, 0x70, 0xF0, 0xFC, 0x70, 0xF8, 0x70, 0x03, 0xE0, 0x3F, 0xE1, 0xC3, + 0x8C, 0x07, 0x60, 0x0D, 0x80, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1B, 0x00, + 0x6E, 0x03, 0x1C, 0x38, 0x7F, 0xC0, 0x7C, 0x00, 0x3F, 0xE1, 0xFF, 0x83, + 0x0E, 0x38, 0x31, 0xC1, 0x8C, 0x0C, 0x60, 0xC3, 0xFC, 0x3F, 0xC1, 0xC0, + 0x0C, 0x00, 0x60, 0x0F, 0xF0, 0x7F, 0x80, 0x03, 0xE0, 0x3F, 0xE1, 0xC3, + 0x8C, 0x07, 0x60, 0x0D, 0x80, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1B, 0x00, + 0x6E, 0x03, 0x1C, 0x38, 0x7F, 0xC0, 0xFC, 0x03, 0x02, 0x1F, 0xFC, 0xFF, + 0xE0, 0x1F, 0xF0, 0x3F, 0xF0, 0x38, 0x70, 0x60, 0x60, 0xC0, 0xC1, 0x87, + 0x07, 0xFC, 0x0F, 0xF0, 0x18, 0xF0, 0x30, 0xE0, 0x60, 0xC1, 0xC1, 0xCF, + 0xE1, 0xFF, 0xC3, 0xC0, 0x0F, 0xB1, 0xFF, 0x30, 0xE6, 0x06, 0x60, 0x67, + 0x80, 0x7F, 0x01, 0xFC, 0x01, 0xC4, 0x0C, 0xC0, 0xCE, 0x18, 0xFF, 0x8B, + 0xE0, 0x7F, 0xFB, 0xFF, 0xD9, 0xCF, 0xCE, 0x7C, 0x63, 0x63, 0x18, 0x18, + 0x01, 0xC0, 0x0E, 0x00, 0x60, 0x03, 0x00, 0x18, 0x0F, 0xF8, 0x7F, 0xC0, + 0x7E, 0xFF, 0xF3, 0xF3, 0x03, 0x1C, 0x0C, 0x60, 0x31, 0x81, 0xC6, 0x06, + 0x38, 0x18, 0xE0, 0x63, 0x03, 0x8C, 0x0C, 0x30, 0x70, 0x7F, 0x80, 0xF8, + 0x00, 0xFC, 0x7F, 0xF8, 0xFD, 0xC0, 0x61, 0x81, 0xC3, 0x87, 0x07, 0x0C, + 0x0E, 0x38, 0x0C, 0x60, 0x19, 0xC0, 0x3F, 0x00, 0x7C, 0x00, 0xF8, 0x00, + 0xE0, 0x01, 0x80, 0x00, 0x7E, 0x7E, 0xFC, 0xFD, 0xC0, 0x73, 0x9C, 0xE7, + 0x79, 0x8E, 0xF7, 0x1B, 0xEE, 0x36, 0xD8, 0x7D, 0xF0, 0xF3, 0xE1, 0xE7, + 0x83, 0x8F, 0x07, 0x1E, 0x1C, 0x38, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x0E, + 0x1C, 0x07, 0x38, 0x07, 0x70, 0x03, 0xE0, 0x03, 0xC0, 0x03, 0xC0, 0x07, + 0xE0, 0x0E, 0xE0, 0x1C, 0x70, 0x38, 0x70, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, + 0xFF, 0xC7, 0xCC, 0x38, 0x73, 0x83, 0x9C, 0x0F, 0xC0, 0x7C, 0x01, 0xC0, + 0x0C, 0x00, 0x60, 0x03, 0x00, 0x38, 0x0F, 0xF8, 0x7F, 0x80, 0x0F, 0xF8, + 0x7F, 0xE1, 0xC7, 0x86, 0x1C, 0x18, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, + 0x0E, 0x00, 0x70, 0xC3, 0x83, 0x1C, 0x1C, 0x7F, 0xF3, 0xFF, 0x80, 0x0F, + 0x87, 0xC3, 0x03, 0x81, 0xC0, 0xC0, 0x60, 0x30, 0x38, 0x1C, 0x0C, 0x06, + 0x03, 0x03, 0x81, 0xC0, 0xC0, 0x60, 0x3E, 0x3F, 0x00, 0x41, 0xC3, 0x83, + 0x07, 0x0E, 0x1C, 0x18, 0x38, 0x70, 0xE0, 0xC1, 0xC3, 0x83, 0x06, 0x0E, + 0x1C, 0x18, 0x20, 0x1F, 0x0F, 0x80, 0xC0, 0xE0, 0x70, 0x30, 0x18, 0x0C, + 0x0E, 0x07, 0x03, 0x01, 0x80, 0xC0, 0xE0, 0x70, 0x30, 0x18, 0x7C, 0x3E, + 0x00, 0x02, 0x01, 0x80, 0xF0, 0x7E, 0x3B, 0x9C, 0x7E, 0x1F, 0x03, 0xFF, + 0xFF, 0xFF, 0xFC, 0xCE, 0x73, 0x1F, 0xC3, 0xFE, 0x00, 0x60, 0x06, 0x0F, + 0xE3, 0xFE, 0x70, 0xCC, 0x0C, 0xC3, 0xCF, 0xFF, 0x7F, 0xF0, 0x1E, 0x00, + 0x3C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xDF, 0x81, 0xFF, 0x83, 0xC3, 0x8F, + 0x03, 0x1C, 0x06, 0x38, 0x0C, 0x70, 0x18, 0xE0, 0x63, 0xE1, 0x9F, 0xFE, + 0x3D, 0xF8, 0x00, 0x0F, 0xF3, 0xFF, 0x30, 0x76, 0x07, 0xE0, 0x6C, 0x00, + 0xC0, 0x0C, 0x00, 0xE0, 0x67, 0xFE, 0x3F, 0x80, 0x00, 0x3C, 0x00, 0xF0, + 0x01, 0xC0, 0x06, 0x07, 0xD8, 0x7F, 0xE3, 0x0F, 0x98, 0x1E, 0x60, 0x73, + 0x01, 0xCC, 0x07, 0x30, 0x3C, 0xE1, 0xF1, 0xFF, 0xE3, 0xF7, 0x80, 0x0F, + 0xC1, 0xFE, 0x78, 0x76, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xE0, + 0xE7, 0xFE, 0x1F, 0x80, 0x00, 0xFC, 0x07, 0xF8, 0x0C, 0x00, 0x38, 0x01, + 0xFF, 0x07, 0xFE, 0x01, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x18, 0x00, 0x30, + 0x00, 0x60, 0x01, 0xC0, 0x1F, 0xF8, 0x3F, 0xF0, 0x00, 0x0F, 0xBC, 0x7F, + 0xF3, 0x0F, 0x18, 0x1C, 0xC0, 0x73, 0x01, 0x8C, 0x0E, 0x30, 0x38, 0xE3, + 0xE1, 0xFF, 0x83, 0xEC, 0x00, 0x30, 0x01, 0xC0, 0x06, 0x07, 0xF0, 0x1F, + 0x80, 0x1E, 0x01, 0xF0, 0x03, 0x00, 0x18, 0x00, 0xDE, 0x0F, 0xF8, 0x78, + 0xC3, 0x86, 0x18, 0x30, 0xC1, 0x8E, 0x1C, 0x70, 0xE3, 0x06, 0x7E, 0xFF, + 0xE7, 0xE0, 0x03, 0x80, 0x70, 0x00, 0x0F, 0xC1, 0xF0, 0x06, 0x00, 0xC0, + 0x38, 0x07, 0x00, 0xC0, 0x18, 0x03, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0x70, + 0x07, 0x00, 0x00, 0xFF, 0x1F, 0xF0, 0x07, 0x00, 0x70, 0x06, 0x00, 0x60, + 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x1C, 0x03, 0x87, + 0xF0, 0xFE, 0x00, 0x1E, 0x00, 0x78, 0x00, 0xE0, 0x03, 0x80, 0x0C, 0xFC, + 0x33, 0xE0, 0xDE, 0x07, 0xE0, 0x1F, 0x00, 0x7C, 0x01, 0xF8, 0x06, 0xF0, + 0x39, 0xC3, 0xE7, 0xEF, 0x1F, 0x80, 0x0F, 0x81, 0xF0, 0x06, 0x01, 0xC0, + 0x38, 0x06, 0x00, 0xC0, 0x18, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0x61, + 0xFF, 0xFF, 0xF8, 0x3F, 0xBC, 0x7F, 0xFC, 0xF3, 0x98, 0xC6, 0x33, 0x9C, + 0xE7, 0x39, 0xCC, 0x63, 0x18, 0xC6, 0x31, 0x8D, 0xF7, 0xBF, 0xEF, 0x78, + 0x3D, 0xE1, 0xFF, 0x8F, 0x8C, 0x38, 0x61, 0x83, 0x0C, 0x18, 0xE1, 0xC7, + 0x0E, 0x30, 0x67, 0xEF, 0xFE, 0x7E, 0x07, 0xC1, 0xFE, 0x38, 0x76, 0x03, + 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x06, 0xE1, 0xC7, 0xF8, 0x3E, 0x00, 0x1E, + 0xFC, 0x1F, 0xFE, 0x0F, 0x87, 0x0F, 0x03, 0x0E, 0x03, 0x0E, 0x03, 0x0E, + 0x07, 0x0E, 0x06, 0x1F, 0x0C, 0x1F, 0xF8, 0x19, 0xF0, 0x18, 0x00, 0x18, + 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0x0F, 0xDE, 0x3F, 0xFC, 0xC3, + 0xE3, 0x03, 0x84, 0x07, 0x18, 0x0E, 0x30, 0x1C, 0x60, 0x78, 0xE1, 0xE0, + 0xFF, 0xC0, 0xF9, 0x80, 0x03, 0x00, 0x0E, 0x00, 0x1C, 0x01, 0xFC, 0x03, + 0xF8, 0x1E, 0x78, 0x7F, 0xF0, 0x7C, 0xC3, 0xC0, 0x0E, 0x00, 0x30, 0x00, + 0xC0, 0x03, 0x00, 0x1C, 0x03, 0xFF, 0x0F, 0xFC, 0x00, 0x07, 0xF1, 0xFF, + 0x30, 0x73, 0x86, 0x3F, 0x81, 0xFE, 0x03, 0xE6, 0x06, 0xE0, 0xEF, 0xFC, + 0xFF, 0x00, 0x0C, 0x07, 0x01, 0x83, 0xFF, 0xFF, 0xCE, 0x03, 0x00, 0xC0, + 0x30, 0x1C, 0x07, 0x01, 0x83, 0x7F, 0xCF, 0xC0, 0xF0, 0xFF, 0x1F, 0x60, + 0x76, 0x07, 0x60, 0x76, 0x06, 0x60, 0x66, 0x0E, 0x61, 0xE7, 0xFF, 0x3E, + 0xF0, 0x7E, 0x7E, 0xFC, 0xFC, 0xE0, 0xC0, 0xC3, 0x81, 0x86, 0x03, 0x98, + 0x07, 0x70, 0x06, 0xC0, 0x0F, 0x80, 0x1E, 0x00, 0x38, 0x00, 0xF8, 0x7F, + 0xE3, 0xE6, 0x63, 0x1B, 0xDC, 0x6F, 0x61, 0xFF, 0x87, 0xFC, 0x1E, 0xF0, + 0x73, 0x81, 0xCE, 0x06, 0x38, 0x00, 0x3E, 0x7C, 0xF9, 0xF1, 0xE7, 0x03, + 0xF8, 0x07, 0xC0, 0x1F, 0x01, 0xFC, 0x0F, 0x38, 0x78, 0xFB, 0xF7, 0xEF, + 0x9F, 0x80, 0x1F, 0x1F, 0x3E, 0x1F, 0x1C, 0x1C, 0x0C, 0x18, 0x0E, 0x38, + 0x0E, 0x70, 0x06, 0x60, 0x07, 0xE0, 0x07, 0xC0, 0x07, 0xC0, 0x03, 0x80, + 0x07, 0x00, 0x07, 0x00, 0x0E, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x1F, 0xF1, + 0xFF, 0x38, 0xE3, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC6, 0x38, 0x67, + 0xFE, 0x7F, 0xE0, 0x01, 0xC0, 0xF0, 0x70, 0x18, 0x06, 0x03, 0x80, 0xE0, + 0x30, 0x1C, 0x3E, 0x0F, 0x00, 0x60, 0x18, 0x06, 0x03, 0x80, 0xC0, 0x30, + 0x0F, 0x01, 0xC0, 0x0C, 0x71, 0xC7, 0x18, 0x63, 0x8E, 0x30, 0xC3, 0x1C, + 0x71, 0x86, 0x38, 0xE3, 0x04, 0x00, 0x0E, 0x07, 0x80, 0xC0, 0x60, 0x70, + 0x30, 0x18, 0x0C, 0x06, 0x01, 0xC1, 0xE1, 0xC0, 0xC0, 0xE0, 0x70, 0x30, + 0x38, 0x78, 0x38, 0x00, 0x3C, 0x27, 0xE6, 0xEF, 0xCC, 0x38}; + +const GFXglyph FreeMonoBoldOblique12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 14, 0, 1}, // 0x20 ' ' + {0, 6, 15, 14, 6, -14}, // 0x21 '!' + {12, 8, 7, 14, 6, -13}, // 0x22 '"' + {19, 13, 18, 14, 2, -15}, // 0x23 '#' + {49, 11, 20, 14, 3, -16}, // 0x24 '$' + {77, 11, 15, 14, 3, -14}, // 0x25 '%' + {98, 11, 13, 14, 2, -12}, // 0x26 '&' + {116, 3, 7, 14, 8, -13}, // 0x27 ''' + {119, 7, 19, 14, 7, -14}, // 0x28 '(' + {136, 7, 19, 14, 2, -14}, // 0x29 ')' + {153, 11, 10, 14, 4, -14}, // 0x2A '*' + {167, 12, 13, 14, 3, -12}, // 0x2B '+' + {187, 6, 7, 14, 3, -2}, // 0x2C ',' + {193, 13, 2, 14, 2, -7}, // 0x2D '-' + {197, 3, 3, 14, 6, -2}, // 0x2E '.' + {199, 14, 20, 14, 2, -16}, // 0x2F '/' + {234, 11, 15, 14, 3, -14}, // 0x30 '0' + {255, 11, 15, 14, 2, -14}, // 0x31 '1' + {276, 13, 15, 14, 1, -14}, // 0x32 '2' + {301, 12, 15, 14, 2, -14}, // 0x33 '3' + {324, 11, 14, 14, 3, -13}, // 0x34 '4' + {344, 12, 15, 14, 2, -14}, // 0x35 '5' + {367, 11, 15, 14, 4, -14}, // 0x36 '6' + {388, 11, 15, 14, 4, -14}, // 0x37 '7' + {409, 11, 15, 14, 3, -14}, // 0x38 '8' + {430, 11, 15, 14, 3, -14}, // 0x39 '9' + {451, 5, 11, 14, 5, -10}, // 0x3A ':' + {458, 7, 15, 14, 3, -10}, // 0x3B ';' + {472, 13, 11, 14, 2, -11}, // 0x3C '<' + {490, 13, 7, 14, 2, -9}, // 0x3D '=' + {502, 13, 11, 14, 2, -11}, // 0x3E '>' + {520, 9, 14, 14, 5, -13}, // 0x3F '?' + {536, 12, 19, 14, 2, -14}, // 0x40 '@' + {565, 15, 14, 14, 0, -13}, // 0x41 'A' + {592, 13, 14, 14, 1, -13}, // 0x42 'B' + {615, 14, 14, 14, 2, -13}, // 0x43 'C' + {640, 13, 14, 14, 1, -13}, // 0x44 'D' + {663, 15, 14, 14, 0, -13}, // 0x45 'E' + {690, 16, 14, 14, 0, -13}, // 0x46 'F' + {718, 14, 14, 14, 1, -13}, // 0x47 'G' + {743, 16, 14, 14, 0, -13}, // 0x48 'H' + {771, 12, 14, 14, 2, -13}, // 0x49 'I' + {792, 16, 14, 14, 0, -13}, // 0x4A 'J' + {820, 16, 14, 14, 0, -13}, // 0x4B 'K' + {848, 13, 14, 14, 1, -13}, // 0x4C 'L' + {871, 18, 14, 14, 0, -13}, // 0x4D 'M' + {903, 16, 14, 14, 1, -13}, // 0x4E 'N' + {931, 14, 14, 14, 1, -13}, // 0x4F 'O' + {956, 13, 14, 14, 1, -13}, // 0x50 'P' + {979, 14, 17, 14, 1, -13}, // 0x51 'Q' + {1009, 15, 14, 14, 0, -13}, // 0x52 'R' + {1036, 12, 14, 14, 3, -13}, // 0x53 'S' + {1057, 13, 14, 14, 2, -13}, // 0x54 'T' + {1080, 14, 14, 14, 2, -13}, // 0x55 'U' + {1105, 15, 14, 14, 1, -13}, // 0x56 'V' + {1132, 15, 14, 14, 1, -13}, // 0x57 'W' + {1159, 16, 14, 14, 0, -13}, // 0x58 'X' + {1187, 13, 14, 14, 2, -13}, // 0x59 'Y' + {1210, 14, 14, 14, 1, -13}, // 0x5A 'Z' + {1235, 9, 19, 14, 5, -14}, // 0x5B '[' + {1257, 7, 20, 14, 5, -16}, // 0x5C '\' + {1275, 9, 19, 14, 3, -14}, // 0x5D ']' + {1297, 10, 8, 14, 4, -15}, // 0x5E '^' + {1307, 15, 2, 14, -1, 4}, // 0x5F '_' + {1311, 4, 4, 14, 7, -15}, // 0x60 '`' + {1313, 12, 11, 14, 2, -10}, // 0x61 'a' + {1330, 15, 15, 14, -1, -14}, // 0x62 'b' + {1359, 12, 11, 14, 2, -10}, // 0x63 'c' + {1376, 14, 15, 14, 2, -14}, // 0x64 'd' + {1403, 12, 11, 14, 2, -10}, // 0x65 'e' + {1420, 15, 15, 14, 2, -14}, // 0x66 'f' + {1449, 14, 16, 14, 2, -10}, // 0x67 'g' + {1477, 13, 15, 14, 1, -14}, // 0x68 'h' + {1502, 11, 14, 14, 2, -13}, // 0x69 'i' + {1522, 12, 19, 14, 1, -13}, // 0x6A 'j' + {1551, 14, 15, 14, 1, -14}, // 0x6B 'k' + {1578, 11, 15, 14, 2, -14}, // 0x6C 'l' + {1599, 15, 11, 14, 0, -10}, // 0x6D 'm' + {1620, 13, 11, 14, 1, -10}, // 0x6E 'n' + {1638, 12, 11, 14, 2, -10}, // 0x6F 'o' + {1655, 16, 16, 14, -1, -10}, // 0x70 'p' + {1687, 15, 16, 14, 1, -10}, // 0x71 'q' + {1717, 14, 11, 14, 1, -10}, // 0x72 'r' + {1737, 12, 11, 14, 2, -10}, // 0x73 's' + {1754, 10, 14, 14, 2, -13}, // 0x74 't' + {1772, 12, 11, 14, 2, -10}, // 0x75 'u' + {1789, 15, 11, 14, 1, -10}, // 0x76 'v' + {1810, 14, 11, 14, 2, -10}, // 0x77 'w' + {1830, 14, 11, 14, 1, -10}, // 0x78 'x' + {1850, 16, 16, 14, 0, -10}, // 0x79 'y' + {1882, 12, 11, 14, 2, -10}, // 0x7A 'z' + {1899, 10, 19, 14, 4, -14}, // 0x7B '{' + {1923, 6, 19, 14, 5, -14}, // 0x7C '|' + {1938, 9, 19, 14, 3, -14}, // 0x7D '}' + {1960, 12, 4, 14, 3, -7}}; // 0x7E '~' + +const GFXfont FreeMonoBoldOblique12pt7b PROGMEM = { + (uint8_t *)FreeMonoBoldOblique12pt7bBitmaps, + (GFXglyph *)FreeMonoBoldOblique12pt7bGlyphs, 0x20, 0x7E, 24}; + +// Approx. 2638 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique18pt7b.h new file mode 100644 index 0000000..76b0764 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique18pt7b.h @@ -0,0 +1,459 @@ +const uint8_t FreeMonoBoldOblique18pt7bBitmaps[] PROGMEM = { + 0x0F, 0x07, 0xC7, 0xE3, 0xF1, 0xF0, 0xF8, 0xFC, 0x7C, 0x3E, 0x1F, 0x0F, + 0x07, 0x87, 0xC3, 0xC1, 0xE0, 0x60, 0x00, 0x38, 0x3E, 0x1F, 0x0F, 0x83, + 0x80, 0xF8, 0xFF, 0x0E, 0xF1, 0xEF, 0x1E, 0xE1, 0xCE, 0x1C, 0xC1, 0xCC, + 0x18, 0xC1, 0x88, 0x18, 0x00, 0xE3, 0x80, 0x79, 0xE0, 0x1C, 0x70, 0x07, + 0x1C, 0x03, 0xCF, 0x00, 0xF3, 0xC0, 0x38, 0xE0, 0x7F, 0xFF, 0x3F, 0xFF, + 0xCF, 0xFF, 0xF3, 0xFF, 0xF8, 0x3C, 0xF0, 0x0F, 0x3C, 0x03, 0x8E, 0x0F, + 0xFF, 0xE3, 0xFF, 0xFC, 0xFF, 0xFF, 0x3F, 0xFF, 0x83, 0xCF, 0x00, 0xF3, + 0xC0, 0x38, 0xE0, 0x1E, 0x78, 0x07, 0x9E, 0x01, 0xC7, 0x00, 0x71, 0xC0, + 0x00, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x07, 0x80, 0x03, 0xF0, 0x03, 0xFF, + 0x81, 0xFF, 0xF0, 0xFF, 0xF8, 0x3C, 0x1E, 0x1E, 0x07, 0x87, 0x80, 0x01, + 0xF0, 0x00, 0x7F, 0xC0, 0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x07, 0xF0, 0x00, + 0x3C, 0x70, 0x0F, 0x3C, 0x03, 0xCF, 0x83, 0xE3, 0xFF, 0xF8, 0xFF, 0xFC, + 0x3F, 0xFE, 0x0C, 0xFE, 0x00, 0x1C, 0x00, 0x07, 0x00, 0x03, 0xC0, 0x00, + 0xF0, 0x00, 0x18, 0x00, 0x03, 0xC0, 0x0F, 0xE0, 0x1C, 0x70, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x70, 0x38, 0xE0, 0x1F, 0xC3, 0x0F, 0x1F, 0x01, 0xFC, + 0x0F, 0xE0, 0x7F, 0x00, 0xF8, 0xF0, 0x83, 0xF8, 0x07, 0x1C, 0x0E, 0x0C, + 0x0C, 0x0C, 0x0C, 0x1C, 0x0E, 0x38, 0x07, 0xF0, 0x03, 0xC0, 0x00, 0x7A, + 0x01, 0xFF, 0x03, 0xFF, 0x07, 0xFE, 0x0F, 0x9C, 0x0F, 0x00, 0x0F, 0x00, + 0x0F, 0x00, 0x07, 0x80, 0x1F, 0x80, 0x3F, 0xC0, 0x7F, 0xCF, 0x79, 0xFF, + 0xF1, 0xFE, 0xF1, 0xFC, 0xF0, 0xF8, 0xFF, 0xFE, 0xFF, 0xFE, 0x7F, 0xFE, + 0x1F, 0xBC, 0x7B, 0xFD, 0xEF, 0x73, 0x9C, 0xC6, 0x00, 0x01, 0xC0, 0xF0, + 0x3C, 0x1E, 0x0F, 0x03, 0xC1, 0xE0, 0x70, 0x3C, 0x0F, 0x07, 0x81, 0xE0, + 0x78, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3E, + 0x07, 0x81, 0xE0, 0x7C, 0x1F, 0x03, 0x80, 0x07, 0x03, 0xC0, 0xF8, 0x3E, + 0x07, 0x81, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, + 0xF0, 0x78, 0x1E, 0x07, 0x81, 0xC0, 0xF0, 0x3C, 0x1E, 0x07, 0x83, 0xC1, + 0xE0, 0x78, 0x3C, 0x0E, 0x00, 0x00, 0xC0, 0x03, 0xC0, 0x07, 0x00, 0x0E, + 0x02, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xBF, 0xFE, 0x1F, 0xF0, 0x1F, 0x80, + 0x7F, 0x81, 0xEF, 0x07, 0x8F, 0x0F, 0x1E, 0x08, 0x10, 0x00, 0x00, 0x70, + 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x38, 0x00, + 0x1E, 0x03, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, + 0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x07, 0x80, + 0x01, 0xC0, 0x00, 0x70, 0x00, 0x0F, 0x87, 0x87, 0x83, 0x83, 0xC1, 0xC1, + 0xC0, 0xC0, 0xE0, 0x60, 0x00, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x00, 0x38, 0x00, 0x03, 0xC0, + 0x00, 0x1C, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xE0, 0x00, 0x0F, + 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x07, 0x80, 0x00, + 0x78, 0x00, 0x03, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, + 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0xF0, + 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x07, 0x80, 0x00, 0x78, 0x00, 0x03, + 0xC0, 0x00, 0x3C, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x01, 0xFF, + 0x01, 0xFF, 0xC1, 0xFF, 0xE1, 0xF1, 0xF9, 0xE0, 0x7C, 0xF0, 0x1E, 0xF0, + 0x0F, 0x78, 0x07, 0xB8, 0x03, 0x9C, 0x03, 0xDE, 0x01, 0xCF, 0x00, 0xE7, + 0x00, 0x73, 0xC0, 0x79, 0xE0, 0x3C, 0xF0, 0x1C, 0x78, 0x1E, 0x3E, 0x1E, + 0x0F, 0xFF, 0x07, 0xFF, 0x01, 0xFF, 0x00, 0x7E, 0x00, 0x00, 0x7C, 0x03, + 0xF8, 0x0F, 0xE0, 0x7F, 0xC0, 0xF7, 0x81, 0x8F, 0x00, 0x1C, 0x00, 0x38, + 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x0E, 0x00, 0x3C, 0x00, + 0x78, 0x00, 0xF0, 0x01, 0xC0, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xC0, 0x00, 0x1F, 0x00, 0x07, 0xFC, 0x00, 0xFF, 0xE0, 0x1F, + 0xFF, 0x03, 0xC1, 0xF0, 0x78, 0x0F, 0x07, 0x80, 0xF0, 0x70, 0x0F, 0x00, + 0x01, 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x3F, 0x00, + 0x07, 0xE0, 0x01, 0xFC, 0x00, 0x3F, 0x80, 0x07, 0xE0, 0x01, 0xF8, 0x00, + 0x3F, 0x03, 0x87, 0xFF, 0xF8, 0x7F, 0xFF, 0x87, 0xFF, 0xF8, 0xFF, 0xFF, + 0x00, 0x00, 0xFE, 0x00, 0xFF, 0xC0, 0x7F, 0xF8, 0x3F, 0xFF, 0x0E, 0x07, + 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, 0x00, 0x07, 0x80, 0x1F, 0xC0, + 0x0F, 0xE0, 0x03, 0xF0, 0x00, 0xFF, 0x00, 0x03, 0xE0, 0x00, 0x78, 0x00, + 0x1E, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x03, 0xF1, 0xFF, 0xF8, 0xFF, 0xFC, + 0x3F, 0xFE, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0x00, 0x3F, 0x00, 0x7F, 0x00, + 0xFE, 0x00, 0xFE, 0x01, 0xEE, 0x03, 0xDE, 0x07, 0x9E, 0x0F, 0x1C, 0x1E, + 0x1C, 0x3C, 0x3C, 0x78, 0x3C, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, + 0xFC, 0x00, 0x70, 0x03, 0xFC, 0x07, 0xFC, 0x07, 0xFC, 0x07, 0xF8, 0x07, + 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0xFC, 0x3F, 0xFE, 0x0F, 0x00, 0x03, 0xC0, + 0x00, 0xE0, 0x00, 0x3B, 0xE0, 0x1F, 0xFE, 0x07, 0xFF, 0xC1, 0xFF, 0xF8, + 0x78, 0x3E, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, + 0x0F, 0x18, 0x0F, 0xCF, 0xFF, 0xE3, 0xFF, 0xF0, 0x7F, 0xF8, 0x07, 0xF0, + 0x00, 0x00, 0x0F, 0xC0, 0x0F, 0xFC, 0x03, 0xFF, 0x81, 0xFF, 0xE0, 0x7F, + 0x00, 0x1F, 0x80, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x9F, + 0x01, 0xEF, 0xF0, 0x3F, 0xFF, 0x0F, 0xFF, 0xF1, 0xFC, 0x3E, 0x3E, 0x03, + 0xC7, 0x80, 0x78, 0xF0, 0x0F, 0x1E, 0x03, 0xC3, 0xE0, 0xF8, 0x7F, 0xFE, + 0x07, 0xFF, 0x80, 0x7F, 0xE0, 0x07, 0xF0, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0E, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x3C, + 0x00, 0x78, 0x00, 0x70, 0x00, 0xF0, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xC0, + 0x03, 0xC0, 0x07, 0x80, 0x07, 0x80, 0x0F, 0x00, 0x0E, 0x00, 0x1E, 0x00, + 0x1C, 0x00, 0x1C, 0x00, 0x00, 0x7E, 0x00, 0x3F, 0xF0, 0x0F, 0xFF, 0x03, + 0xFF, 0xF0, 0xF8, 0x3E, 0x3E, 0x03, 0xC7, 0x80, 0x78, 0xF0, 0x0F, 0x1E, + 0x03, 0xC3, 0xE0, 0xF0, 0x3F, 0xFC, 0x03, 0xFF, 0x00, 0xFF, 0xE0, 0x7F, + 0xFE, 0x1F, 0x83, 0xE3, 0xC0, 0x3C, 0xF0, 0x07, 0x9E, 0x01, 0xF3, 0xE0, + 0x7C, 0x7F, 0xFF, 0x87, 0xFF, 0xE0, 0x7F, 0xF0, 0x03, 0xF8, 0x00, 0x00, + 0x7E, 0x00, 0x7F, 0xC0, 0x3F, 0xF8, 0x1F, 0xFE, 0x0F, 0x87, 0xC3, 0xC0, + 0xF1, 0xE0, 0x3C, 0x78, 0x0F, 0x1E, 0x03, 0xC7, 0x81, 0xF1, 0xF1, 0xFC, + 0x7F, 0xFE, 0x0F, 0xFF, 0x81, 0xFD, 0xE0, 0x3E, 0xF0, 0x00, 0x7C, 0x00, + 0x3E, 0x00, 0x1F, 0x00, 0x1F, 0x81, 0xFF, 0xC0, 0xFF, 0xE0, 0x3F, 0xE0, + 0x07, 0xE0, 0x00, 0x1C, 0x7C, 0xF9, 0xF1, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x8F, 0x9F, 0x3E, 0x38, 0x01, 0xC0, 0x7C, 0x0F, 0x81, 0xF0, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0xF0, 0x1E, + 0x07, 0x80, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x30, 0x0E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0xFE, 0x00, + 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x00, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0xFE, + 0x00, 0x1F, 0xC0, 0x01, 0xFC, 0x00, 0x1F, 0xC0, 0x01, 0xF0, 0x00, 0x38, + 0x3F, 0xFF, 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x9F, 0xFF, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0xF7, 0xFF, 0xFE, 0xFF, 0xFF, 0xDF, 0xFF, 0xF0, + 0x00, 0x00, 0x03, 0x80, 0x00, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0x7F, 0x00, + 0x07, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xFC, 0x00, 0x1F, 0xC0, 0x0F, 0xE0, + 0x07, 0xF0, 0x07, 0xF8, 0x03, 0xF8, 0x01, 0xFC, 0x00, 0x3E, 0x00, 0x07, + 0x00, 0x00, 0x07, 0xE0, 0xFF, 0xC7, 0xFF, 0xBF, 0xFF, 0xF0, 0x7F, 0x80, + 0xFE, 0x03, 0xC0, 0x0F, 0x00, 0x78, 0x0F, 0xE1, 0xFE, 0x0F, 0xF0, 0x7E, + 0x01, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x70, 0x03, 0xE0, 0x0F, 0x80, 0x3E, + 0x00, 0x70, 0x00, 0x00, 0x3E, 0x00, 0x3F, 0xE0, 0x1F, 0xF8, 0x0F, 0x0F, + 0x07, 0x01, 0xC3, 0x80, 0x71, 0xE0, 0x1C, 0x70, 0x0E, 0x18, 0x0F, 0x8E, + 0x1F, 0xE3, 0x8F, 0xF0, 0xE7, 0x9C, 0x33, 0xC7, 0x1C, 0xE1, 0xC7, 0x38, + 0x71, 0xCF, 0x18, 0x73, 0xFE, 0x38, 0x7F, 0xCE, 0x0F, 0xF3, 0x80, 0x00, + 0xE0, 0x00, 0x38, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0xC0, 0x7F, 0xF0, 0x0F, + 0xF8, 0x01, 0xF8, 0x00, 0x01, 0xFF, 0x80, 0x07, 0xFE, 0x00, 0x1F, 0xF8, + 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x00, 0x07, 0xBC, 0x00, + 0x1C, 0xF0, 0x00, 0xF3, 0xC0, 0x07, 0x87, 0x80, 0x1E, 0x1E, 0x00, 0xF0, + 0x78, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, 0x80, 0xFF, 0xFF, 0x07, 0xFF, 0xFC, + 0x1E, 0x00, 0xF1, 0xFE, 0x1F, 0xFF, 0xF8, 0x7F, 0xFF, 0xE1, 0xFF, 0xFF, + 0x07, 0xF8, 0x0F, 0xFF, 0xC0, 0x7F, 0xFF, 0x87, 0xFF, 0xFC, 0x1F, 0xFF, + 0xF0, 0x38, 0x0F, 0x81, 0xC0, 0x3C, 0x1E, 0x01, 0xE0, 0xF0, 0x3E, 0x07, + 0xFF, 0xE0, 0x3F, 0xFE, 0x03, 0xFF, 0xF8, 0x1F, 0xFF, 0xE0, 0xE0, 0x1F, + 0x87, 0x00, 0x3C, 0x38, 0x01, 0xE3, 0xC0, 0x0F, 0x1E, 0x00, 0xF3, 0xFF, + 0xFF, 0xBF, 0xFF, 0xF9, 0xFF, 0xFF, 0x8F, 0xFF, 0xF0, 0x00, 0x00, 0x7F, + 0x30, 0x0F, 0xFF, 0xC1, 0xFF, 0xFE, 0x1F, 0xFF, 0xF1, 0xF8, 0x3F, 0x1F, + 0x00, 0x78, 0xF0, 0x03, 0xCF, 0x80, 0x1C, 0x78, 0x00, 0x03, 0xC0, 0x00, + 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, + 0x00, 0x1F, 0x00, 0x38, 0x7E, 0x07, 0xC3, 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, + 0x3F, 0xFC, 0x00, 0x7F, 0x80, 0x00, 0x0F, 0xFF, 0x80, 0x7F, 0xFE, 0x07, + 0xFF, 0xF8, 0x1F, 0xFF, 0xE0, 0x78, 0x1F, 0x03, 0x80, 0x7C, 0x1C, 0x01, + 0xE1, 0xE0, 0x0F, 0x0F, 0x00, 0x78, 0x70, 0x03, 0xC3, 0x80, 0x1E, 0x1C, + 0x00, 0xF1, 0xE0, 0x0F, 0x0F, 0x00, 0x78, 0x70, 0x07, 0xC3, 0x80, 0x7C, + 0x3C, 0x07, 0xC3, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFC, 0x0F, 0xFF, + 0x80, 0x00, 0x07, 0xFF, 0xFC, 0x3F, 0xFF, 0xF0, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFF, 0x03, 0xC0, 0x3C, 0x0F, 0x00, 0xE0, 0x3C, 0x73, 0x80, 0xE3, 0xCC, + 0x03, 0xFF, 0x00, 0x1F, 0xFC, 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x80, 0x07, + 0x1E, 0x00, 0x3C, 0x70, 0x00, 0xF0, 0x07, 0x03, 0xC0, 0x1C, 0x0E, 0x00, + 0xF1, 0xFF, 0xFF, 0xC7, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, 0x7F, 0xFF, 0xE0, + 0x07, 0xFF, 0xFE, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF0, 0x7F, 0xFF, 0xE0, + 0x3C, 0x01, 0xC0, 0x70, 0x07, 0x80, 0xE1, 0x8E, 0x03, 0xC7, 0x1C, 0x07, + 0xFE, 0x00, 0x0F, 0xFC, 0x00, 0x1F, 0xF8, 0x00, 0x3F, 0xF0, 0x00, 0xF1, + 0xC0, 0x01, 0xE3, 0x80, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x07, 0xFF, 0x80, 0x0F, 0xFE, 0x00, + 0x00, 0x00, 0x3F, 0x18, 0x0F, 0xFF, 0xC0, 0xFF, 0xFE, 0x0F, 0xFF, 0xF0, + 0xFC, 0x0F, 0x0F, 0x80, 0x38, 0xF8, 0x01, 0x87, 0x80, 0x00, 0x78, 0x00, + 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x7F, 0xEF, 0x07, 0xFF, 0x78, + 0x3F, 0xFB, 0xC0, 0xFF, 0x9E, 0x00, 0x38, 0xFC, 0x03, 0xC3, 0xFF, 0xFE, + 0x1F, 0xFF, 0xE0, 0x3F, 0xFC, 0x00, 0x7F, 0x80, 0x00, 0x03, 0xF8, 0xFE, + 0x0F, 0xF3, 0xFC, 0x1F, 0xE7, 0xF8, 0x3F, 0x8F, 0xE0, 0x3C, 0x07, 0x80, + 0x70, 0x0E, 0x00, 0xE0, 0x1C, 0x03, 0xC0, 0x78, 0x07, 0x80, 0xF0, 0x0F, + 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x3F, 0xFF, 0x00, 0xFF, 0xFE, 0x01, 0xE0, + 0x3C, 0x03, 0x80, 0x70, 0x07, 0x00, 0xE0, 0x1E, 0x03, 0xC0, 0xFF, 0x1F, + 0xE1, 0xFE, 0x7F, 0xC7, 0xFC, 0xFF, 0x87, 0xF1, 0xFE, 0x00, 0x07, 0xFF, + 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0x87, 0xFF, 0xE0, 0x07, 0x80, 0x00, 0xE0, + 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0x80, + 0x00, 0x70, 0x00, 0x1E, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x0E, 0x00, + 0x01, 0xC0, 0x0F, 0xFF, 0xC3, 0xFF, 0xF8, 0x7F, 0xFF, 0x07, 0xFF, 0xE0, + 0x00, 0x3F, 0xFE, 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x03, 0xFF, 0xE0, + 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x01, 0xE0, 0x00, + 0x03, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x1C, 0x03, 0x80, + 0x78, 0x0F, 0x00, 0xF0, 0x1E, 0x01, 0xC0, 0x38, 0x07, 0x80, 0x70, 0x1F, + 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xF0, 0x03, 0xFF, 0xC0, 0x00, 0xFC, 0x00, + 0x00, 0x07, 0xF8, 0xFC, 0x1F, 0xFB, 0xFC, 0x3F, 0xE7, 0xF0, 0x7F, 0xCF, + 0xE0, 0x3C, 0x1E, 0x00, 0x70, 0xF8, 0x00, 0xE3, 0xE0, 0x03, 0xCF, 0x00, + 0x07, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xF0, 0x00, + 0xF9, 0xF0, 0x01, 0xE1, 0xE0, 0x03, 0x83, 0xE0, 0x07, 0x03, 0xC0, 0x1E, + 0x07, 0x80, 0xFF, 0x8F, 0xE3, 0xFF, 0x0F, 0xC7, 0xFE, 0x1F, 0x8F, 0xF8, + 0x3E, 0x00, 0x0F, 0xFE, 0x00, 0xFF, 0xF0, 0x1F, 0xFE, 0x00, 0xFF, 0xE0, + 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x03, 0xC0, + 0x00, 0x3C, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x07, 0x80, 0x60, 0x78, + 0x0F, 0x07, 0x80, 0xF0, 0x70, 0x0E, 0x07, 0x00, 0xE7, 0xFF, 0xFE, 0xFF, + 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xC0, 0x0F, 0xC0, 0x1F, 0x87, 0xE0, + 0x0F, 0xC7, 0xF8, 0x0F, 0xE1, 0xFC, 0x0F, 0xE0, 0x7E, 0x07, 0xE0, 0x3F, + 0x07, 0xF0, 0x3F, 0xC7, 0xF8, 0x1F, 0xE3, 0xF8, 0x0E, 0xF3, 0xDC, 0x07, + 0x7B, 0xDE, 0x03, 0x9F, 0xEF, 0x03, 0xCF, 0xE7, 0x81, 0xE7, 0xE3, 0x80, + 0xE3, 0xF1, 0xC0, 0x70, 0xF1, 0xE0, 0x38, 0x70, 0xF0, 0x3C, 0x00, 0x70, + 0x3F, 0xC1, 0xFE, 0x3F, 0xE1, 0xFF, 0x1F, 0xF0, 0xFF, 0x8F, 0xF0, 0x7F, + 0x80, 0x0F, 0xC1, 0xFE, 0x1F, 0xC1, 0xFF, 0x1F, 0xC3, 0xFE, 0x1F, 0xE1, + 0xFE, 0x07, 0xE0, 0x38, 0x07, 0xF0, 0x78, 0x07, 0xF0, 0x78, 0x0F, 0xF8, + 0x70, 0x0F, 0x78, 0x70, 0x0E, 0x78, 0xF0, 0x0E, 0x7C, 0xF0, 0x1E, 0x3C, + 0xF0, 0x1E, 0x3E, 0xE0, 0x1E, 0x1E, 0xE0, 0x1C, 0x1F, 0xE0, 0x1C, 0x0F, + 0xE0, 0x3C, 0x0F, 0xE0, 0x7F, 0x87, 0xC0, 0xFF, 0x87, 0xC0, 0xFF, 0x87, + 0xC0, 0xFF, 0x03, 0xC0, 0x00, 0x7E, 0x00, 0x1F, 0xF8, 0x07, 0xFF, 0xC0, + 0xFF, 0xFE, 0x1F, 0x87, 0xE3, 0xE0, 0x1F, 0x3C, 0x01, 0xF7, 0xC0, 0x0F, + 0x78, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x01, + 0xEF, 0x00, 0x3E, 0xF8, 0x03, 0xCF, 0x80, 0x7C, 0x7C, 0x1F, 0x87, 0xFF, + 0xF0, 0x3F, 0xFE, 0x01, 0xFF, 0x80, 0x07, 0xE0, 0x00, 0x0F, 0xFF, 0x80, + 0x7F, 0xFF, 0x07, 0xFF, 0xFC, 0x1F, 0xFF, 0xF0, 0x38, 0x0F, 0x81, 0xC0, + 0x3C, 0x1E, 0x01, 0xE0, 0xF0, 0x0F, 0x07, 0x00, 0xF0, 0x38, 0x0F, 0x83, + 0xFF, 0xF8, 0x1F, 0xFF, 0x80, 0xFF, 0xF8, 0x07, 0xFF, 0x00, 0x38, 0x00, + 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x03, 0xFF, 0x80, 0x3F, 0xFC, 0x01, 0xFF, + 0xE0, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x1F, 0xF8, 0x07, 0xFF, + 0xC0, 0xFF, 0xFE, 0x1F, 0x87, 0xE3, 0xE0, 0x1F, 0x3C, 0x01, 0xF7, 0xC0, + 0x0F, 0x78, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, + 0x01, 0xEF, 0x00, 0x3E, 0xF8, 0x03, 0xCF, 0x80, 0x7C, 0x7C, 0x1F, 0x87, + 0xFF, 0xF0, 0x3F, 0xFE, 0x01, 0xFF, 0x80, 0x07, 0xE0, 0x01, 0xFE, 0x30, + 0x3F, 0xFF, 0x87, 0xFF, 0xF0, 0x7F, 0xFF, 0x07, 0x83, 0xC0, 0x07, 0xFF, + 0x80, 0x3F, 0xFF, 0x80, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x03, 0xC0, 0xF8, + 0x0E, 0x01, 0xE0, 0x38, 0x07, 0x81, 0xE0, 0x3E, 0x07, 0x83, 0xF0, 0x1F, + 0xFF, 0x80, 0x7F, 0xFC, 0x01, 0xFF, 0xC0, 0x0F, 0xFF, 0x80, 0x3C, 0x3E, + 0x00, 0xE0, 0x7C, 0x03, 0x80, 0xF0, 0x1E, 0x03, 0xE1, 0xFF, 0x07, 0xFF, + 0xFC, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, 0x80, 0xF8, 0x00, 0x7C, 0xE0, 0x7F, + 0xFC, 0x1F, 0xFF, 0x87, 0xFF, 0xE0, 0xF8, 0x7C, 0x3C, 0x07, 0x87, 0x80, + 0xE0, 0xF0, 0x00, 0x1F, 0x00, 0x03, 0xFE, 0x00, 0x3F, 0xF8, 0x03, 0xFF, + 0x80, 0x07, 0xF8, 0x40, 0x1F, 0x3C, 0x01, 0xE7, 0x80, 0x3C, 0xFC, 0x1F, + 0x1F, 0xFF, 0xE3, 0xFF, 0xF8, 0x7F, 0xFE, 0x00, 0x7E, 0x00, 0x7F, 0xFF, + 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0x0E, 0x1F, 0xE1, 0xC3, + 0xBC, 0x78, 0x77, 0x0F, 0x1E, 0xE1, 0xC1, 0x80, 0x38, 0x00, 0x0F, 0x00, + 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x3C, 0x00, + 0x07, 0x80, 0x0F, 0xFE, 0x03, 0xFF, 0xE0, 0x7F, 0xFC, 0x0F, 0xFF, 0x00, + 0x7F, 0x8F, 0xF3, 0xFE, 0x7F, 0xDF, 0xF7, 0xFC, 0xFF, 0x1F, 0xE3, 0xC0, + 0x3C, 0x1C, 0x01, 0xE0, 0xE0, 0x0F, 0x0F, 0x00, 0x70, 0x78, 0x03, 0x83, + 0xC0, 0x3C, 0x1C, 0x01, 0xE0, 0xE0, 0x0E, 0x0F, 0x00, 0x70, 0x78, 0x03, + 0x83, 0xC0, 0x3C, 0x1F, 0x01, 0xC0, 0xFC, 0x3E, 0x03, 0xFF, 0xE0, 0x1F, + 0xFE, 0x00, 0x7F, 0xE0, 0x00, 0xFC, 0x00, 0x00, 0x7F, 0x81, 0xFE, 0xFF, + 0x87, 0xFF, 0xFF, 0x0F, 0xFB, 0xFC, 0x1F, 0xE1, 0xC0, 0x0F, 0x03, 0xC0, + 0x1C, 0x07, 0x80, 0x78, 0x0F, 0x01, 0xE0, 0x1E, 0x03, 0x80, 0x1E, 0x0F, + 0x00, 0x3C, 0x3C, 0x00, 0x78, 0x70, 0x00, 0xF1, 0xE0, 0x01, 0xE7, 0x80, + 0x01, 0xEF, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, + 0x0F, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x7F, 0x87, 0xFF, + 0xFF, 0x1F, 0xFF, 0xF8, 0x7F, 0xFF, 0xE1, 0xFE, 0x78, 0x00, 0xF1, 0xE3, + 0xC3, 0x87, 0x8F, 0x0E, 0x1E, 0x7C, 0x78, 0x79, 0xF9, 0xC1, 0xEF, 0xEF, + 0x07, 0xBF, 0xBC, 0x1D, 0xFE, 0xE0, 0x77, 0x7F, 0x81, 0xFD, 0xFE, 0x07, + 0xE3, 0xF0, 0x3F, 0x8F, 0xC0, 0xFC, 0x3F, 0x03, 0xF0, 0xF8, 0x0F, 0x83, + 0xE0, 0x3E, 0x0F, 0x80, 0xF0, 0x3C, 0x00, 0x07, 0xE0, 0x7E, 0x0F, 0xF0, + 0xFF, 0x0F, 0xF0, 0xFE, 0x0F, 0xE0, 0xFE, 0x03, 0xC0, 0xF8, 0x01, 0xE1, + 0xE0, 0x01, 0xF3, 0xC0, 0x00, 0xF7, 0x80, 0x00, 0x7F, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFF, 0x00, 0x03, 0xEF, + 0x00, 0x07, 0xCF, 0x80, 0x0F, 0x87, 0xC0, 0x1F, 0x03, 0xC0, 0x7F, 0x07, + 0xF0, 0xFF, 0x8F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xE0, 0x7E, 0x0F, + 0xEF, 0xF0, 0xFF, 0xFF, 0x0F, 0xEF, 0xE0, 0xFE, 0x3C, 0x0F, 0x01, 0xE1, + 0xE0, 0x1E, 0x3E, 0x00, 0xF7, 0xC0, 0x0F, 0xF8, 0x00, 0x7F, 0x00, 0x07, + 0xE0, 0x00, 0x3C, 0x00, 0x03, 0x80, 0x00, 0x78, 0x00, 0x07, 0x80, 0x00, + 0x78, 0x00, 0x07, 0x00, 0x07, 0xFF, 0x00, 0xFF, 0xF8, 0x0F, 0xFF, 0x00, + 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xE0, 0xFF, 0xFC, 0x3F, 0xFF, 0x87, 0xFF, + 0xF0, 0xF0, 0x7C, 0x1C, 0x1F, 0x03, 0x87, 0xC0, 0x61, 0xF0, 0x00, 0x7C, + 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x07, + 0x07, 0xC0, 0xE1, 0xF0, 0x3C, 0x7C, 0x07, 0x9F, 0xFF, 0xF3, 0xFF, 0xFC, + 0x7F, 0xFF, 0x8F, 0xFF, 0xF0, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xE0, + 0x70, 0x07, 0x80, 0x3C, 0x01, 0xC0, 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x3C, + 0x01, 0xC0, 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x38, 0x01, 0xC0, 0x0E, 0x00, + 0xF0, 0x07, 0x80, 0x38, 0x01, 0xC0, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, + 0x80, 0xE0, 0x38, 0x0F, 0x03, 0xC0, 0xF0, 0x1C, 0x07, 0x81, 0xE0, 0x78, + 0x0E, 0x03, 0xC0, 0xF0, 0x3C, 0x07, 0x01, 0xE0, 0x78, 0x1E, 0x03, 0x80, + 0xF0, 0x3C, 0x0F, 0x01, 0xE0, 0x78, 0x1E, 0x03, 0x80, 0xF0, 0x3C, 0x06, + 0x07, 0xF8, 0x3F, 0xC1, 0xFC, 0x0F, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, + 0x1C, 0x00, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0x80, 0x1C, 0x01, 0xE0, 0x0F, + 0x00, 0x78, 0x03, 0x80, 0x1C, 0x01, 0xE0, 0x0F, 0x00, 0x70, 0x03, 0x80, + 0x1C, 0x0F, 0xE0, 0xFF, 0x07, 0xF0, 0x3F, 0x80, 0x00, 0x40, 0x01, 0x80, + 0x07, 0x80, 0x3F, 0x80, 0xFF, 0x03, 0xFF, 0x0F, 0x9F, 0x3E, 0x1E, 0xF8, + 0x3F, 0xE0, 0x3F, 0x00, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0xC3, 0xC7, 0x0E, 0x3C, 0x30, 0x00, 0xFE, 0x00, + 0x7F, 0xF0, 0x1F, 0xFF, 0x03, 0xFF, 0xE0, 0x00, 0x3C, 0x07, 0xFF, 0x83, + 0xFF, 0xF0, 0xFF, 0xFC, 0x3F, 0xFF, 0x8F, 0x80, 0xF3, 0xE0, 0x1E, 0x78, + 0x1F, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xF8, 0xFE, 0x7E, 0x07, + 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x01, 0xC0, + 0x00, 0x0F, 0x00, 0x00, 0x3C, 0xFC, 0x00, 0xEF, 0xFC, 0x03, 0xFF, 0xF8, + 0x1F, 0xFF, 0xE0, 0x7E, 0x0F, 0xC1, 0xE0, 0x1F, 0x07, 0x00, 0x3C, 0x1C, + 0x00, 0xF0, 0xE0, 0x03, 0xC3, 0x80, 0x1E, 0x0F, 0x00, 0xF8, 0x3E, 0x07, + 0xC7, 0xFF, 0xFF, 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0x81, 0xF1, 0xF8, 0x00, + 0x00, 0xFE, 0x60, 0xFF, 0xFC, 0x3F, 0xFF, 0x8F, 0xFF, 0xF3, 0xF0, 0x3C, + 0xF8, 0x03, 0x9E, 0x00, 0x67, 0x80, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, + 0xC0, 0x00, 0x7E, 0x01, 0xC7, 0xFF, 0xF8, 0xFF, 0xFE, 0x0F, 0xFF, 0x80, + 0x7F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, + 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x70, 0x07, 0xE3, 0x80, 0xFF, 0xDC, 0x0F, + 0xFF, 0xE0, 0xFF, 0xFF, 0x0F, 0xC1, 0xF0, 0xF8, 0x07, 0x87, 0x80, 0x1C, + 0x78, 0x00, 0xE3, 0xC0, 0x0F, 0x1E, 0x00, 0x70, 0xF0, 0x07, 0x87, 0xE0, + 0xFC, 0x1F, 0xFF, 0xF8, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x07, 0xE3, 0xE0, + 0x00, 0xFC, 0x01, 0xFF, 0xC0, 0xFF, 0xF8, 0x7F, 0xFE, 0x3E, 0x0F, 0xCE, + 0x00, 0xF7, 0x00, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0, 0x1E, 0xFF, 0xFF, 0x9F, 0xFF, 0xE3, 0xFF, 0xF0, 0x3F, 0xF0, + 0x00, 0x0F, 0xF0, 0x01, 0xFF, 0xC0, 0x1F, 0xFE, 0x01, 0xFF, 0xE0, 0x0F, + 0x00, 0x00, 0xF0, 0x00, 0x3F, 0xFF, 0x03, 0xFF, 0xF8, 0x1F, 0xFF, 0xC0, + 0xFF, 0xFC, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x38, 0x00, 0x01, 0xC0, + 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x00, 0x00, 0x38, 0x00, 0x1F, + 0xFF, 0x81, 0xFF, 0xFC, 0x0F, 0xFF, 0xE0, 0x7F, 0xFE, 0x00, 0x01, 0xF9, + 0xF8, 0x3F, 0xFF, 0xC3, 0xFF, 0xFE, 0x7F, 0xFF, 0xE3, 0xE0, 0xFC, 0x3E, + 0x03, 0xE1, 0xE0, 0x0E, 0x1E, 0x00, 0x70, 0xF0, 0x03, 0x87, 0x80, 0x3C, + 0x3E, 0x03, 0xE1, 0xF8, 0x7E, 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0x80, 0xFF, + 0xFC, 0x01, 0xF9, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x80, + 0x7F, 0xF8, 0x07, 0xFF, 0x80, 0x3F, 0xF8, 0x00, 0xFF, 0x00, 0x00, 0x0F, + 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x03, 0xC0, 0x00, + 0x38, 0x00, 0x03, 0x9F, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xC0, 0x7F, 0xFE, + 0x07, 0xC3, 0xE0, 0x70, 0x1E, 0x0F, 0x01, 0xC0, 0xF0, 0x1C, 0x0E, 0x03, + 0xC0, 0xE0, 0x3C, 0x1E, 0x03, 0x81, 0xE0, 0x38, 0x7F, 0x0F, 0xFF, 0xF8, + 0xFF, 0xFF, 0x8F, 0xF7, 0xF0, 0xFE, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0, + 0x0F, 0xF0, 0x00, 0xF0, 0x00, 0xE0, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xE0, + 0x01, 0xE0, 0x01, 0xC0, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0xF8, 0x00, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFC, 0x3F, 0xFE, 0x0F, 0xFF, 0x81, 0xFF, + 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x01, 0xC0, 0x00, 0xF0, + 0x00, 0x3C, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xE0, 0x00, 0x78, 0x00, + 0x1E, 0x00, 0x07, 0x00, 0x01, 0xC0, 0x00, 0xF0, 0x00, 0x7C, 0x1F, 0xFE, + 0x0F, 0xFF, 0x03, 0xFF, 0x80, 0x7F, 0x80, 0x00, 0x07, 0xE0, 0x00, 0xFE, + 0x00, 0x0F, 0xE0, 0x00, 0x7C, 0x00, 0x01, 0xC0, 0x00, 0x3C, 0x00, 0x03, + 0xCF, 0xF0, 0x3C, 0xFF, 0x03, 0x9F, 0xF0, 0x38, 0xFE, 0x07, 0xBF, 0x00, + 0x7F, 0xC0, 0x07, 0xF8, 0x00, 0x7F, 0x00, 0x07, 0xF8, 0x00, 0xFF, 0xC0, + 0x0F, 0x7E, 0x00, 0xE3, 0xF0, 0x7E, 0x1F, 0xE7, 0xE1, 0xFE, 0xFE, 0x3F, + 0xE7, 0xE1, 0xFC, 0x03, 0xFC, 0x07, 0xFC, 0x07, 0xF8, 0x07, 0xF8, 0x00, + 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x70, 0x00, 0x70, 0x00, 0xF0, 0x00, + 0xF0, 0x00, 0xE0, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, + 0xC0, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, + 0x7C, 0x78, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF0, 0xF1, + 0xF1, 0xE1, 0xC3, 0x83, 0xC7, 0x87, 0x07, 0x8F, 0x0E, 0x0E, 0x1C, 0x3C, + 0x1C, 0x38, 0x78, 0x78, 0x70, 0xE0, 0xF1, 0xE1, 0xC1, 0xC7, 0xE3, 0xC3, + 0xFF, 0xCF, 0xC7, 0xFF, 0x9F, 0x9F, 0xFF, 0x3E, 0x3E, 0x0F, 0x8F, 0x80, + 0xFD, 0xFF, 0x07, 0xFF, 0xF8, 0x3F, 0xFF, 0xE0, 0x7E, 0x1F, 0x07, 0xC0, + 0x78, 0x3C, 0x03, 0x81, 0xE0, 0x1C, 0x0E, 0x01, 0xE0, 0x70, 0x0F, 0x07, + 0x80, 0x70, 0x3C, 0x03, 0x87, 0xF0, 0x3F, 0x7F, 0xC3, 0xFF, 0xFE, 0x1F, + 0xEF, 0xE0, 0xFE, 0x01, 0xFC, 0x01, 0xFF, 0x80, 0xFF, 0xF8, 0x7F, 0xFE, + 0x3E, 0x0F, 0xDF, 0x01, 0xF7, 0x80, 0x3F, 0xC0, 0x0F, 0xF0, 0x03, 0xFC, + 0x01, 0xEF, 0x80, 0xFB, 0xF0, 0x7C, 0x7F, 0xFF, 0x1F, 0xFF, 0x03, 0xFF, + 0x80, 0x3F, 0x80, 0x07, 0xC7, 0xE0, 0x1F, 0xBF, 0xF0, 0x3F, 0xFF, 0xF0, + 0x7F, 0xFF, 0xE0, 0x3F, 0x07, 0xE0, 0x78, 0x03, 0xC0, 0xE0, 0x07, 0x81, + 0xC0, 0x0F, 0x07, 0x00, 0x1E, 0x0F, 0x00, 0x78, 0x1E, 0x01, 0xF0, 0x3E, + 0x07, 0xC0, 0xFF, 0xFF, 0x81, 0xFF, 0xFE, 0x03, 0xDF, 0xF0, 0x07, 0x1F, + 0x80, 0x0E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x78, 0x00, 0x03, 0xFE, 0x00, + 0x0F, 0xFE, 0x00, 0x1F, 0xF8, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x01, 0xF8, + 0xF8, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xCF, 0xFF, 0xFE, 0x3E, 0x07, 0xC1, + 0xF0, 0x0F, 0x07, 0x80, 0x1C, 0x3C, 0x00, 0x70, 0xF0, 0x03, 0x83, 0xC0, + 0x0E, 0x0F, 0x80, 0x78, 0x3F, 0x07, 0xE0, 0x7F, 0xFF, 0x81, 0xFF, 0xFC, + 0x03, 0xFF, 0x70, 0x03, 0xF3, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0xE0, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0xC0, 0x07, 0xFF, 0x00, 0x1F, + 0xF8, 0x00, 0x0F, 0xC3, 0xC1, 0xFC, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFE, + 0x03, 0xFC, 0x00, 0x3F, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x07, 0x80, + 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0xF0, 0x00, 0xFF, 0xFC, 0x0F, 0xFF, + 0xE0, 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, 0x03, 0xF3, 0x0F, 0xFF, 0x3F, 0xFF, + 0x3F, 0xFF, 0x7C, 0x0E, 0x78, 0x00, 0x7F, 0xE0, 0x3F, 0xFC, 0x1F, 0xFF, + 0x00, 0x3F, 0x70, 0x0F, 0xF8, 0x1F, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF8, + 0x0F, 0xE0, 0x06, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x0E, 0x00, + 0x7F, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFC, 0x1C, 0x00, 0x3C, 0x00, + 0x3C, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x78, 0x00, 0x7C, 0x0E, + 0x7F, 0xFF, 0x7F, 0xFE, 0x3F, 0xFC, 0x0F, 0xE0, 0x7C, 0x0F, 0xFF, 0x07, + 0xFF, 0x81, 0xFF, 0xE0, 0x7E, 0x78, 0x03, 0x9E, 0x00, 0xE7, 0x80, 0x79, + 0xE0, 0x1E, 0x78, 0x07, 0x1E, 0x01, 0xC7, 0x80, 0xF1, 0xE0, 0xFC, 0x7F, + 0xFF, 0x9F, 0xFF, 0xE3, 0xFF, 0xF8, 0x3E, 0x7C, 0x7F, 0x87, 0xFF, 0xFC, + 0x7F, 0xFF, 0xE3, 0xFF, 0xFF, 0x1F, 0xE1, 0xE0, 0x3C, 0x0F, 0x03, 0xC0, + 0x78, 0x3C, 0x01, 0xE1, 0xC0, 0x0F, 0x1E, 0x00, 0x79, 0xE0, 0x03, 0xCE, + 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0x80, 0x00, + 0x78, 0x00, 0x7E, 0x03, 0xF7, 0xF0, 0x3F, 0xFF, 0x81, 0xFD, 0xF8, 0x0F, + 0xE7, 0x8E, 0x1C, 0x3C, 0xF9, 0xE1, 0xE7, 0xCE, 0x0F, 0x7E, 0xF0, 0x7B, + 0xF7, 0x03, 0xFF, 0xF8, 0x1F, 0xDF, 0x80, 0xFC, 0xFC, 0x07, 0xE7, 0xE0, + 0x3E, 0x3E, 0x01, 0xF1, 0xF0, 0x0F, 0x07, 0x00, 0x0F, 0xE3, 0xF8, 0xFF, + 0x1F, 0xC7, 0xF9, 0xFE, 0x1F, 0x87, 0xF0, 0x7E, 0x7C, 0x01, 0xFF, 0xC0, + 0x07, 0xFC, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x1F, 0xF0, 0x01, 0xF7, + 0xC0, 0x1F, 0x1F, 0x03, 0xF0, 0x7C, 0x7F, 0xCF, 0xFB, 0xFE, 0x7F, 0xDF, + 0xE3, 0xFC, 0x07, 0xF0, 0x7F, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x07, + 0xE0, 0xFE, 0x03, 0xC0, 0x78, 0x03, 0xC0, 0x78, 0x03, 0xC0, 0xF0, 0x01, + 0xE1, 0xE0, 0x01, 0xE1, 0xC0, 0x01, 0xE3, 0xC0, 0x00, 0xF7, 0x80, 0x00, + 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x7F, + 0xF0, 0x00, 0xFF, 0xF8, 0x00, 0xFF, 0xF0, 0x00, 0x7F, 0xF0, 0x00, 0x1F, + 0xFF, 0xC7, 0xFF, 0xF1, 0xFF, 0xF8, 0xFF, 0xFE, 0x3C, 0x1F, 0x0E, 0x1F, + 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, + 0xF8, 0x3C, 0xFF, 0xFF, 0x3F, 0xFF, 0xCF, 0xFF, 0xE3, 0xFF, 0xF8, 0x00, + 0xF0, 0x1F, 0x03, 0xF0, 0x7E, 0x07, 0x80, 0x70, 0x0F, 0x00, 0xF0, 0x0E, + 0x00, 0xE0, 0x1E, 0x01, 0xC0, 0xFC, 0x0F, 0x80, 0xF8, 0x0F, 0xC0, 0x3C, + 0x03, 0xC0, 0x38, 0x03, 0x80, 0x78, 0x07, 0x80, 0x78, 0x07, 0xE0, 0x7E, + 0x03, 0xE0, 0x1C, 0x00, 0x02, 0x07, 0x07, 0x0F, 0x0F, 0x0E, 0x0E, 0x0E, + 0x1E, 0x1E, 0x1C, 0x1C, 0x1C, 0x3C, 0x3C, 0x38, 0x38, 0x38, 0x78, 0x78, + 0x70, 0x70, 0x70, 0xF0, 0xF0, 0xE0, 0xE0, 0x01, 0xC0, 0x1F, 0x00, 0xFC, + 0x07, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1C, 0x00, 0xE0, 0x0F, 0x00, + 0x78, 0x03, 0xC0, 0x1F, 0x80, 0x7C, 0x03, 0xE0, 0x3F, 0x03, 0xC0, 0x1C, + 0x00, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0x80, 0x3C, 0x0F, 0xE0, 0x7E, 0x07, + 0xE0, 0x1E, 0x00, 0x0F, 0x00, 0x1F, 0xC0, 0x1F, 0xF0, 0xFF, 0xFC, 0xFF, + 0x3F, 0xFF, 0x0F, 0xF8, 0x03, 0xF8, 0x00, 0xF0}; + +const GFXglyph FreeMonoBoldOblique18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 21, 0, 1}, // 0x20 ' ' + {0, 9, 22, 21, 9, -21}, // 0x21 '!' + {25, 12, 10, 21, 9, -20}, // 0x22 '"' + {40, 18, 25, 21, 4, -22}, // 0x23 '#' + {97, 18, 28, 21, 4, -23}, // 0x24 '$' + {160, 16, 21, 21, 5, -20}, // 0x25 '%' + {202, 16, 20, 21, 4, -19}, // 0x26 '&' + {242, 5, 10, 21, 12, -20}, // 0x27 ''' + {249, 10, 27, 21, 11, -21}, // 0x28 '(' + {283, 10, 27, 21, 4, -21}, // 0x29 ')' + {317, 15, 15, 21, 6, -21}, // 0x2A '*' + {346, 18, 19, 21, 4, -18}, // 0x2B '+' + {389, 9, 10, 21, 4, -3}, // 0x2C ',' + {401, 18, 4, 21, 4, -11}, // 0x2D '-' + {410, 5, 5, 21, 8, -4}, // 0x2E '.' + {414, 21, 28, 21, 2, -23}, // 0x2F '/' + {488, 17, 23, 21, 5, -22}, // 0x30 '0' + {537, 15, 22, 21, 3, -21}, // 0x31 '1' + {579, 20, 23, 21, 2, -22}, // 0x32 '2' + {637, 18, 23, 21, 3, -22}, // 0x33 '3' + {689, 16, 21, 21, 4, -20}, // 0x34 '4' + {731, 18, 22, 21, 4, -21}, // 0x35 '5' + {781, 19, 23, 21, 5, -22}, // 0x36 '6' + {836, 16, 22, 21, 6, -21}, // 0x37 '7' + {880, 19, 23, 21, 3, -22}, // 0x38 '8' + {935, 18, 23, 21, 4, -22}, // 0x39 '9' + {987, 7, 16, 21, 9, -15}, // 0x3A ':' + {1001, 11, 22, 21, 4, -15}, // 0x3B ';' + {1032, 18, 16, 21, 4, -17}, // 0x3C '<' + {1068, 19, 10, 21, 3, -14}, // 0x3D '=' + {1092, 19, 16, 21, 3, -17}, // 0x3E '>' + {1130, 14, 21, 21, 8, -20}, // 0x3F '?' + {1167, 18, 27, 21, 3, -21}, // 0x40 '@' + {1228, 22, 21, 21, 0, -20}, // 0x41 'A' + {1286, 21, 21, 21, 1, -20}, // 0x42 'B' + {1342, 21, 21, 21, 2, -20}, // 0x43 'C' + {1398, 21, 21, 21, 1, -20}, // 0x44 'D' + {1454, 22, 21, 21, 0, -20}, // 0x45 'E' + {1512, 23, 21, 21, 0, -20}, // 0x46 'F' + {1573, 21, 21, 21, 2, -20}, // 0x47 'G' + {1629, 23, 21, 21, 0, -20}, // 0x48 'H' + {1690, 19, 21, 21, 2, -20}, // 0x49 'I' + {1740, 23, 21, 21, 0, -20}, // 0x4A 'J' + {1801, 23, 21, 21, 0, -20}, // 0x4B 'K' + {1862, 20, 21, 21, 1, -20}, // 0x4C 'L' + {1915, 25, 21, 21, 0, -20}, // 0x4D 'M' + {1981, 24, 21, 21, 1, -20}, // 0x4E 'N' + {2044, 20, 21, 21, 2, -20}, // 0x4F 'O' + {2097, 21, 21, 21, 1, -20}, // 0x50 'P' + {2153, 20, 26, 21, 2, -20}, // 0x51 'Q' + {2218, 22, 21, 21, 0, -20}, // 0x52 'R' + {2276, 19, 21, 21, 3, -20}, // 0x53 'S' + {2326, 19, 21, 21, 3, -20}, // 0x54 'T' + {2376, 21, 21, 21, 3, -20}, // 0x55 'U' + {2432, 23, 21, 21, 1, -20}, // 0x56 'V' + {2493, 22, 21, 21, 2, -20}, // 0x57 'W' + {2551, 24, 21, 21, 0, -20}, // 0x58 'X' + {2614, 20, 21, 21, 3, -20}, // 0x59 'Y' + {2667, 19, 21, 21, 2, -20}, // 0x5A 'Z' + {2717, 13, 27, 21, 8, -21}, // 0x5B '[' + {2761, 10, 28, 21, 8, -23}, // 0x5C '\' + {2796, 13, 27, 21, 4, -21}, // 0x5D ']' + {2840, 15, 11, 21, 6, -21}, // 0x5E '^' + {2861, 21, 4, 21, -1, 4}, // 0x5F '_' + {2872, 6, 6, 21, 10, -22}, // 0x60 '`' + {2877, 19, 16, 21, 2, -15}, // 0x61 'a' + {2915, 22, 22, 21, 0, -21}, // 0x62 'b' + {2976, 19, 16, 21, 3, -15}, // 0x63 'c' + {3014, 21, 22, 21, 3, -21}, // 0x64 'd' + {3072, 18, 16, 21, 3, -15}, // 0x65 'e' + {3108, 21, 22, 21, 3, -21}, // 0x66 'f' + {3166, 21, 23, 21, 2, -15}, // 0x67 'g' + {3227, 20, 22, 21, 1, -21}, // 0x68 'h' + {3282, 16, 22, 21, 3, -21}, // 0x69 'i' + {3326, 18, 29, 21, 2, -21}, // 0x6A 'j' + {3392, 20, 22, 21, 1, -21}, // 0x6B 'k' + {3447, 16, 22, 21, 3, -21}, // 0x6C 'l' + {3491, 23, 16, 21, 0, -15}, // 0x6D 'm' + {3537, 21, 16, 21, 1, -15}, // 0x6E 'n' + {3579, 18, 16, 21, 3, -15}, // 0x6F 'o' + {3615, 23, 23, 21, -1, -15}, // 0x70 'p' + {3682, 22, 23, 21, 2, -15}, // 0x71 'q' + {3746, 20, 16, 21, 2, -15}, // 0x72 'r' + {3786, 16, 16, 21, 4, -15}, // 0x73 's' + {3818, 16, 21, 21, 4, -20}, // 0x74 't' + {3860, 18, 16, 21, 3, -15}, // 0x75 'u' + {3896, 21, 16, 21, 2, -15}, // 0x76 'v' + {3938, 21, 16, 21, 3, -15}, // 0x77 'w' + {3980, 21, 16, 21, 1, -15}, // 0x78 'x' + {4022, 24, 23, 21, -1, -15}, // 0x79 'y' + {4091, 18, 16, 21, 3, -15}, // 0x7A 'z' + {4127, 12, 27, 21, 8, -21}, // 0x7B '{' + {4168, 8, 27, 21, 8, -21}, // 0x7C '|' + {4195, 13, 27, 21, 4, -21}, // 0x7D '}' + {4239, 17, 8, 21, 4, -13}}; // 0x7E '~' + +const GFXfont FreeMonoBoldOblique18pt7b PROGMEM = { + (uint8_t *)FreeMonoBoldOblique18pt7bBitmaps, + (GFXglyph *)FreeMonoBoldOblique18pt7bGlyphs, 0x20, 0x7E, 35}; + +// Approx. 4928 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique24pt7b.h new file mode 100644 index 0000000..b0c3370 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique24pt7b.h @@ -0,0 +1,741 @@ +const uint8_t FreeMonoBoldOblique24pt7bBitmaps[] PROGMEM = { + 0x01, 0xE0, 0x3F, 0x07, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xE0, 0xFE, + 0x0F, 0xE0, 0xFE, 0x0F, 0xC0, 0xFC, 0x1F, 0xC1, 0xF8, 0x1F, 0x81, 0xF8, + 0x1F, 0x81, 0xF0, 0x1F, 0x01, 0xF0, 0x1E, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xC0, 0x7E, 0x0F, 0xE0, 0xFE, 0x0F, 0xC0, 0x78, 0x00, 0x7E, + 0x1F, 0xBF, 0x0F, 0xDF, 0x87, 0xCF, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF1, + 0xE0, 0xF8, 0xF0, 0x7C, 0x78, 0x3C, 0x38, 0x1E, 0x1C, 0x0F, 0x0E, 0x07, + 0x0E, 0x03, 0x83, 0x01, 0x80, 0x00, 0x1C, 0x1C, 0x00, 0x3E, 0x3E, 0x00, + 0x3E, 0x3E, 0x00, 0x3C, 0x3C, 0x00, 0x7C, 0x7C, 0x00, 0x7C, 0x7C, 0x00, + 0x7C, 0x7C, 0x00, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x0F, + 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, + 0xFF, 0xFE, 0x03, 0xE3, 0xE0, 0x03, 0xE3, 0xE0, 0x03, 0xC3, 0xC0, 0x07, + 0xC7, 0xC0, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFC, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0x0F, 0x0F, 0x00, 0x1F, 0x1F, 0x00, 0x1F, + 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x3E, 0x1E, 0x00, 0x3E, 0x3E, 0x00, 0x3E, + 0x3E, 0x00, 0x3C, 0x3C, 0x00, 0x7C, 0x7C, 0x00, 0x38, 0x38, 0x00, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0xFF, 0x00, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, + 0x01, 0xFF, 0xFF, 0x81, 0xFC, 0x1F, 0xC1, 0xF8, 0x03, 0xC0, 0xF8, 0x01, + 0xE0, 0x7C, 0x00, 0x40, 0x3F, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x0F, 0xFF, + 0x80, 0x03, 0xFF, 0xF8, 0x00, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0x00, 0x00, + 0x7F, 0xC0, 0x00, 0x07, 0xE0, 0xE0, 0x01, 0xF0, 0xF0, 0x00, 0xF8, 0xF8, + 0x00, 0xFC, 0x7E, 0x00, 0xFC, 0x3F, 0x81, 0xFE, 0x1F, 0xFF, 0xFE, 0x0F, + 0xFF, 0xFE, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFC, 0x00, 0x07, 0xF0, 0x00, + 0x01, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xE0, 0x03, 0xC3, 0xC0, 0x0E, 0x07, 0x00, + 0x70, 0x1C, 0x01, 0xC0, 0x70, 0x07, 0x01, 0xC0, 0x1C, 0x0E, 0x00, 0x78, + 0x78, 0x00, 0xFF, 0xC0, 0x03, 0xFE, 0x1F, 0x03, 0xE3, 0xFC, 0x00, 0x7F, + 0xC0, 0x0F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xC0, 0x03, 0xF8, 0x7C, 0x0F, + 0x07, 0xFC, 0x00, 0x3F, 0xF0, 0x01, 0xE1, 0xE0, 0x07, 0x03, 0x80, 0x38, + 0x0E, 0x00, 0xE0, 0x38, 0x03, 0x80, 0xE0, 0x0E, 0x07, 0x00, 0x3C, 0x3C, + 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x00, + 0x01, 0xFF, 0x80, 0x3F, 0xFC, 0x03, 0xFF, 0xE0, 0x1F, 0xFE, 0x01, 0xF1, + 0xE0, 0x1F, 0x04, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, + 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x07, 0xF8, 0x00, 0x7F, 0xE3, + 0xE7, 0xFF, 0x3F, 0x7E, 0xFF, 0xFB, 0xE7, 0xFF, 0x9E, 0x1F, 0xF1, 0xF0, + 0xFF, 0x8F, 0x83, 0xF8, 0x7C, 0x1F, 0xC3, 0xF0, 0xFF, 0x9F, 0xFF, 0xFC, + 0x7F, 0xFF, 0xE3, 0xFF, 0xFF, 0x0F, 0xFD, 0xF0, 0x1F, 0x80, 0x00, 0x7E, + 0xFD, 0xF3, 0xE7, 0xCF, 0x3E, 0x7C, 0xF1, 0xE3, 0xC7, 0x0E, 0x18, 0x00, + 0x00, 0x18, 0x00, 0xF0, 0x07, 0xC0, 0x3F, 0x01, 0xF8, 0x07, 0xC0, 0x3E, + 0x01, 0xF8, 0x07, 0xC0, 0x3E, 0x00, 0xF8, 0x07, 0xC0, 0x1F, 0x00, 0xF8, + 0x03, 0xE0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x3E, 0x00, 0xF8, + 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, + 0x00, 0xFC, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x80, 0x7E, 0x00, 0xFC, 0x03, + 0xF0, 0x07, 0xC0, 0x1E, 0x00, 0x00, 0xC0, 0x07, 0x80, 0x3F, 0x00, 0xFC, + 0x03, 0xF0, 0x07, 0xE0, 0x1F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xF0, 0x07, + 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, + 0xF0, 0x07, 0xC0, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x1F, 0x00, 0x7C, 0x01, + 0xF0, 0x0F, 0x80, 0x3E, 0x01, 0xF0, 0x0F, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, + 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF0, 0x00, 0x00, 0x3C, + 0x00, 0x01, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x08, + 0x3C, 0x09, 0xF9, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, + 0x87, 0xFF, 0xE0, 0x07, 0xF8, 0x00, 0x7F, 0xC0, 0x07, 0xFF, 0x00, 0x7F, + 0xF8, 0x07, 0xE7, 0xE0, 0x3E, 0x3F, 0x01, 0xE0, 0xF8, 0x0E, 0x07, 0x80, + 0x00, 0x07, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x7C, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xF0, 0x00, + 0x01, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x03, 0xF0, 0x7E, 0x07, 0xC0, 0xFC, + 0x0F, 0x81, 0xF0, 0x1E, 0x03, 0xE0, 0x3C, 0x07, 0x80, 0x78, 0x0F, 0x00, + 0xE0, 0x0C, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3C, 0xFF, 0xFF, 0xFF, 0xCF, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, + 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x07, 0xE0, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x01, 0xF0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x1F, 0xFF, + 0xE0, 0x7F, 0xFF, 0xC0, 0xFC, 0x1F, 0x83, 0xF0, 0x1F, 0x8F, 0xC0, 0x1F, + 0x1F, 0x00, 0x3E, 0x7C, 0x00, 0x7C, 0xF8, 0x00, 0xF9, 0xF0, 0x01, 0xF3, + 0xC0, 0x07, 0xCF, 0x80, 0x0F, 0x9F, 0x00, 0x1E, 0x3E, 0x00, 0x3C, 0x78, + 0x00, 0xF8, 0xF0, 0x01, 0xF3, 0xE0, 0x03, 0xE7, 0xC0, 0x07, 0x8F, 0x80, + 0x1F, 0x1F, 0x00, 0x3E, 0x3E, 0x00, 0xF8, 0x7C, 0x01, 0xF0, 0xFC, 0x07, + 0xC1, 0xFC, 0x3F, 0x81, 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x03, 0xFF, 0xE0, + 0x03, 0xFF, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x7E, + 0x00, 0x0F, 0xF0, 0x01, 0xFF, 0x80, 0x1F, 0xFC, 0x03, 0xFB, 0xE0, 0x1F, + 0x9E, 0x00, 0xF1, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, + 0x00, 0x1E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x03, + 0xC0, 0x00, 0x1E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, + 0x03, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, + 0x01, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, + 0xFF, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x03, 0xFF, 0x80, 0x03, 0xFF, 0xF0, + 0x01, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0x07, 0xF0, 0x1F, 0x00, + 0xFC, 0x0F, 0x80, 0x1F, 0x03, 0xE0, 0x07, 0xC0, 0xF0, 0x01, 0xF0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xF8, + 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, 0x80, 0x70, 0x3F, 0x80, 0x3E, 0x1F, + 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x0F, 0xFE, 0x00, 0x1F, + 0xFF, 0x80, 0x1F, 0xFF, 0xE0, 0x1F, 0xFF, 0xF8, 0x0F, 0x81, 0xFC, 0x07, + 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x0F, 0xC0, + 0x00, 0x07, 0xC0, 0x00, 0x0F, 0xC0, 0x01, 0xFF, 0xC0, 0x01, 0xFF, 0xC0, + 0x00, 0xFF, 0x80, 0x00, 0x7F, 0xE0, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x1F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFC, 0x3C, 0x01, + 0xFC, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFC, 0x03, 0xFF, + 0xFC, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x07, 0xF0, 0x00, + 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x0F, 0xF8, 0x00, 0x7F, 0xE0, 0x03, 0xFF, + 0x80, 0x1F, 0xBE, 0x00, 0x7C, 0xF0, 0x03, 0xE7, 0xC0, 0x1F, 0x1F, 0x00, + 0xF8, 0x7C, 0x07, 0xE1, 0xE0, 0x3F, 0x07, 0x81, 0xF8, 0x3E, 0x07, 0xC0, + 0xF8, 0x3E, 0x03, 0xC1, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFE, + 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0x80, 0x00, 0xF8, 0x00, 0x3F, 0xF8, 0x01, + 0xFF, 0xE0, 0x07, 0xFF, 0x80, 0x1F, 0xFE, 0x00, 0x7F, 0xF0, 0x01, 0xFF, + 0xFF, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xC0, 0x3F, 0xFF, 0xE0, 0x3F, + 0xFF, 0xE0, 0x1F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, + 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF7, 0xF0, 0x00, 0xFF, 0xFE, 0x00, + 0x7F, 0xFF, 0x80, 0x3F, 0xFF, 0xE0, 0x1F, 0xFF, 0xF0, 0x0F, 0x01, 0xFC, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, + 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0xF8, 0x3C, 0x03, 0xFC, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, + 0xFC, 0x0F, 0xFF, 0xFC, 0x03, 0xFF, 0xF8, 0x00, 0x3F, 0xE0, 0x00, 0x00, + 0x01, 0xFC, 0x00, 0x07, 0xFE, 0x00, 0x1F, 0xFF, 0x00, 0x7F, 0xFF, 0x00, + 0xFF, 0xFE, 0x01, 0xFE, 0x1C, 0x03, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x0F, + 0xC0, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x3E, + 0xFF, 0x80, 0x7D, 0xFF, 0xC0, 0x7F, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x7F, + 0x87, 0xF0, 0xFF, 0x03, 0xF0, 0xFC, 0x01, 0xF0, 0xF8, 0x01, 0xF0, 0xF8, + 0x01, 0xF0, 0xF8, 0x01, 0xF0, 0xF8, 0x03, 0xE0, 0xF8, 0x03, 0xE0, 0xFC, + 0x07, 0xC0, 0xFE, 0x0F, 0xC0, 0x7F, 0xFF, 0x80, 0x7F, 0xFF, 0x00, 0x3F, + 0xFE, 0x00, 0x1F, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x7F, 0xFF, 0xFD, 0xFF, + 0xFF, 0xE7, 0xFF, 0xFF, 0xBF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFB, 0xE0, 0x07, + 0xCF, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x00, + 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x01, + 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, + 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x00, + 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, + 0x00, 0x1F, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0xFF, + 0xE0, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, 0xE0, 0x7F, 0xFF, 0xC0, 0xFC, 0x1F, + 0xC3, 0xF0, 0x1F, 0x8F, 0xC0, 0x1F, 0x1F, 0x00, 0x3E, 0x3E, 0x00, 0x7C, + 0x7C, 0x01, 0xF0, 0xFC, 0x07, 0xE0, 0xFC, 0x1F, 0x81, 0xFF, 0xFE, 0x01, + 0xFF, 0xF0, 0x01, 0xFF, 0xE0, 0x0F, 0xFF, 0xE0, 0x3F, 0xFF, 0xE0, 0xFE, + 0x0F, 0xC3, 0xF0, 0x0F, 0xC7, 0xC0, 0x0F, 0x9F, 0x00, 0x1F, 0x3E, 0x00, + 0x3E, 0x7C, 0x00, 0xFC, 0xFC, 0x03, 0xF1, 0xFC, 0x1F, 0xE3, 0xFF, 0xFF, + 0x83, 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x03, 0xFF, 0xC0, 0x01, 0xFC, 0x00, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0xFF, 0xFC, 0x01, 0xFF, + 0xFE, 0x03, 0xFF, 0xFE, 0x03, 0xF0, 0x7F, 0x07, 0xE0, 0x3F, 0x07, 0xC0, + 0x1F, 0x0F, 0xC0, 0x1F, 0x0F, 0x80, 0x1F, 0x0F, 0x80, 0x1F, 0x0F, 0x80, + 0x3F, 0x0F, 0xC0, 0x7F, 0x0F, 0xE1, 0xFF, 0x07, 0xFF, 0xFE, 0x07, 0xFF, + 0xFE, 0x03, 0xFF, 0xBE, 0x01, 0xFF, 0x7C, 0x00, 0xFC, 0x7C, 0x00, 0x00, + 0xFC, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x03, 0xF0, 0x00, 0x0F, + 0xE0, 0x00, 0x1F, 0xC0, 0x38, 0x7F, 0x80, 0x7F, 0xFF, 0x00, 0xFF, 0xFE, + 0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0x07, 0x83, + 0xF1, 0xFC, 0x7F, 0x1F, 0x83, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x1F, 0x8F, 0xE3, 0xF8, 0xFC, + 0x1E, 0x00, 0x00, 0x3C, 0x00, 0xFC, 0x03, 0xF8, 0x07, 0xF0, 0x0F, 0xC0, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x03, 0xE0, 0x0F, 0xC0, + 0x1F, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x38, + 0x00, 0xF0, 0x01, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x03, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xE0, 0x00, + 0x7F, 0xC0, 0x00, 0xFF, 0x80, 0x03, 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x0F, + 0xFC, 0x00, 0x1F, 0xF0, 0x00, 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0x0F, 0xFC, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x07, 0xFC, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, + 0x80, 0x1F, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFC, 0xFF, + 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, + 0xF3, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x80, 0x00, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0xFF, 0xC0, + 0x00, 0x1F, 0xF0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x7F, + 0xE0, 0x00, 0xFF, 0xC0, 0x01, 0xFF, 0x80, 0x03, 0xFE, 0x00, 0x07, 0xFC, + 0x00, 0x1F, 0xF8, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0x80, + 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x01, 0xFF, + 0xE1, 0xFF, 0xFE, 0x3F, 0xFF, 0xE7, 0xFF, 0xFF, 0xF8, 0x1F, 0xFE, 0x00, + 0xFF, 0x80, 0x1F, 0xF0, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x0F, + 0xE0, 0x07, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xC0, 0x0F, 0xE0, + 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x00, 0x3F, 0x00, 0x0F, 0xE0, 0x01, 0xFC, 0x00, + 0x3F, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x7F, 0xF0, 0x01, + 0xFF, 0xF0, 0x0F, 0xFF, 0xE0, 0x3F, 0x07, 0xE0, 0x7C, 0x07, 0xC1, 0xE0, + 0x07, 0x87, 0xC0, 0x0F, 0x0F, 0x00, 0x1C, 0x3C, 0x00, 0x78, 0x78, 0x07, + 0xF1, 0xE0, 0x3F, 0xE3, 0xC1, 0xFF, 0x87, 0x87, 0xFF, 0x0E, 0x1F, 0x9E, + 0x3C, 0x7C, 0x3C, 0x78, 0xF0, 0x78, 0xF3, 0xC0, 0xE1, 0xC7, 0x83, 0xC3, + 0x8F, 0x07, 0x8F, 0x1E, 0x0F, 0x1E, 0x3E, 0x1C, 0x3C, 0x7F, 0xFC, 0x78, + 0x7F, 0xFC, 0xF0, 0x7F, 0xF1, 0xE0, 0x3F, 0xE3, 0xC0, 0x00, 0x07, 0x80, + 0x00, 0x0F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3F, 0x01, + 0xC0, 0x7F, 0xFF, 0x80, 0x7F, 0xFE, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x0F, 0xFC, 0x00, + 0x00, 0x7F, 0xF0, 0x00, 0x01, 0xE7, 0xC0, 0x00, 0x0F, 0x9F, 0x00, 0x00, + 0x7C, 0x7C, 0x00, 0x01, 0xE1, 0xF8, 0x00, 0x0F, 0x87, 0xE0, 0x00, 0x7C, + 0x0F, 0x80, 0x01, 0xF0, 0x3E, 0x00, 0x0F, 0x80, 0xF8, 0x00, 0x3F, 0xFF, + 0xF0, 0x01, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFC, + 0x01, 0xFF, 0xFF, 0xF8, 0x0F, 0xC0, 0x07, 0xE0, 0x3E, 0x00, 0x0F, 0x87, + 0xFF, 0x03, 0xFF, 0xBF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0xC1, 0xFF, 0xEF, 0xFE, 0x07, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x3F, + 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, + 0xFF, 0x80, 0x7C, 0x00, 0xFC, 0x03, 0xE0, 0x03, 0xE0, 0x1E, 0x00, 0x1F, + 0x01, 0xF0, 0x00, 0xF8, 0x0F, 0x80, 0x0F, 0x80, 0x7C, 0x01, 0xF8, 0x03, + 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFC, 0x03, 0xC0, 0x0F, 0xF0, 0x3E, 0x00, 0x1F, + 0x81, 0xF0, 0x00, 0x7C, 0x0F, 0x80, 0x03, 0xE0, 0x78, 0x00, 0x1F, 0x03, + 0xC0, 0x03, 0xF1, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, + 0xFF, 0x87, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x07, 0xF0, + 0x00, 0x03, 0xFF, 0xE6, 0x00, 0x7F, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0x03, + 0xFF, 0xFF, 0xF0, 0x7F, 0x81, 0xFF, 0x0F, 0xE0, 0x07, 0xE1, 0xF8, 0x00, + 0x3E, 0x1F, 0x00, 0x03, 0xE3, 0xF0, 0x00, 0x3C, 0x3E, 0x00, 0x03, 0xC7, + 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x0F, 0xC0, + 0x00, 0x70, 0x7E, 0x00, 0x1F, 0x07, 0xF8, 0x07, 0xF0, 0x3F, 0xFF, 0xFF, + 0x03, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xF8, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF0, 0x07, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xE0, 0x1F, 0x00, 0xFE, + 0x01, 0xF0, 0x07, 0xE0, 0x1E, 0x00, 0x3F, 0x01, 0xE0, 0x01, 0xF0, 0x3E, + 0x00, 0x1F, 0x03, 0xE0, 0x01, 0xF0, 0x3E, 0x00, 0x1F, 0x03, 0xC0, 0x01, + 0xF0, 0x7C, 0x00, 0x1F, 0x07, 0xC0, 0x03, 0xF0, 0x7C, 0x00, 0x3E, 0x07, + 0x80, 0x03, 0xE0, 0x78, 0x00, 0x7E, 0x0F, 0x80, 0x07, 0xC0, 0xF8, 0x00, + 0xFC, 0x0F, 0x80, 0x1F, 0x80, 0xF0, 0x07, 0xF0, 0x7F, 0xFF, 0xFE, 0x07, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, + 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, + 0xFE, 0x1F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0x00, 0x78, 0x00, 0xF8, + 0x07, 0xC0, 0x07, 0xC0, 0x3E, 0x00, 0x3E, 0x01, 0xF0, 0xF1, 0xE0, 0x0F, + 0x0F, 0x8E, 0x00, 0x78, 0x7C, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x3F, 0xFE, + 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0xFF, 0xFC, 0x00, + 0x07, 0xC3, 0xC0, 0x00, 0x3E, 0x1E, 0x1E, 0x01, 0xE0, 0xE0, 0xF0, 0x0F, + 0x00, 0x0F, 0x80, 0xF8, 0x00, 0x7C, 0x07, 0xC0, 0x03, 0xE1, 0xFF, 0xFF, + 0xFE, 0x1F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF8, + 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xC0, + 0x1F, 0x00, 0x0F, 0x80, 0x3E, 0x00, 0x1E, 0x00, 0x78, 0x00, 0x7C, 0x00, + 0xF0, 0x70, 0xF8, 0x03, 0xE1, 0xF0, 0xE0, 0x07, 0xC3, 0xC0, 0x00, 0x0F, + 0xFF, 0x80, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xFF, + 0xFC, 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x03, 0xC3, 0xE0, 0x00, 0x07, 0x87, + 0xC0, 0x00, 0x1F, 0x07, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x3F, 0xFF, 0x80, + 0x00, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x07, 0xF8, 0x60, 0x03, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xF0, + 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xE0, 0x7F, 0x80, 0xFE, 0x0F, 0xE0, + 0x03, 0xE0, 0xF8, 0x00, 0x3C, 0x1F, 0x00, 0x03, 0xC3, 0xF0, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, 0x7F, 0xFC, 0xF8, 0x0F, 0xFF, + 0xEF, 0x80, 0xFF, 0xFE, 0xF8, 0x0F, 0xFF, 0xCF, 0x80, 0x7F, 0xF8, 0xF8, + 0x00, 0x1F, 0x0F, 0xC0, 0x01, 0xF0, 0xFE, 0x00, 0x1F, 0x07, 0xF8, 0x07, + 0xE0, 0x7F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFC, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0x0F, 0xF8, 0x0F, + 0xFC, 0x7F, 0xF0, 0x7F, 0xF1, 0xFF, 0xC1, 0xFF, 0xC7, 0xFE, 0x03, 0xFE, + 0x1F, 0xF0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3C, 0x00, 0x78, 0x00, + 0xF0, 0x01, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, + 0x00, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFE, 0x00, + 0x7F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xC0, 0x07, 0x80, 0x1F, 0x00, 0x1E, + 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, + 0x1E, 0x00, 0x3C, 0x00, 0xF8, 0x07, 0xFE, 0x1F, 0xF8, 0x3F, 0xF8, 0xFF, + 0xF0, 0xFF, 0xE3, 0xFF, 0xC3, 0xFF, 0x8F, 0xFE, 0x0F, 0xFC, 0x3F, 0xF8, + 0x00, 0x03, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xE0, 0xFF, + 0xFF, 0xF0, 0x7F, 0xFF, 0xF0, 0x00, 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, + 0x03, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x07, 0x80, 0x00, 0x07, 0xC0, 0x01, 0xFF, 0xFF, + 0xC1, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xF8, 0x1F, 0xFF, + 0xF8, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x3F, + 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x07, 0xC0, 0x07, 0x00, 0x0F, 0x80, + 0x1F, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x78, 0x00, 0x78, 0x01, + 0xF0, 0x01, 0xF0, 0x03, 0xE0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x0F, + 0x80, 0x3F, 0x00, 0x1F, 0xC0, 0xFC, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0xFF, + 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x00, 0x03, 0xFF, 0xC3, 0xFE, 0x0F, 0xFF, 0x8F, 0xFC, 0x1F, + 0xFF, 0x3F, 0xF8, 0x3F, 0xFE, 0x7F, 0xF0, 0x7F, 0xF8, 0x7F, 0xC0, 0x1F, + 0x01, 0xFC, 0x00, 0x3E, 0x07, 0xF0, 0x00, 0x78, 0x3F, 0x80, 0x01, 0xF0, + 0xFE, 0x00, 0x03, 0xE3, 0xF0, 0x00, 0x07, 0xDF, 0xC0, 0x00, 0x0F, 0xFE, + 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xFF, 0xFE, + 0x00, 0x01, 0xFC, 0xFC, 0x00, 0x03, 0xE0, 0xFC, 0x00, 0x0F, 0x81, 0xF8, + 0x00, 0x1F, 0x01, 0xF8, 0x00, 0x3E, 0x03, 0xF0, 0x00, 0x78, 0x03, 0xE0, + 0x00, 0xF0, 0x07, 0xE0, 0x1F, 0xFE, 0x0F, 0xF8, 0x7F, 0xFC, 0x1F, 0xF8, + 0xFF, 0xF8, 0x1F, 0xF1, 0xFF, 0xF0, 0x3F, 0xE1, 0xFF, 0xC0, 0x7F, 0x80, + 0x03, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xE0, 0x03, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x07, 0xC0, 0x0E, 0x00, 0xF0, 0x01, 0xE0, 0x3E, 0x00, + 0x7C, 0x07, 0xC0, 0x0F, 0x80, 0xF8, 0x01, 0xF0, 0x1E, 0x00, 0x7C, 0x07, + 0xC0, 0x0F, 0x9F, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, + 0x9F, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFC, 0x00, 0x3F, + 0xC1, 0xFF, 0x00, 0x1F, 0xF0, 0x7F, 0xC0, 0x07, 0xFC, 0x1F, 0xF0, 0x03, + 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0x3F, 0xE0, + 0x3F, 0xE0, 0x0F, 0xF8, 0x1F, 0xF0, 0x03, 0xFF, 0x0F, 0xFC, 0x00, 0xF7, + 0xC3, 0xFF, 0x00, 0x7D, 0xF1, 0xF7, 0xC0, 0x1F, 0x7C, 0xFD, 0xF0, 0x07, + 0xDF, 0xBE, 0x78, 0x01, 0xE3, 0xFF, 0x3E, 0x00, 0x78, 0xFF, 0xCF, 0x80, + 0x3E, 0x3F, 0xE3, 0xE0, 0x0F, 0x87, 0xF0, 0xF8, 0x03, 0xE1, 0xFC, 0x3C, + 0x00, 0xF0, 0x7E, 0x1F, 0x00, 0x7C, 0x1F, 0x07, 0xC0, 0x1F, 0x00, 0x01, + 0xF0, 0x07, 0xC0, 0x00, 0x78, 0x07, 0xFE, 0x01, 0xFF, 0x83, 0xFF, 0xC0, + 0xFF, 0xF0, 0xFF, 0xF0, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F, 0xFE, 0x0F, 0xFC, + 0x03, 0xFF, 0x00, 0x07, 0xF8, 0x07, 0xFF, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, + 0xFC, 0x0F, 0xFF, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, 0xFE, 0x0F, 0xFE, 0x01, + 0xFE, 0x00, 0xF8, 0x01, 0xFF, 0x00, 0xF0, 0x01, 0xFF, 0x01, 0xF0, 0x03, + 0xFF, 0x81, 0xF0, 0x03, 0xFF, 0x81, 0xF0, 0x03, 0xEF, 0xC1, 0xF0, 0x03, + 0xCF, 0xC1, 0xE0, 0x07, 0xC7, 0xE3, 0xE0, 0x07, 0xC7, 0xE3, 0xE0, 0x07, + 0xC3, 0xF3, 0xE0, 0x07, 0xC3, 0xF3, 0xC0, 0x07, 0x81, 0xF7, 0xC0, 0x0F, + 0x81, 0xFF, 0xC0, 0x0F, 0x80, 0xFF, 0xC0, 0x0F, 0x80, 0xFF, 0xC0, 0x0F, + 0x00, 0xFF, 0x80, 0x0F, 0x00, 0x7F, 0x80, 0x7F, 0xF0, 0x7F, 0x80, 0xFF, + 0xF0, 0x3F, 0x80, 0xFF, 0xF0, 0x3F, 0x00, 0xFF, 0xF0, 0x1F, 0x00, 0x7F, + 0xE0, 0x1F, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xFF, 0x80, 0x01, 0xFF, + 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xF8, 0x0F, 0xF0, 0x7F, 0x83, + 0xF8, 0x03, 0xF0, 0xFC, 0x00, 0x7E, 0x1F, 0x00, 0x07, 0xE7, 0xE0, 0x00, + 0x7C, 0xF8, 0x00, 0x0F, 0xBE, 0x00, 0x01, 0xF7, 0xC0, 0x00, 0x3E, 0xF0, + 0x00, 0x07, 0xFE, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x3E, 0xF8, 0x00, 0x07, + 0xDF, 0x00, 0x00, 0xFB, 0xE0, 0x00, 0x3E, 0x7C, 0x00, 0x0F, 0xCF, 0xC0, + 0x01, 0xF0, 0xF8, 0x00, 0x7E, 0x1F, 0x80, 0x3F, 0x83, 0xFC, 0x1F, 0xE0, + 0x3F, 0xFF, 0xF8, 0x03, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0x00, 0x03, 0xFF, + 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF8, + 0x07, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xF0, 0x0F, + 0x80, 0x7F, 0x00, 0xF8, 0x01, 0xF0, 0x0F, 0x00, 0x1F, 0x01, 0xF0, 0x01, + 0xF0, 0x1F, 0x00, 0x1F, 0x01, 0xF0, 0x03, 0xE0, 0x1E, 0x00, 0x7E, 0x01, + 0xE0, 0x0F, 0xC0, 0x3F, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, + 0xE0, 0x03, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x78, 0x00, 0x00, 0x7F, 0xFF, + 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xF0, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xFF, 0x80, 0x03, + 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xF8, 0x0F, 0xF0, 0x7F, + 0x83, 0xF8, 0x03, 0xF0, 0xFC, 0x00, 0x3F, 0x1F, 0x00, 0x07, 0xE7, 0xC0, + 0x00, 0x7D, 0xF8, 0x00, 0x0F, 0xBE, 0x00, 0x01, 0xF7, 0xC0, 0x00, 0x3F, + 0xF0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x3E, 0xF8, 0x00, + 0x07, 0xDF, 0x00, 0x01, 0xFB, 0xE0, 0x00, 0x3E, 0x7E, 0x00, 0x0F, 0x8F, + 0xC0, 0x03, 0xF0, 0xFC, 0x01, 0xFC, 0x1F, 0xE0, 0xFF, 0x01, 0xFF, 0xFF, + 0xC0, 0x1F, 0xFF, 0xF0, 0x01, 0xFF, 0xFC, 0x00, 0x1F, 0xFE, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1F, 0xF8, 0x38, 0x0F, 0xFF, 0xFF, + 0x81, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0x00, 0xF0, + 0x1F, 0x80, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, + 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x00, 0x7C, 0x03, + 0xF8, 0x03, 0xE0, 0x07, 0xC0, 0x1E, 0x00, 0x3E, 0x00, 0xF0, 0x01, 0xF0, + 0x0F, 0x80, 0x1F, 0x80, 0x7C, 0x01, 0xF8, 0x03, 0xE0, 0x3F, 0x80, 0x1F, + 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, + 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x1E, 0x07, 0xF0, 0x01, 0xF0, 0x1F, 0xC0, + 0x0F, 0x80, 0x7E, 0x00, 0x7C, 0x03, 0xF8, 0x03, 0xC0, 0x0F, 0xC0, 0xFF, + 0xE0, 0x7F, 0xCF, 0xFF, 0x01, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xDF, 0xFC, 0x01, 0xFC, 0x00, 0x0F, 0xE1, 0x80, 0x0F, 0xFF, 0xF0, + 0x0F, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xC1, 0xFC, 0x0F, + 0xE0, 0x7C, 0x01, 0xF8, 0x3E, 0x00, 0x3E, 0x0F, 0x80, 0x0F, 0x03, 0xE0, + 0x03, 0xC0, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x07, 0xFF, 0x80, 0x01, + 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, 0x07, 0xFE, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xE1, 0xE0, 0x00, 0xF8, 0xF8, 0x00, + 0x3E, 0x3E, 0x00, 0x1F, 0x8F, 0xC0, 0x0F, 0xC3, 0xFC, 0x0F, 0xF0, 0xFF, + 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0x9F, 0xFE, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xF7, 0xFF, + 0xFF, 0xFD, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0x9F, 0x07, 0x83, 0xE7, + 0x83, 0xE0, 0xFB, 0xE0, 0xF8, 0x3E, 0xF8, 0x3E, 0x0F, 0x3E, 0x0F, 0x07, + 0xCF, 0x07, 0xC1, 0xF3, 0x81, 0xF0, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x03, 0xE0, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x7C, 0x00, 0x07, 0xFF, 0xF8, 0x01, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, + 0x80, 0x3F, 0xFF, 0xE0, 0x07, 0xFF, 0xF0, 0x00, 0x3F, 0xF0, 0x7F, 0xE7, + 0xFF, 0x8F, 0xFF, 0x7F, 0xF9, 0xFF, 0xF7, 0xFF, 0x1F, 0xFE, 0x7F, 0xF0, + 0xFF, 0xC1, 0xE0, 0x01, 0xF0, 0x1E, 0x00, 0x1F, 0x03, 0xE0, 0x01, 0xF0, + 0x3E, 0x00, 0x1F, 0x03, 0xE0, 0x01, 0xE0, 0x3C, 0x00, 0x3E, 0x07, 0xC0, + 0x03, 0xE0, 0x7C, 0x00, 0x3E, 0x07, 0xC0, 0x03, 0xC0, 0x7C, 0x00, 0x3C, + 0x07, 0x80, 0x07, 0xC0, 0xF8, 0x00, 0x7C, 0x0F, 0x80, 0x07, 0xC0, 0xF8, + 0x00, 0x78, 0x0F, 0x80, 0x0F, 0x80, 0xFC, 0x01, 0xF8, 0x0F, 0xC0, 0x3F, + 0x00, 0xFF, 0x07, 0xE0, 0x07, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xC0, 0x03, + 0xFF, 0xF0, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x7F, 0xF0, + 0x1F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFC, 0x0F, + 0xFF, 0x7F, 0xE0, 0x3F, 0xF8, 0x7C, 0x00, 0x1F, 0x01, 0xF0, 0x00, 0xF8, + 0x07, 0xC0, 0x03, 0xE0, 0x1F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0xF8, 0x00, + 0xF8, 0x03, 0xE0, 0x03, 0xE0, 0x1F, 0x00, 0x0F, 0xC0, 0xFC, 0x00, 0x1F, + 0x03, 0xE0, 0x00, 0x7C, 0x1F, 0x00, 0x01, 0xF0, 0xFC, 0x00, 0x07, 0xC3, + 0xE0, 0x00, 0x1F, 0x9F, 0x00, 0x00, 0x3E, 0xFC, 0x00, 0x00, 0xFB, 0xE0, + 0x00, 0x03, 0xFF, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x3F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, + 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0x1E, 0x00, + 0x01, 0xE0, 0xF0, 0x7C, 0x1F, 0x0F, 0x87, 0xE0, 0xF0, 0x7C, 0x3F, 0x0F, + 0x83, 0xE3, 0xF8, 0x7C, 0x1F, 0x1F, 0xE3, 0xC0, 0xF9, 0xFF, 0x3E, 0x07, + 0xCF, 0xF9, 0xF0, 0x3E, 0xFF, 0xCF, 0x01, 0xF7, 0xBE, 0xF8, 0x0F, 0xFD, + 0xF7, 0xC0, 0x7B, 0xCF, 0xFC, 0x03, 0xFE, 0x7F, 0xE0, 0x3F, 0xE3, 0xFF, + 0x01, 0xFF, 0x0F, 0xF0, 0x0F, 0xF0, 0x7F, 0x80, 0x7F, 0x83, 0xFC, 0x03, + 0xF8, 0x1F, 0xC0, 0x1F, 0xC0, 0xFE, 0x00, 0xFC, 0x07, 0xF0, 0x07, 0xE0, + 0x3F, 0x00, 0x3E, 0x01, 0xF8, 0x00, 0x01, 0xFE, 0x03, 0xFE, 0x03, 0xFF, + 0x07, 0xFF, 0x07, 0xFF, 0x07, 0xFF, 0x07, 0xFE, 0x07, 0xFE, 0x03, 0xFC, + 0x03, 0xFC, 0x00, 0xFC, 0x03, 0xF0, 0x00, 0xFE, 0x07, 0xE0, 0x00, 0x7E, + 0x1F, 0xC0, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x1F, + 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x7F, + 0xF8, 0x00, 0x00, 0xFC, 0xFC, 0x00, 0x01, 0xF8, 0x7E, 0x00, 0x03, 0xF0, + 0x7E, 0x00, 0x07, 0xE0, 0x3F, 0x00, 0x0F, 0xC0, 0x1F, 0x80, 0x7F, 0xE0, + 0x7F, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, + 0xFF, 0xE0, 0x7F, 0xC0, 0xFF, 0xC0, 0x7F, 0xC0, 0x7F, 0xFF, 0xF0, 0x3F, + 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0x03, 0xFF, 0x7F, 0x80, 0xFF, 0x87, 0xC0, + 0x1F, 0x01, 0xF8, 0x0F, 0x80, 0x3E, 0x07, 0xC0, 0x0F, 0xC3, 0xE0, 0x01, + 0xF1, 0xF0, 0x00, 0x7E, 0xF8, 0x00, 0x0F, 0xFC, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x00, 0xF0, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x78, 0x00, 0x07, 0xFF, 0xF0, 0x03, 0xFF, 0xFE, 0x00, + 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0x00, 0x01, 0xFF, + 0xFF, 0xC0, 0x3F, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFC, 0x07, 0xC0, 0x3F, 0x00, 0xF8, 0x0F, 0xC0, 0x1F, 0x03, + 0xF0, 0x03, 0xC0, 0xFC, 0x00, 0xF8, 0x3F, 0x00, 0x0E, 0x0F, 0xC0, 0x00, + 0x03, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x1F, 0x80, + 0x00, 0x07, 0xE0, 0x00, 0x01, 0xF8, 0x0E, 0x00, 0x7E, 0x03, 0xE0, 0x1F, + 0x80, 0x7C, 0x07, 0xE0, 0x0F, 0x01, 0xF8, 0x03, 0xE0, 0x7E, 0x00, 0x7C, + 0x1F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFC, 0x0F, 0xFF, + 0xFF, 0x81, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xC0, 0x3F, 0xF0, 0x0F, + 0xFC, 0x07, 0xFF, 0x01, 0xFF, 0x80, 0x7C, 0x00, 0x1E, 0x00, 0x07, 0x80, + 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x07, 0xC0, 0x01, + 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0x80, 0x03, 0xE0, 0x00, 0xF8, + 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x1F, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, + 0x80, 0x03, 0xC0, 0x01, 0xF0, 0x00, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, + 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x00, 0x20, 0x03, 0xC0, 0x3E, 0x01, 0xF0, + 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x3E, 0x01, 0xF0, 0x0F, 0x80, + 0x7C, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x0F, 0x80, 0x7C, 0x03, + 0xE0, 0x1F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0x7C, 0x03, 0xE0, 0x1F, + 0x00, 0xF8, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x1F, 0x00, 0xF8, + 0x07, 0xC0, 0x3E, 0x00, 0xF0, 0x07, 0x80, 0x38, 0x00, 0xFF, 0xC0, 0x7F, + 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x03, 0xE0, 0x00, 0xF0, + 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xE0, 0x00, 0x78, 0x00, + 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, + 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0x80, + 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, + 0xF0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x7F, 0xE0, 0x3F, 0xF8, + 0x0F, 0xFC, 0x03, 0xFF, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x08, 0x00, 0x01, + 0xC0, 0x00, 0x3C, 0x00, 0x07, 0xE0, 0x00, 0xFE, 0x00, 0x1F, 0xF0, 0x03, + 0xFF, 0x80, 0xFF, 0xF8, 0x1F, 0xCF, 0xC3, 0xF8, 0xFE, 0x7E, 0x07, 0xEF, + 0xC0, 0x3F, 0xF8, 0x03, 0xFF, 0x00, 0x1F, 0xE0, 0x00, 0xE0, 0x7F, 0xFF, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, + 0x06, 0x00, 0x3F, 0xE0, 0x03, 0xFF, 0xF8, 0x07, 0xFF, 0xFC, 0x07, 0xFF, + 0xFE, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x3E, 0x00, 0x7F, 0xFE, 0x03, 0xFF, 0xFC, 0x0F, 0xFF, 0xFC, 0x1F, 0xFF, + 0xFC, 0x3F, 0xFF, 0xFC, 0x7F, 0x00, 0x78, 0x7C, 0x00, 0x78, 0xF8, 0x00, + 0xF8, 0xF8, 0x03, 0xF8, 0xFC, 0x0F, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFF, 0x7F, 0xFF, 0xFF, 0x3F, 0xFD, 0xFE, 0x0F, 0xE0, 0x00, 0x03, 0xFC, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0x00, 0x0F, 0xBF, 0xFC, 0x00, 0x7F, + 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0x00, 0xFF, 0x03, + 0xF8, 0x0F, 0xE0, 0x07, 0xE0, 0x7E, 0x00, 0x3F, 0x03, 0xE0, 0x00, 0xF8, + 0x1F, 0x00, 0x07, 0xC0, 0xF0, 0x00, 0x3E, 0x0F, 0x80, 0x01, 0xF0, 0x7C, + 0x00, 0x1F, 0x03, 0xE0, 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0xC0, 0xFC, 0x00, + 0x7C, 0x0F, 0xE0, 0x07, 0xE3, 0xFF, 0xC0, 0xFE, 0x3F, 0xFF, 0xFF, 0xE1, + 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xE0, 0x7F, 0x9F, 0xFC, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x00, 0x1F, 0xE3, 0x80, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, + 0xE0, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0x01, 0xFC, 0x7E, 0x00, + 0x7C, 0x7E, 0x00, 0x3E, 0x3E, 0x00, 0x0E, 0x3E, 0x00, 0x00, 0x1F, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, + 0x00, 0x01, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x0C, 0x7F, 0x80, 0x3F, 0x1F, + 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0x00, + 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x01, 0xFE, 0x00, 0x00, + 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x7C, 0x00, 0x3F, 0x87, 0xC0, 0x0F, + 0xFF, 0x7C, 0x03, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, + 0x81, 0xFC, 0x0F, 0xF8, 0x3F, 0x00, 0x3F, 0x83, 0xE0, 0x01, 0xF0, 0x7C, + 0x00, 0x1F, 0x07, 0xC0, 0x01, 0xF0, 0xF8, 0x00, 0x1F, 0x0F, 0x80, 0x01, + 0xF0, 0xF8, 0x00, 0x1E, 0x0F, 0x80, 0x03, 0xE0, 0xF8, 0x00, 0x3E, 0x0F, + 0xC0, 0x07, 0xE0, 0xFC, 0x00, 0xFE, 0x07, 0xF0, 0x3F, 0xF8, 0x7F, 0xFF, + 0xFF, 0xC3, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xE7, 0xF8, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0xF0, 0x03, 0xFF, + 0xF8, 0x07, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x1F, 0xE0, 0x7E, 0x3F, 0x80, + 0x1F, 0x3F, 0x00, 0x0F, 0x7E, 0x00, 0x0F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, + 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x1C, 0x7F, 0x01, 0xFE, 0x7F, 0xFF, + 0xFE, 0x3F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFC, 0x0F, 0xFF, 0xF0, 0x03, 0xFF, + 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFF, 0xF0, + 0x00, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xE0, 0x01, 0xF0, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x7F, 0xFF, 0xF0, + 0x0F, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0xFF, + 0xFF, 0xE0, 0x00, 0x78, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x03, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x0F, + 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x0F, 0xFE, 0xFF, 0x03, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0x0F, 0xFF, 0xFF, 0xF1, 0xFC, 0x1F, 0xFE, 0x3F, 0x80, 0x7F, 0x03, 0xE0, + 0x03, 0xF0, 0x7E, 0x00, 0x3E, 0x07, 0xC0, 0x03, 0xE0, 0xF8, 0x00, 0x3E, + 0x0F, 0x80, 0x03, 0xE0, 0xF8, 0x00, 0x3E, 0x0F, 0x80, 0x03, 0xC0, 0xF8, + 0x00, 0x7C, 0x0F, 0xC0, 0x0F, 0xC0, 0xFC, 0x01, 0xFC, 0x07, 0xF0, 0x7F, + 0x80, 0x7F, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xF8, 0x00, + 0xFF, 0xEF, 0x80, 0x03, 0xF0, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0x7E, 0x00, 0x1F, 0xFF, 0xE0, 0x03, 0xFF, 0xFC, 0x00, + 0x3F, 0xFF, 0x80, 0x03, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x03, + 0xF8, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x3F, 0xE0, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0x01, 0xF1, 0xF8, 0x00, 0x79, 0xFF, 0x80, 0x1E, 0xFF, 0xF0, 0x0F, + 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0x07, 0xE0, 0x3F, 0x00, 0xF8, + 0x1F, 0x80, 0x3E, 0x07, 0xC0, 0x0F, 0x81, 0xF0, 0x03, 0xC0, 0x7C, 0x00, + 0xF0, 0x1E, 0x00, 0x7C, 0x0F, 0x80, 0x1F, 0x03, 0xE0, 0x07, 0xC0, 0xF8, + 0x01, 0xE0, 0x3C, 0x00, 0xF8, 0x0F, 0x00, 0x3E, 0x1F, 0xF8, 0x3F, 0xEF, + 0xFE, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xF0, 0x3F, + 0xE0, 0x00, 0x07, 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0x1F, 0x80, 0x00, 0x3E, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xC0, 0x07, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0x1F, 0xFC, 0x00, + 0x3F, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x80, 0x00, + 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF0, 0x00, 0x01, + 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x3F, 0xFF, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xF0, 0x00, 0x07, + 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xFE, 0x07, 0xFF, 0xFC, 0x0F, 0xFF, 0xF8, 0x1F, 0xFF, 0xF0, + 0x3F, 0xFF, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x07, 0xC0, 0x00, + 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x00, 0x01, + 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0xC0, + 0xFF, 0xFF, 0x03, 0xFF, 0xFC, 0x07, 0xFF, 0xF0, 0x0F, 0xFF, 0xC0, 0x0F, + 0xFC, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x1F, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x0F, + 0x80, 0x00, 0x03, 0xE0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x3C, 0x3F, 0xF0, + 0x1F, 0x1F, 0xFC, 0x07, 0xC7, 0xFF, 0x01, 0xF1, 0xFF, 0xC0, 0x78, 0x7F, + 0xE0, 0x1E, 0x7F, 0x80, 0x0F, 0xBF, 0x80, 0x03, 0xFF, 0xC0, 0x00, 0xFF, + 0xC0, 0x00, 0x3F, 0xE0, 0x00, 0x0F, 0xFC, 0x00, 0x07, 0xFF, 0x80, 0x01, + 0xF7, 0xF0, 0x00, 0x7C, 0xFE, 0x00, 0x1E, 0x1F, 0xC0, 0x0F, 0x83, 0xF8, + 0x1F, 0xE0, 0xFF, 0xEF, 0xF8, 0x3F, 0xFB, 0xFE, 0x1F, 0xFE, 0xFF, 0x07, + 0xFF, 0x9F, 0xC0, 0xFF, 0xC0, 0x00, 0x7F, 0xF0, 0x01, 0xFF, 0xC0, 0x03, + 0xFF, 0x80, 0x07, 0xFF, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, + 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0xF8, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x80, 0x00, + 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x7F, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0x00, 0x00, 0x07, 0x81, 0xE0, + 0x3F, 0xBF, 0x9F, 0xE1, 0xFF, 0xFE, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0x1F, + 0xFF, 0xFF, 0xFC, 0x7F, 0xC7, 0xF1, 0xF0, 0x7E, 0x1F, 0x87, 0xC1, 0xF0, + 0x7C, 0x1F, 0x07, 0x81, 0xE0, 0x7C, 0x1E, 0x0F, 0x81, 0xE0, 0xF8, 0x3E, + 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3C, 0x0F, 0x03, + 0xC1, 0xF0, 0x7C, 0x0F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF1, + 0xFE, 0x1F, 0x87, 0xEF, 0xFC, 0x7F, 0x1F, 0xFF, 0xF3, 0xFC, 0x7F, 0xFF, + 0xCF, 0xF3, 0xFF, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x01, 0xF8, 0x01, 0xF9, + 0xFF, 0x80, 0xFE, 0xFF, 0xF0, 0x7F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x83, + 0xFF, 0x07, 0xE0, 0x3F, 0x00, 0xF8, 0x1F, 0x80, 0x3E, 0x07, 0xC0, 0x0F, + 0x81, 0xF0, 0x03, 0xC0, 0x7C, 0x00, 0xF0, 0x1E, 0x00, 0x7C, 0x0F, 0x80, + 0x1F, 0x03, 0xE0, 0x07, 0xC0, 0xF8, 0x01, 0xE0, 0x3C, 0x00, 0xF8, 0x0F, + 0x00, 0x3E, 0x1F, 0xF8, 0x3F, 0xEF, 0xFE, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, + 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0xFF, + 0x03, 0xF8, 0xFE, 0x00, 0xFE, 0x7C, 0x00, 0x3F, 0x7C, 0x00, 0x0F, 0xBE, + 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, 0xFF, + 0xC0, 0x00, 0xFB, 0xE0, 0x00, 0xFD, 0xF8, 0x00, 0x7C, 0xFE, 0x00, 0xFE, + 0x3F, 0x81, 0xFE, 0x1F, 0xFF, 0xFE, 0x07, 0xFF, 0xFE, 0x01, 0xFF, 0xFC, + 0x00, 0x7F, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x07, + 0xF9, 0xFF, 0xC0, 0x1F, 0xF7, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xC0, 0x7F, + 0xFF, 0xFF, 0xC0, 0x7F, 0xF0, 0x3F, 0x80, 0x3F, 0x80, 0x1F, 0x80, 0x7E, + 0x00, 0x3F, 0x00, 0xF8, 0x00, 0x3E, 0x01, 0xF0, 0x00, 0x7C, 0x03, 0xC0, + 0x00, 0xF8, 0x0F, 0x80, 0x01, 0xF0, 0x1F, 0x00, 0x07, 0xE0, 0x3E, 0x00, + 0x0F, 0x80, 0x7C, 0x00, 0x3F, 0x01, 0xFC, 0x00, 0xFC, 0x03, 0xFE, 0x07, + 0xF8, 0x07, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0x80, 0x1E, 0xFF, 0xFC, + 0x00, 0x7C, 0xFF, 0xF0, 0x00, 0xF8, 0x7F, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0x03, 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, + 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x07, 0xFF, 0x3F, 0xC0, 0xFF, 0xFD, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, + 0xFF, 0xFF, 0x8F, 0xE0, 0x7F, 0xF8, 0xFC, 0x00, 0xFE, 0x07, 0xC0, 0x03, + 0xE0, 0x7C, 0x00, 0x1F, 0x03, 0xE0, 0x00, 0xF8, 0x1E, 0x00, 0x07, 0xC1, + 0xF0, 0x00, 0x3E, 0x0F, 0x80, 0x01, 0xE0, 0x7C, 0x00, 0x1F, 0x03, 0xF0, + 0x01, 0xF8, 0x1F, 0x80, 0x1F, 0xC0, 0xFF, 0x03, 0xFC, 0x03, 0xFF, 0xFF, + 0xE0, 0x1F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0xFF, 0xE7, 0xC0, + 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x7F, + 0xFE, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0xFF, 0xF0, + 0x00, 0x00, 0x00, 0x0F, 0x80, 0x3F, 0xC3, 0xFE, 0x07, 0xFC, 0xFF, 0xE0, + 0x7F, 0xDF, 0xFF, 0x07, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0x1C, 0x00, 0x7F, + 0xC0, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x01, 0xE0, 0x00, + 0x07, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0xFF, + 0xFF, 0xF0, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xCE, 0x03, 0xFF, 0xFC, + 0x0F, 0xFF, 0xF8, 0x3F, 0xFF, 0xF0, 0xFF, 0xFF, 0xC3, 0xF8, 0x0F, 0x87, + 0xC0, 0x0E, 0x0F, 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xFF, 0x80, 0x3F, + 0xFF, 0xC0, 0x3F, 0xFF, 0xC0, 0x1F, 0xFF, 0xC0, 0x01, 0xFF, 0x80, 0x00, + 0x3F, 0x1C, 0x00, 0x3E, 0x7C, 0x00, 0x7C, 0xFC, 0x03, 0xF3, 0xFF, 0xFF, + 0xE7, 0xFF, 0xFF, 0x8F, 0xFF, 0xFE, 0x1F, 0xFF, 0xF0, 0x00, 0xFF, 0x00, + 0x00, 0x03, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, + 0x80, 0x00, 0x78, 0x00, 0x7F, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xE0, 0x00, + 0x1E, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x3C, 0x00, + 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7E, 0x00, 0xF7, 0xFF, + 0xFF, 0x7F, 0xFF, 0xF3, 0xFF, 0xFE, 0x1F, 0xFF, 0x80, 0x7F, 0x80, 0x7F, + 0x01, 0xFF, 0xFE, 0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, 0xE0, + 0x3F, 0xC7, 0xC0, 0x07, 0x8F, 0x80, 0x1F, 0x3E, 0x00, 0x3E, 0x7C, 0x00, + 0x7C, 0xF8, 0x00, 0xF1, 0xF0, 0x03, 0xE3, 0xE0, 0x07, 0xC7, 0xC0, 0x0F, + 0x8F, 0x80, 0x1F, 0x1F, 0x00, 0x7C, 0x3E, 0x01, 0xF8, 0x7E, 0x0F, 0xFC, + 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, 0xFF, 0xEF, 0xE1, 0xFF, 0xBF, 0x80, + 0xFC, 0x00, 0x00, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF0, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xE1, 0xF8, 0x03, 0xE0, + 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x07, 0xC0, 0x0F, 0x80, 0xF8, 0x00, 0xFC, + 0x1F, 0x80, 0x07, 0xC1, 0xF0, 0x00, 0x7C, 0x3E, 0x00, 0x07, 0xE7, 0xE0, + 0x00, 0x3E, 0x7C, 0x00, 0x03, 0xEF, 0x80, 0x00, 0x3F, 0xF0, 0x00, 0x03, + 0xFF, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x1F, 0xEF, 0xFC, 0x03, 0xFF, + 0xFF, 0xC0, 0x7F, 0xFF, 0xFC, 0x07, 0xFE, 0x7F, 0x80, 0x3F, 0xC3, 0xE1, + 0xF0, 0xF8, 0x3E, 0x3F, 0x0F, 0x03, 0xE3, 0xF1, 0xF0, 0x3E, 0x7F, 0x1E, + 0x03, 0xE7, 0xF3, 0xE0, 0x3E, 0xFF, 0xBC, 0x03, 0xFF, 0xFF, 0xC0, 0x3F, + 0xFF, 0xFC, 0x03, 0xFE, 0xFF, 0x80, 0x3F, 0xEF, 0xF8, 0x03, 0xFC, 0xFF, + 0x00, 0x3F, 0x8F, 0xF0, 0x03, 0xF8, 0x7E, 0x00, 0x3F, 0x07, 0xE0, 0x01, + 0xF0, 0x7C, 0x00, 0x1E, 0x07, 0xC0, 0x00, 0x03, 0xFE, 0x0F, 0xF8, 0x3F, + 0xF0, 0xFF, 0xC1, 0xFF, 0x8F, 0xFE, 0x0F, 0xFC, 0x7F, 0xF0, 0x7F, 0xC1, + 0xFF, 0x00, 0xFE, 0x1F, 0xC0, 0x03, 0xF9, 0xFC, 0x00, 0x0F, 0xFF, 0xC0, + 0x00, 0x3F, 0xF8, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x7F, 0xE0, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0xFE, 0xFE, 0x00, 0x0F, 0xE3, + 0xF8, 0x00, 0xFE, 0x0F, 0xE0, 0x3F, 0xE0, 0x7F, 0xC3, 0xFF, 0x87, 0xFF, + 0x3F, 0xFC, 0x7F, 0xF9, 0xFF, 0xE3, 0xFF, 0x87, 0xFE, 0x0F, 0xF8, 0x00, + 0x01, 0xFE, 0x03, 0xFE, 0x03, 0xFF, 0x07, 0xFF, 0x07, 0xFF, 0x07, 0xFF, + 0x07, 0xFF, 0x07, 0xFE, 0x03, 0xFC, 0x03, 0xFC, 0x01, 0xF8, 0x01, 0xF0, + 0x00, 0xF8, 0x03, 0xF0, 0x00, 0xF8, 0x03, 0xE0, 0x00, 0xFC, 0x07, 0xC0, + 0x00, 0x7C, 0x0F, 0x80, 0x00, 0x7C, 0x0F, 0x80, 0x00, 0x7E, 0x1F, 0x00, + 0x00, 0x7E, 0x3E, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, + 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0x07, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xE0, 0xFF, 0xFF, 0xE0, 0x7C, 0x0F, 0xE0, 0x3C, 0x0F, 0xE0, 0x1E, 0x0F, + 0xC0, 0x00, 0x1F, 0xC0, 0x00, 0x1F, 0xC0, 0x00, 0x1F, 0xC0, 0x00, 0x1F, + 0x80, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0x80, 0xF0, 0x3F, + 0x00, 0xF8, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFE, 0x0F, + 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0x00, 0x1F, 0xC0, 0x1F, + 0xE0, 0x1F, 0xF0, 0x0F, 0xE0, 0x0F, 0xC0, 0x07, 0xC0, 0x07, 0xC0, 0x03, + 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x3F, 0x80, 0x3F, 0xC0, 0x1F, 0xC0, + 0x0F, 0xE0, 0x07, 0xF8, 0x00, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, + 0x80, 0x07, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, + 0x7E, 0x00, 0x3F, 0x80, 0x1F, 0xE0, 0x07, 0xF0, 0x03, 0xF8, 0x00, 0x78, + 0x00, 0x01, 0xE0, 0x3C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0x80, 0xF0, 0x3E, + 0x07, 0xC0, 0xF0, 0x1E, 0x03, 0xC0, 0xF8, 0x1F, 0x03, 0xC0, 0x78, 0x0F, + 0x03, 0xE0, 0x7C, 0x0F, 0x01, 0xE0, 0x3C, 0x0F, 0x81, 0xF0, 0x3C, 0x07, + 0x80, 0xF0, 0x3E, 0x07, 0xC0, 0xF0, 0x1E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, + 0xC0, 0x70, 0x00, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0x7F, 0x00, 0x3F, 0xC0, + 0x0F, 0xE0, 0x03, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, + 0x00, 0x0F, 0x80, 0x07, 0x80, 0x03, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, + 0xF8, 0x00, 0x7E, 0x00, 0x3F, 0xC0, 0x0F, 0xE0, 0x07, 0xF0, 0x07, 0xF8, + 0x07, 0xF8, 0x03, 0xE0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF0, 0x00, 0x78, + 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x00, 0x1F, 0x80, 0x7F, + 0xC0, 0x7F, 0xC0, 0x3F, 0xC0, 0x1F, 0xC0, 0x07, 0x80, 0x00, 0x03, 0xE0, + 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0xE0, 0x39, 0xFF, 0xE0, 0xF7, 0xFF, 0xE7, + 0xFF, 0xCF, 0xFF, 0xFE, 0x0F, 0xFF, 0x38, 0x0F, 0xFC, 0x00, 0x0F, 0xE0, + 0x00, 0x0F, 0x80}; + +const GFXglyph FreeMonoBoldOblique24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 28, 0, 1}, // 0x20 ' ' + {0, 12, 31, 28, 12, -29}, // 0x21 '!' + {47, 17, 14, 28, 11, -28}, // 0x22 '"' + {77, 24, 34, 28, 5, -30}, // 0x23 '#' + {179, 25, 38, 28, 4, -31}, // 0x24 '$' + {298, 22, 30, 28, 6, -28}, // 0x25 '%' + {381, 21, 28, 28, 5, -26}, // 0x26 '&' + {455, 7, 14, 28, 16, -28}, // 0x27 ''' + {468, 14, 37, 28, 14, -29}, // 0x28 '(' + {533, 14, 37, 28, 5, -29}, // 0x29 ')' + {598, 21, 19, 28, 8, -28}, // 0x2A '*' + {648, 24, 26, 28, 5, -25}, // 0x2B '+' + {726, 12, 14, 28, 6, -6}, // 0x2C ',' + {747, 24, 5, 28, 5, -15}, // 0x2D '-' + {762, 7, 6, 28, 11, -4}, // 0x2E '.' + {768, 28, 38, 28, 3, -32}, // 0x2F '/' + {901, 23, 31, 28, 6, -29}, // 0x30 '0' + {991, 21, 30, 28, 4, -29}, // 0x31 '1' + {1070, 26, 30, 28, 3, -29}, // 0x32 '2' + {1168, 25, 31, 28, 4, -29}, // 0x33 '3' + {1265, 22, 28, 28, 5, -27}, // 0x34 '4' + {1342, 25, 31, 28, 4, -29}, // 0x35 '5' + {1439, 24, 31, 28, 7, -29}, // 0x36 '6' + {1532, 22, 30, 28, 9, -29}, // 0x37 '7' + {1615, 23, 31, 28, 6, -29}, // 0x38 '8' + {1705, 24, 31, 28, 5, -29}, // 0x39 '9' + {1798, 10, 22, 28, 11, -20}, // 0x3A ':' + {1826, 15, 28, 28, 5, -20}, // 0x3B ';' + {1879, 25, 21, 28, 5, -23}, // 0x3C '<' + {1945, 26, 14, 28, 4, -19}, // 0x3D '=' + {1991, 25, 22, 28, 4, -23}, // 0x3E '>' + {2060, 19, 29, 28, 10, -27}, // 0x3F '?' + {2129, 23, 36, 28, 5, -28}, // 0x40 '@' + {2233, 30, 27, 28, 0, -26}, // 0x41 'A' + {2335, 29, 27, 28, 1, -26}, // 0x42 'B' + {2433, 28, 29, 28, 3, -27}, // 0x43 'C' + {2535, 28, 27, 28, 1, -26}, // 0x44 'D' + {2630, 29, 27, 28, 1, -26}, // 0x45 'E' + {2728, 31, 27, 28, 0, -26}, // 0x46 'F' + {2833, 28, 29, 28, 3, -27}, // 0x47 'G' + {2935, 30, 27, 28, 1, -26}, // 0x48 'H' + {3037, 25, 27, 28, 3, -26}, // 0x49 'I' + {3122, 31, 28, 28, 0, -26}, // 0x4A 'J' + {3231, 31, 27, 28, 0, -26}, // 0x4B 'K' + {3336, 27, 27, 28, 1, -26}, // 0x4C 'L' + {3428, 34, 27, 28, 0, -26}, // 0x4D 'M' + {3543, 32, 27, 28, 1, -26}, // 0x4E 'N' + {3651, 27, 29, 28, 3, -27}, // 0x4F 'O' + {3749, 28, 27, 28, 1, -26}, // 0x50 'P' + {3844, 27, 35, 28, 3, -27}, // 0x51 'Q' + {3963, 29, 27, 28, 0, -26}, // 0x52 'R' + {4061, 26, 29, 28, 3, -27}, // 0x53 'S' + {4156, 26, 27, 28, 4, -26}, // 0x54 'T' + {4244, 28, 28, 28, 4, -26}, // 0x55 'U' + {4342, 30, 27, 28, 2, -26}, // 0x56 'V' + {4444, 29, 27, 28, 3, -26}, // 0x57 'W' + {4542, 32, 27, 28, 0, -26}, // 0x58 'X' + {4650, 26, 27, 28, 4, -26}, // 0x59 'Y' + {4738, 27, 27, 28, 2, -26}, // 0x5A 'Z' + {4830, 18, 37, 28, 10, -29}, // 0x5B '[' + {4914, 13, 38, 28, 10, -32}, // 0x5C '\' + {4976, 18, 37, 28, 5, -29}, // 0x5D ']' + {5060, 20, 15, 28, 8, -29}, // 0x5E '^' + {5098, 29, 5, 28, -2, 5}, // 0x5F '_' + {5117, 8, 8, 28, 13, -30}, // 0x60 '`' + {5125, 24, 23, 28, 3, -21}, // 0x61 'a' + {5194, 29, 31, 28, 0, -29}, // 0x62 'b' + {5307, 25, 23, 28, 3, -21}, // 0x63 'c' + {5379, 28, 31, 28, 3, -29}, // 0x64 'd' + {5488, 24, 23, 28, 3, -21}, // 0x65 'e' + {5557, 28, 30, 28, 4, -29}, // 0x66 'f' + {5662, 28, 31, 28, 3, -21}, // 0x67 'g' + {5771, 26, 30, 28, 2, -29}, // 0x68 'h' + {5869, 23, 29, 28, 3, -28}, // 0x69 'i' + {5953, 23, 38, 28, 3, -28}, // 0x6A 'j' + {6063, 26, 30, 28, 2, -29}, // 0x6B 'k' + {6161, 23, 30, 28, 3, -29}, // 0x6C 'l' + {6248, 30, 22, 28, 0, -21}, // 0x6D 'm' + {6331, 26, 22, 28, 2, -21}, // 0x6E 'n' + {6403, 25, 23, 28, 3, -21}, // 0x6F 'o' + {6475, 31, 31, 28, -1, -21}, // 0x70 'p' + {6596, 29, 31, 28, 2, -21}, // 0x71 'q' + {6709, 28, 22, 28, 2, -21}, // 0x72 'r' + {6786, 23, 23, 28, 4, -21}, // 0x73 's' + {6853, 20, 28, 28, 5, -26}, // 0x74 't' + {6923, 23, 22, 28, 5, -20}, // 0x75 'u' + {6987, 28, 21, 28, 3, -20}, // 0x76 'v' + {7061, 28, 21, 28, 3, -20}, // 0x77 'w' + {7135, 29, 21, 28, 1, -20}, // 0x78 'x' + {7212, 32, 30, 28, -1, -20}, // 0x79 'y' + {7332, 25, 21, 28, 4, -20}, // 0x7A 'z' + {7398, 17, 37, 28, 10, -29}, // 0x7B '{' + {7477, 11, 36, 28, 11, -28}, // 0x7C '|' + {7527, 17, 37, 28, 6, -29}, // 0x7D '}' + {7606, 23, 10, 28, 5, -17}}; // 0x7E '~' + +const GFXfont FreeMonoBoldOblique24pt7b PROGMEM = { + (uint8_t *)FreeMonoBoldOblique24pt7bBitmaps, + (GFXglyph *)FreeMonoBoldOblique24pt7bGlyphs, 0x20, 0x7E, 47}; + +// Approx. 8307 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique9pt7b.h new file mode 100644 index 0000000..334ab7b --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoBoldOblique9pt7b.h @@ -0,0 +1,202 @@ +const uint8_t FreeMonoBoldOblique9pt7bBitmaps[] PROGMEM = { + 0x39, 0xCC, 0x67, 0x31, 0x8C, 0x07, 0x38, 0x6C, 0xD9, 0x36, 0x48, 0x80, + 0x09, 0x0D, 0x86, 0xCF, 0xF7, 0xF9, 0xB3, 0xFD, 0xFE, 0x6C, 0x36, 0x1B, + 0x00, 0x00, 0x06, 0x07, 0x07, 0xE6, 0x33, 0x01, 0xE0, 0x7C, 0x06, 0x43, + 0x33, 0xBF, 0x83, 0x03, 0x00, 0x80, 0x1C, 0x11, 0x10, 0x88, 0x83, 0xB8, + 0xF3, 0xB8, 0x22, 0x21, 0x11, 0x07, 0x00, 0x0F, 0x1F, 0x30, 0x30, 0x38, + 0x7B, 0xDF, 0xCE, 0xFF, 0x7E, 0xFA, 0x80, 0x19, 0x8C, 0xC6, 0x63, 0x18, + 0xC6, 0x31, 0xC6, 0x30, 0x31, 0xC6, 0x31, 0x8C, 0x63, 0x31, 0x98, 0xCC, + 0x40, 0x08, 0x08, 0xFF, 0xFF, 0x38, 0x6C, 0x6C, 0x0C, 0x06, 0x03, 0x1F, + 0xFF, 0xF8, 0xC0, 0x60, 0x30, 0x10, 0x00, 0x36, 0x4C, 0x80, 0xFF, 0xFF, + 0xC0, 0xFC, 0x00, 0x00, 0x0C, 0x03, 0x00, 0xC0, 0x18, 0x06, 0x01, 0x80, + 0x30, 0x0C, 0x03, 0x00, 0x60, 0x18, 0x06, 0x00, 0xC0, 0x30, 0x00, 0x0F, + 0x0F, 0xCC, 0x6C, 0x36, 0x1B, 0x0D, 0x05, 0x86, 0xC3, 0x63, 0x3F, 0x8F, + 0x00, 0x06, 0x1C, 0x3C, 0x6C, 0x0C, 0x0C, 0x08, 0x18, 0x18, 0x18, 0xFE, + 0xFE, 0x07, 0x83, 0xF1, 0x8C, 0x43, 0x00, 0xC0, 0xE0, 0x70, 0x38, 0x38, + 0x1C, 0x6F, 0xF3, 0xFC, 0x1F, 0x1F, 0xC0, 0x60, 0x30, 0x30, 0x70, 0x38, + 0x06, 0x03, 0x03, 0xBF, 0x9F, 0x80, 0x03, 0x07, 0x0B, 0x1B, 0x32, 0x66, + 0xFF, 0xFF, 0x1E, 0x1E, 0x3F, 0x9F, 0x98, 0x0F, 0xC7, 0xF3, 0x18, 0x0C, + 0x06, 0x06, 0x7F, 0x1E, 0x00, 0x07, 0x87, 0xCE, 0x06, 0x06, 0x03, 0xF3, + 0xFD, 0xC6, 0xC3, 0x63, 0xBF, 0x8F, 0x80, 0xFF, 0xFF, 0xC3, 0x06, 0x06, + 0x0C, 0x18, 0x18, 0x30, 0x30, 0x60, 0x1F, 0x1F, 0xDC, 0x6C, 0x36, 0x31, + 0xF1, 0xF8, 0xC6, 0xC3, 0x63, 0xBF, 0x8F, 0x80, 0x1E, 0x3F, 0x33, 0x63, + 0x63, 0x67, 0x7F, 0x3E, 0x06, 0x1C, 0xF8, 0xF0, 0x77, 0x00, 0x00, 0xEE, + 0x1C, 0x70, 0x00, 0x00, 0x03, 0x0C, 0x61, 0x08, 0x00, 0x00, 0xC1, 0xE1, + 0xE1, 0xE0, 0xF0, 0x07, 0x00, 0xF0, 0x0C, 0x7F, 0xDF, 0xF0, 0x00, 0x00, + 0x7F, 0xFF, 0xF0, 0x30, 0x0F, 0x00, 0xE0, 0x1E, 0x07, 0xC7, 0x87, 0x83, + 0x00, 0x7D, 0xFF, 0x18, 0x30, 0xE3, 0x9C, 0x30, 0x01, 0xC3, 0x80, 0x0F, + 0x0F, 0xCC, 0x6C, 0x36, 0x72, 0x79, 0x7D, 0xB6, 0xDA, 0x6F, 0xB3, 0xD8, + 0x0C, 0x07, 0xE1, 0xE0, 0x0F, 0x83, 0xF0, 0x1E, 0x03, 0xC0, 0xD8, 0x31, + 0x87, 0xF1, 0xFE, 0x30, 0xDF, 0x3F, 0xC7, 0x80, 0x3F, 0xC7, 0xFC, 0x61, + 0x8C, 0x31, 0xFC, 0x3F, 0x84, 0x19, 0x83, 0x30, 0x6F, 0xFB, 0xFE, 0x00, + 0x0F, 0xF1, 0xFF, 0x30, 0x66, 0x06, 0x60, 0x0C, 0x00, 0xC0, 0x0C, 0x00, + 0xE0, 0xC7, 0xF8, 0x3F, 0x00, 0x3F, 0x87, 0xF8, 0x63, 0x8C, 0x31, 0x06, + 0x60, 0xCC, 0x19, 0x86, 0x31, 0xCF, 0xF3, 0xF8, 0x00, 0x3F, 0xE3, 0xFE, + 0x18, 0x61, 0xB6, 0x1F, 0x01, 0xF0, 0x32, 0x03, 0x00, 0x30, 0x4F, 0xFC, + 0xFF, 0xC0, 0x3F, 0xF3, 0xFE, 0x18, 0x61, 0xB6, 0x1F, 0x03, 0xF0, 0x32, + 0x03, 0x00, 0x30, 0x0F, 0xC0, 0xFC, 0x00, 0x0F, 0xE3, 0xFC, 0xC1, 0x30, + 0x06, 0x01, 0x80, 0x31, 0xF6, 0x3E, 0xE1, 0x9F, 0xF0, 0xF8, 0x00, 0x1E, + 0xF3, 0xCF, 0x18, 0x61, 0x84, 0x10, 0xC3, 0xFC, 0x3F, 0xC3, 0x08, 0x31, + 0x8F, 0xBC, 0xFB, 0xC0, 0x3F, 0xCF, 0xF0, 0x60, 0x10, 0x0C, 0x03, 0x00, + 0xC0, 0x20, 0x18, 0x3F, 0xCF, 0xF0, 0x07, 0xF0, 0x7F, 0x00, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x61, 0x84, 0x10, 0xC3, 0x0F, 0xE0, 0x7C, 0x00, 0x3E, + 0xE7, 0xFC, 0x66, 0x0D, 0x81, 0x60, 0x7C, 0x0E, 0xC1, 0x98, 0x31, 0x1F, + 0x3B, 0xE7, 0x00, 0x3F, 0x07, 0xE0, 0x30, 0x06, 0x00, 0xC0, 0x10, 0x06, + 0x00, 0xC3, 0x18, 0x6F, 0xFB, 0xFF, 0x00, 0x38, 0x39, 0xC3, 0xC7, 0x3C, + 0x79, 0xE3, 0xDA, 0x1F, 0xF0, 0x9D, 0x8C, 0xCC, 0x60, 0x67, 0xCF, 0x3C, + 0x78, 0x3C, 0xF9, 0xE7, 0x87, 0x18, 0x3C, 0xC1, 0x66, 0x1B, 0xB0, 0xCD, + 0x06, 0x78, 0x31, 0xC3, 0xCE, 0x3E, 0x30, 0x0F, 0x0F, 0xE7, 0x1D, 0x83, + 0xC0, 0xF0, 0x3C, 0x0F, 0x06, 0xE3, 0x9F, 0xC3, 0xC0, 0x3F, 0xC7, 0xFC, + 0x61, 0x8C, 0x31, 0x8E, 0x3F, 0x87, 0xE1, 0x80, 0x30, 0x0F, 0xC3, 0xF0, + 0x00, 0x0F, 0x0F, 0xE7, 0x1D, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x06, 0xE3, + 0x1F, 0xC3, 0xC0, 0x80, 0x7F, 0x3F, 0xC0, 0x3F, 0xC3, 0xFE, 0x18, 0x61, + 0x86, 0x10, 0xE3, 0xFC, 0x3F, 0x83, 0x18, 0x31, 0xCF, 0x8F, 0xF8, 0x70, + 0x1E, 0xCF, 0xF7, 0x19, 0x80, 0x70, 0x1F, 0x81, 0xF3, 0x0C, 0xC3, 0x3F, + 0x8B, 0xC0, 0x7F, 0xCF, 0xF9, 0x93, 0x66, 0x60, 0xC0, 0x18, 0x02, 0x00, + 0xC0, 0x18, 0x0F, 0xC1, 0xF8, 0x00, 0xF9, 0xFF, 0x7D, 0x83, 0x30, 0x64, + 0x09, 0x83, 0x30, 0x66, 0x0C, 0xE3, 0x0F, 0xC0, 0xF0, 0x00, 0xF9, 0xFE, + 0x3D, 0x83, 0x30, 0xC6, 0x30, 0xE6, 0x0D, 0x81, 0xB0, 0x3C, 0x07, 0x00, + 0x60, 0x00, 0xF9, 0xFF, 0x3D, 0x83, 0x36, 0x64, 0xC8, 0xBF, 0x35, 0xE7, + 0xB8, 0xE7, 0x1C, 0xE3, 0x18, 0x00, 0x3C, 0xF3, 0xCF, 0x1C, 0xC0, 0xD8, + 0x0F, 0x00, 0x60, 0x0F, 0x01, 0xB8, 0x31, 0x8F, 0x3C, 0xF3, 0xC0, 0x79, + 0xEE, 0x38, 0xC6, 0x19, 0x81, 0xE0, 0x38, 0x06, 0x00, 0xC0, 0x18, 0x0F, + 0xC3, 0xF8, 0x00, 0x3F, 0xCF, 0xF3, 0x18, 0xCC, 0x06, 0x03, 0x01, 0x80, + 0xC6, 0x61, 0xBF, 0xCF, 0xF0, 0x1E, 0x3C, 0xC1, 0x83, 0x06, 0x08, 0x30, + 0x60, 0xC1, 0x06, 0x0F, 0x1E, 0x00, 0x06, 0x31, 0x86, 0x31, 0x8C, 0x31, + 0x8C, 0x61, 0x8C, 0x60, 0x1E, 0x78, 0x30, 0x60, 0xC1, 0x86, 0x0C, 0x18, + 0x30, 0x41, 0x8F, 0x1E, 0x00, 0x08, 0x1C, 0x3C, 0x76, 0xE7, 0xC3, 0x7F, + 0xFF, 0xFC, 0x88, 0x80, 0x0F, 0x07, 0xE1, 0xF9, 0xFE, 0xE3, 0x30, 0xCF, + 0xFD, 0xFF, 0x38, 0x07, 0x00, 0x60, 0x0F, 0xC1, 0xFC, 0x71, 0xCC, 0x19, + 0x83, 0x30, 0xDF, 0xFB, 0xBC, 0x00, 0x1F, 0xCF, 0xF6, 0x1B, 0x00, 0xC0, + 0x30, 0x0F, 0xF1, 0xF8, 0x01, 0xE0, 0x38, 0x03, 0x0F, 0x63, 0xFC, 0xC3, + 0x30, 0x66, 0x0C, 0xC3, 0x9F, 0xF9, 0xF7, 0x00, 0x1F, 0x1F, 0xD8, 0x3F, + 0xFF, 0xFE, 0x1B, 0xFC, 0xF8, 0x07, 0xC3, 0xF1, 0x81, 0xFE, 0x7F, 0x84, + 0x03, 0x00, 0xC0, 0x30, 0x3F, 0x8F, 0xE0, 0x1E, 0xE7, 0xFD, 0x86, 0x60, + 0xCC, 0x19, 0xC6, 0x3F, 0xC1, 0xD8, 0x03, 0x00, 0xE1, 0xF8, 0x3E, 0x00, + 0x38, 0x1E, 0x01, 0x00, 0xDC, 0x3F, 0x8C, 0x62, 0x19, 0x84, 0x63, 0x3D, + 0xFF, 0x7C, 0x06, 0x03, 0x00, 0x03, 0xC3, 0xE0, 0x20, 0x30, 0x18, 0x0C, + 0x3F, 0xFF, 0xE0, 0x01, 0x81, 0x80, 0x07, 0xF3, 0xF8, 0x0C, 0x04, 0x06, + 0x03, 0x01, 0x80, 0xC0, 0x40, 0x67, 0xE3, 0xE0, 0x38, 0x0E, 0x01, 0x80, + 0x4F, 0x37, 0xCF, 0x83, 0xC0, 0xF0, 0x26, 0x39, 0xEE, 0x78, 0x1F, 0x0F, + 0x01, 0x80, 0xC0, 0x60, 0x20, 0x30, 0x18, 0x0C, 0x3F, 0xFF, 0xE0, 0x7E, + 0xE7, 0xFF, 0x33, 0x32, 0x63, 0x66, 0x36, 0x62, 0xF7, 0x7F, 0x67, 0x77, + 0x8F, 0xF8, 0xC3, 0x10, 0x66, 0x08, 0xC3, 0x3C, 0x7F, 0x8F, 0x1F, 0x0F, + 0xE6, 0x1F, 0x03, 0xC0, 0xF8, 0x67, 0xF0, 0xF8, 0x3F, 0xE3, 0xFF, 0x1C, + 0x31, 0x83, 0x18, 0x31, 0x86, 0x3F, 0xE3, 0x78, 0x30, 0x03, 0x00, 0xFC, + 0x0F, 0x80, 0x1E, 0xEF, 0xFD, 0x86, 0x60, 0xCC, 0x19, 0xC7, 0x3F, 0xE1, + 0xE8, 0x03, 0x00, 0x60, 0x3E, 0x07, 0xC0, 0x39, 0xDF, 0xF1, 0xC0, 0x60, + 0x10, 0x0C, 0x0F, 0xF3, 0xF8, 0x1F, 0x7F, 0x63, 0x7E, 0x1F, 0xC3, 0xFE, + 0xFC, 0x10, 0x08, 0x0C, 0x1F, 0xEF, 0xF1, 0x80, 0x80, 0xC0, 0x60, 0x3F, + 0x8F, 0x80, 0xF3, 0xFC, 0xF6, 0x09, 0x86, 0x61, 0x98, 0xE7, 0xF8, 0xFE, + 0xFB, 0xFF, 0x7C, 0xC6, 0x19, 0x83, 0x60, 0x6C, 0x07, 0x00, 0xC0, 0xF1, + 0xFE, 0x3D, 0xB3, 0x37, 0xC7, 0xF8, 0xEE, 0x1D, 0xC3, 0x30, 0x79, 0xEF, + 0x38, 0xEE, 0x0F, 0x01, 0xE0, 0x6E, 0x3C, 0xE7, 0xBC, 0x3C, 0xF3, 0x8F, + 0x18, 0xC1, 0x9C, 0x19, 0x81, 0xF0, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, + 0xFC, 0x0F, 0xC0, 0x7F, 0xBF, 0xD9, 0xC1, 0x83, 0x83, 0x1B, 0xFD, 0xFE, + 0x06, 0x1C, 0x60, 0xC1, 0x86, 0x3C, 0x70, 0x30, 0x41, 0x83, 0x07, 0x06, + 0x00, 0x33, 0x32, 0x26, 0x66, 0x44, 0xCC, 0xC8, 0x0C, 0x0E, 0x04, 0x0C, + 0x0C, 0x0C, 0x0F, 0x0F, 0x18, 0x18, 0x10, 0x30, 0xF0, 0xE0, 0x38, 0x7C, + 0xF7, 0xC1, 0xC0}; + +const GFXglyph FreeMonoBoldOblique9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 11, 0, 1}, // 0x20 ' ' + {0, 5, 11, 11, 4, -10}, // 0x21 '!' + {7, 7, 5, 11, 4, -10}, // 0x22 '"' + {12, 9, 12, 11, 2, -10}, // 0x23 '#' + {26, 9, 14, 11, 2, -11}, // 0x24 '$' + {42, 9, 11, 11, 2, -10}, // 0x25 '%' + {55, 8, 10, 11, 2, -9}, // 0x26 '&' + {65, 2, 5, 11, 6, -10}, // 0x27 ''' + {67, 5, 14, 11, 5, -10}, // 0x28 '(' + {76, 5, 14, 11, 2, -10}, // 0x29 ')' + {85, 8, 7, 11, 3, -10}, // 0x2A '*' + {92, 9, 9, 11, 2, -8}, // 0x2B '+' + {103, 4, 5, 11, 2, -1}, // 0x2C ',' + {106, 9, 2, 11, 2, -5}, // 0x2D '-' + {109, 3, 2, 11, 4, -1}, // 0x2E '.' + {110, 11, 15, 11, 1, -12}, // 0x2F '/' + {131, 9, 12, 11, 2, -11}, // 0x30 '0' + {145, 8, 12, 11, 2, -11}, // 0x31 '1' + {157, 10, 12, 11, 1, -11}, // 0x32 '2' + {172, 9, 12, 11, 2, -11}, // 0x33 '3' + {186, 8, 10, 11, 2, -9}, // 0x34 '4' + {196, 9, 11, 11, 3, -10}, // 0x35 '5' + {209, 9, 12, 11, 3, -11}, // 0x36 '6' + {223, 8, 11, 11, 3, -10}, // 0x37 '7' + {234, 9, 12, 11, 2, -11}, // 0x38 '8' + {248, 8, 12, 11, 3, -11}, // 0x39 '9' + {260, 4, 8, 11, 4, -7}, // 0x3A ':' + {264, 6, 11, 11, 2, -7}, // 0x3B ';' + {273, 10, 8, 11, 2, -8}, // 0x3C '<' + {283, 10, 6, 11, 1, -7}, // 0x3D '=' + {291, 10, 8, 11, 1, -8}, // 0x3E '>' + {301, 7, 11, 11, 4, -10}, // 0x3F '?' + {311, 9, 15, 11, 2, -11}, // 0x40 '@' + {328, 11, 11, 11, 0, -10}, // 0x41 'A' + {344, 11, 11, 11, 0, -10}, // 0x42 'B' + {360, 12, 11, 11, 1, -10}, // 0x43 'C' + {377, 11, 11, 11, 0, -10}, // 0x44 'D' + {393, 12, 11, 11, 0, -10}, // 0x45 'E' + {410, 12, 11, 11, 0, -10}, // 0x46 'F' + {427, 11, 11, 11, 1, -10}, // 0x47 'G' + {443, 12, 11, 11, 0, -10}, // 0x48 'H' + {460, 10, 11, 11, 1, -10}, // 0x49 'I' + {474, 12, 11, 11, 0, -10}, // 0x4A 'J' + {491, 11, 11, 11, 0, -10}, // 0x4B 'K' + {507, 11, 11, 11, 0, -10}, // 0x4C 'L' + {523, 13, 11, 11, 0, -10}, // 0x4D 'M' + {541, 13, 11, 11, 0, -10}, // 0x4E 'N' + {559, 10, 11, 11, 1, -10}, // 0x4F 'O' + {573, 11, 11, 11, 0, -10}, // 0x50 'P' + {589, 10, 14, 11, 1, -10}, // 0x51 'Q' + {607, 12, 11, 11, 0, -10}, // 0x52 'R' + {624, 10, 11, 11, 2, -10}, // 0x53 'S' + {638, 11, 11, 11, 1, -10}, // 0x54 'T' + {654, 11, 11, 11, 1, -10}, // 0x55 'U' + {670, 11, 11, 11, 1, -10}, // 0x56 'V' + {686, 11, 11, 11, 1, -10}, // 0x57 'W' + {702, 12, 11, 11, 0, -10}, // 0x58 'X' + {719, 11, 11, 11, 1, -10}, // 0x59 'Y' + {735, 10, 11, 11, 1, -10}, // 0x5A 'Z' + {749, 7, 14, 11, 4, -10}, // 0x5B '[' + {762, 5, 15, 11, 4, -12}, // 0x5C '\' + {772, 7, 14, 11, 2, -10}, // 0x5D ']' + {785, 8, 6, 11, 3, -11}, // 0x5E '^' + {791, 11, 2, 11, -1, 3}, // 0x5F '_' + {794, 3, 3, 11, 5, -11}, // 0x60 '`' + {796, 10, 8, 11, 1, -7}, // 0x61 'a' + {806, 11, 11, 11, 0, -10}, // 0x62 'b' + {822, 10, 8, 11, 1, -7}, // 0x63 'c' + {832, 11, 11, 11, 1, -10}, // 0x64 'd' + {848, 9, 8, 11, 1, -7}, // 0x65 'e' + {857, 10, 11, 11, 2, -10}, // 0x66 'f' + {871, 11, 12, 11, 1, -7}, // 0x67 'g' + {888, 10, 11, 11, 1, -10}, // 0x68 'h' + {902, 9, 11, 11, 1, -10}, // 0x69 'i' + {915, 9, 15, 11, 1, -10}, // 0x6A 'j' + {932, 10, 11, 11, 1, -10}, // 0x6B 'k' + {946, 9, 11, 11, 1, -10}, // 0x6C 'l' + {959, 12, 8, 11, 0, -7}, // 0x6D 'm' + {971, 11, 8, 11, 1, -7}, // 0x6E 'n' + {982, 10, 8, 11, 1, -7}, // 0x6F 'o' + {992, 12, 12, 11, -1, -7}, // 0x70 'p' + {1010, 11, 12, 11, 1, -7}, // 0x71 'q' + {1027, 10, 8, 11, 1, -7}, // 0x72 'r' + {1037, 8, 8, 11, 2, -7}, // 0x73 's' + {1045, 9, 11, 11, 1, -10}, // 0x74 't' + {1058, 10, 8, 11, 1, -7}, // 0x75 'u' + {1068, 11, 8, 11, 1, -7}, // 0x76 'v' + {1079, 11, 8, 11, 1, -7}, // 0x77 'w' + {1090, 11, 8, 11, 1, -7}, // 0x78 'x' + {1101, 12, 12, 11, 0, -7}, // 0x79 'y' + {1119, 9, 8, 11, 2, -7}, // 0x7A 'z' + {1128, 7, 14, 11, 3, -10}, // 0x7B '{' + {1141, 4, 14, 11, 4, -10}, // 0x7C '|' + {1148, 8, 14, 11, 2, -10}, // 0x7D '}' + {1162, 9, 4, 11, 2, -6}}; // 0x7E '~' + +const GFXfont FreeMonoBoldOblique9pt7b PROGMEM = { + (uint8_t *)FreeMonoBoldOblique9pt7bBitmaps, + (GFXglyph *)FreeMonoBoldOblique9pt7bGlyphs, 0x20, 0x7E, 18}; + +// Approx. 1839 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique12pt7b.h new file mode 100644 index 0000000..f17a0f8 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique12pt7b.h @@ -0,0 +1,247 @@ +const uint8_t FreeMonoOblique12pt7bBitmaps[] PROGMEM = { + 0x11, 0x11, 0x12, 0x22, 0x22, 0x00, 0x0E, 0xE0, 0xE7, 0xE7, 0xC6, 0xC6, + 0xC6, 0x84, 0x84, 0x02, 0x40, 0x88, 0x12, 0x02, 0x40, 0x48, 0x7F, 0xC2, + 0x40, 0x48, 0x11, 0x1F, 0xF8, 0x48, 0x09, 0x02, 0x40, 0x48, 0x09, 0x02, + 0x20, 0x02, 0x01, 0x00, 0xF4, 0xC3, 0x60, 0x50, 0x04, 0x00, 0xC0, 0x0F, + 0x00, 0x60, 0x0A, 0x02, 0x81, 0x30, 0xC7, 0xC0, 0x80, 0x20, 0x08, 0x00, + 0x0E, 0x02, 0x20, 0x84, 0x10, 0x82, 0x20, 0x38, 0x00, 0x38, 0x38, 0x38, + 0x08, 0xE0, 0x22, 0x08, 0x41, 0x08, 0x22, 0x03, 0x80, 0x07, 0x84, 0x04, + 0x02, 0x01, 0x00, 0xC1, 0xA2, 0x8A, 0x85, 0x43, 0x31, 0x8F, 0x60, 0xFF, + 0x6D, 0x20, 0x00, 0x44, 0x42, 0x21, 0x08, 0x84, 0x21, 0x08, 0x42, 0x10, + 0x42, 0x00, 0x00, 0x84, 0x10, 0x84, 0x21, 0x08, 0x46, 0x21, 0x10, 0x88, + 0x44, 0x00, 0x04, 0x02, 0x02, 0x1D, 0x13, 0xF0, 0x40, 0x50, 0x48, 0x44, + 0x00, 0x02, 0x00, 0x40, 0x08, 0x02, 0x00, 0x41, 0xFF, 0xC1, 0x00, 0x20, + 0x08, 0x01, 0x00, 0x20, 0x00, 0x1C, 0xE3, 0x18, 0x63, 0x08, 0x00, 0xFF, + 0xE0, 0x7F, 0x00, 0x00, 0x08, 0x00, 0x80, 0x04, 0x00, 0x40, 0x04, 0x00, + 0x60, 0x02, 0x00, 0x20, 0x03, 0x00, 0x10, 0x01, 0x00, 0x18, 0x00, 0x80, + 0x08, 0x00, 0x80, 0x04, 0x00, 0x40, 0x04, 0x00, 0x00, 0x07, 0x06, 0x23, + 0x04, 0x81, 0x40, 0x50, 0x14, 0x06, 0x02, 0x80, 0xA0, 0x28, 0x0A, 0x04, + 0x83, 0x11, 0x83, 0x80, 0x03, 0x03, 0x83, 0x83, 0x43, 0x20, 0x10, 0x08, + 0x08, 0x04, 0x02, 0x01, 0x01, 0x00, 0x80, 0x43, 0xFE, 0x01, 0xC0, 0x62, + 0x0C, 0x10, 0x81, 0x00, 0x10, 0x02, 0x00, 0x60, 0x0C, 0x01, 0x00, 0x20, + 0x0C, 0x01, 0x80, 0x20, 0x04, 0x04, 0xFF, 0xC0, 0x07, 0xC3, 0x0C, 0x00, + 0x80, 0x10, 0x06, 0x01, 0x81, 0xC0, 0x0C, 0x00, 0x40, 0x08, 0x01, 0x00, + 0x20, 0x09, 0x86, 0x0F, 0x00, 0x00, 0xC0, 0x50, 0x24, 0x12, 0x04, 0x82, + 0x21, 0x08, 0x82, 0x21, 0x10, 0x4F, 0xF8, 0x04, 0x01, 0x00, 0x80, 0xF8, + 0x0F, 0xE2, 0x00, 0x40, 0x08, 0x01, 0x00, 0x4E, 0x0E, 0x20, 0x02, 0x00, + 0x40, 0x08, 0x01, 0x00, 0x40, 0x19, 0x06, 0x1F, 0x00, 0x01, 0xE0, 0xC0, + 0x60, 0x18, 0x02, 0x00, 0x80, 0x13, 0xC5, 0x88, 0xE0, 0x98, 0x12, 0x02, + 0x40, 0x48, 0x10, 0x84, 0x0F, 0x00, 0xFF, 0xA0, 0x20, 0x08, 0x04, 0x01, + 0x00, 0x80, 0x20, 0x10, 0x04, 0x02, 0x00, 0x80, 0x40, 0x10, 0x08, 0x02, + 0x00, 0x07, 0x81, 0x08, 0x40, 0x90, 0x12, 0x02, 0x40, 0x84, 0x20, 0x78, + 0x30, 0x88, 0x0A, 0x01, 0x40, 0x28, 0x08, 0x82, 0x0F, 0x80, 0x07, 0x81, + 0x08, 0x40, 0x90, 0x12, 0x02, 0x40, 0xC8, 0x39, 0x8D, 0x1E, 0x40, 0x08, + 0x02, 0x00, 0xC0, 0x30, 0x18, 0x3E, 0x00, 0x19, 0xCC, 0x00, 0x00, 0x0C, + 0xE6, 0x00, 0x06, 0x1C, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x30, 0xE1, 0x86, + 0x08, 0x00, 0x00, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x06, 0x00, 0x30, + 0x00, 0xC0, 0x06, 0x00, 0x18, 0x00, 0xC0, 0x7F, 0xF8, 0x00, 0x00, 0x01, + 0xFF, 0xE0, 0x18, 0x00, 0xC0, 0x03, 0x00, 0x18, 0x00, 0x60, 0x03, 0x00, + 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, 0x3E, 0xC3, 0x81, 0x01, 0x03, + 0x06, 0x18, 0x20, 0x20, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0x07, 0x82, 0x31, + 0x04, 0x81, 0x20, 0x48, 0x74, 0x65, 0x21, 0x48, 0x92, 0x28, 0x7A, 0x00, + 0x80, 0x20, 0x04, 0x00, 0xF8, 0x07, 0xE0, 0x02, 0x80, 0x0A, 0x00, 0x48, + 0x01, 0x20, 0x08, 0x40, 0x41, 0x01, 0x04, 0x0F, 0xF0, 0x20, 0x41, 0x01, + 0x04, 0x02, 0x20, 0x0B, 0xE1, 0xF0, 0x1F, 0xF0, 0x40, 0xC2, 0x02, 0x10, + 0x10, 0x81, 0x84, 0x18, 0x7F, 0x82, 0x02, 0x10, 0x08, 0x80, 0x44, 0x02, + 0x60, 0x22, 0x03, 0x7F, 0xE0, 0x07, 0x91, 0x87, 0x20, 0x34, 0x02, 0x40, + 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x04, 0x04, 0x61, + 0x81, 0xE0, 0x1F, 0xE0, 0x41, 0x82, 0x06, 0x10, 0x11, 0x00, 0x88, 0x04, + 0x40, 0x22, 0x01, 0x10, 0x11, 0x00, 0x88, 0x08, 0x40, 0xC2, 0x0C, 0x7F, + 0x80, 0x1F, 0xFC, 0x20, 0x10, 0x80, 0x82, 0x00, 0x08, 0x00, 0x22, 0x01, + 0xF8, 0x04, 0x20, 0x10, 0x00, 0x40, 0x01, 0x01, 0x0C, 0x04, 0x20, 0x13, + 0xFF, 0xC0, 0x1F, 0xFC, 0x20, 0x10, 0x80, 0x42, 0x01, 0x08, 0x00, 0x22, + 0x01, 0xF8, 0x04, 0x20, 0x10, 0x00, 0x40, 0x01, 0x00, 0x0C, 0x00, 0x20, + 0x03, 0xF8, 0x00, 0x07, 0xD0, 0x83, 0x30, 0x12, 0x00, 0x40, 0x04, 0x00, + 0x80, 0x08, 0x00, 0x83, 0xE8, 0x04, 0x80, 0x4C, 0x04, 0x60, 0x41, 0xF8, + 0x0F, 0x3C, 0x08, 0x10, 0x20, 0x20, 0x40, 0x40, 0x81, 0x01, 0x02, 0x03, + 0xFC, 0x08, 0x08, 0x10, 0x10, 0x20, 0x40, 0x40, 0x80, 0x81, 0x02, 0x02, + 0x1F, 0x1E, 0x00, 0x3F, 0xE0, 0x40, 0x08, 0x01, 0x00, 0x20, 0x08, 0x01, + 0x00, 0x20, 0x04, 0x00, 0x80, 0x20, 0x04, 0x00, 0x81, 0xFF, 0x00, 0x03, + 0xFE, 0x00, 0x20, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, + 0x00, 0x20, 0x40, 0x40, 0x80, 0x81, 0x01, 0x02, 0x04, 0x06, 0x10, 0x07, + 0xC0, 0x00, 0x1F, 0x1E, 0x10, 0x10, 0x20, 0xC0, 0x43, 0x00, 0x88, 0x01, + 0x20, 0x07, 0xC0, 0x0C, 0x40, 0x10, 0x40, 0x20, 0x80, 0x41, 0x01, 0x81, + 0x02, 0x02, 0x1F, 0x87, 0x00, 0x3F, 0x80, 0x40, 0x04, 0x00, 0x40, 0x08, + 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x01, 0x01, 0x10, 0x11, 0x02, 0x10, + 0x2F, 0xFE, 0x1C, 0x03, 0x85, 0x03, 0x02, 0x82, 0x81, 0x41, 0x40, 0xA1, + 0x20, 0x89, 0x30, 0x44, 0x90, 0x22, 0x88, 0x11, 0x44, 0x08, 0x42, 0x08, + 0x03, 0x04, 0x01, 0x02, 0x00, 0x87, 0xC3, 0xE0, 0x3C, 0x3E, 0x18, 0x08, + 0x38, 0x20, 0x50, 0x41, 0x20, 0x82, 0x61, 0x04, 0x42, 0x08, 0x88, 0x10, + 0x90, 0x41, 0x20, 0x83, 0x41, 0x02, 0x82, 0x06, 0x1F, 0x04, 0x00, 0x03, + 0xC0, 0x61, 0x84, 0x04, 0x40, 0x14, 0x00, 0xA0, 0x06, 0x00, 0x30, 0x01, + 0x80, 0x14, 0x00, 0xA0, 0x08, 0x80, 0x86, 0x18, 0x0F, 0x00, 0x1F, 0xE0, + 0x40, 0x82, 0x02, 0x10, 0x10, 0x80, 0x84, 0x08, 0x40, 0x83, 0xF8, 0x10, + 0x00, 0x80, 0x04, 0x00, 0x60, 0x02, 0x00, 0x7F, 0x00, 0x03, 0xC0, 0x61, + 0x84, 0x04, 0x40, 0x14, 0x00, 0xA0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x14, + 0x00, 0xA0, 0x08, 0x80, 0x86, 0x18, 0x1F, 0x00, 0x40, 0x0F, 0xC4, 0x41, + 0xC0, 0x1F, 0xE0, 0x40, 0x82, 0x02, 0x10, 0x10, 0x80, 0x84, 0x08, 0x60, + 0x83, 0xF8, 0x10, 0xC0, 0x82, 0x04, 0x08, 0x40, 0x42, 0x03, 0x7E, 0x0C, + 0x07, 0xA3, 0x0C, 0x40, 0x90, 0x12, 0x00, 0x40, 0x06, 0x00, 0x3C, 0x00, + 0x40, 0x0A, 0x01, 0x40, 0x4C, 0x11, 0x7C, 0x00, 0xFF, 0xE8, 0x42, 0x84, + 0x20, 0x40, 0x04, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x10, + 0x01, 0x00, 0x10, 0x0F, 0xE0, 0xF8, 0xF9, 0x00, 0x88, 0x08, 0x80, 0x44, + 0x02, 0x20, 0x11, 0x01, 0x08, 0x08, 0x80, 0x44, 0x02, 0x20, 0x31, 0x01, + 0x04, 0x30, 0x1E, 0x00, 0xF8, 0x7D, 0x00, 0x42, 0x01, 0x08, 0x08, 0x20, + 0x40, 0x81, 0x02, 0x08, 0x08, 0x20, 0x11, 0x00, 0x48, 0x01, 0x20, 0x05, + 0x00, 0x14, 0x00, 0x60, 0x00, 0xF8, 0x7D, 0x00, 0x44, 0x01, 0x11, 0x84, + 0x46, 0x21, 0x18, 0x84, 0xA2, 0x12, 0x90, 0x91, 0x42, 0x45, 0x0A, 0x14, + 0x28, 0x60, 0xC1, 0x83, 0x06, 0x00, 0x1E, 0x1E, 0x10, 0x10, 0x10, 0x40, + 0x21, 0x00, 0x24, 0x00, 0x78, 0x00, 0x60, 0x01, 0xC0, 0x06, 0x80, 0x09, + 0x80, 0x21, 0x00, 0x81, 0x02, 0x02, 0x1E, 0x1F, 0x00, 0xF0, 0xF4, 0x04, + 0x20, 0x82, 0x18, 0x11, 0x01, 0x20, 0x1C, 0x00, 0x80, 0x08, 0x00, 0x80, + 0x10, 0x01, 0x00, 0x10, 0x0F, 0xE0, 0x0F, 0xF1, 0x01, 0x10, 0x21, 0x04, + 0x00, 0x80, 0x10, 0x02, 0x00, 0x40, 0x0C, 0x01, 0x82, 0x10, 0x22, 0x04, + 0x40, 0x47, 0xFC, 0x0E, 0x20, 0x40, 0x81, 0x02, 0x08, 0x10, 0x20, 0x40, + 0x82, 0x04, 0x08, 0x10, 0x20, 0x81, 0xE0, 0x84, 0x20, 0x84, 0x20, 0x84, + 0x21, 0x04, 0x21, 0x08, 0x21, 0x08, 0x40, 0x1E, 0x04, 0x08, 0x20, 0x40, + 0x81, 0x02, 0x04, 0x10, 0x20, 0x40, 0x81, 0x02, 0x08, 0x11, 0xE0, 0x04, + 0x06, 0x04, 0x84, 0x44, 0x14, 0x0C, 0xFF, 0xFE, 0x99, 0x90, 0x1F, 0xC0, + 0x06, 0x00, 0x20, 0x02, 0x1F, 0xE6, 0x04, 0xC0, 0x48, 0x04, 0x81, 0xC7, + 0xEF, 0x18, 0x00, 0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x09, 0xF0, 0x50, + 0xC3, 0x03, 0x10, 0x08, 0x80, 0x48, 0x02, 0x40, 0x23, 0x03, 0x1C, 0x33, + 0xBE, 0x00, 0x0F, 0xD3, 0x07, 0x60, 0x24, 0x02, 0x80, 0x08, 0x00, 0x80, + 0x08, 0x06, 0x41, 0xC3, 0xF0, 0x00, 0x38, 0x00, 0x40, 0x02, 0x00, 0x20, + 0x01, 0x07, 0xC8, 0x43, 0x44, 0x0E, 0x40, 0x24, 0x01, 0x20, 0x09, 0x00, + 0xC8, 0x0E, 0x20, 0xE0, 0xF9, 0xC0, 0x0F, 0x86, 0x09, 0x00, 0xA0, 0x1F, + 0xFF, 0x00, 0x20, 0x06, 0x00, 0x60, 0xC7, 0xE0, 0x01, 0xF8, 0x10, 0x01, + 0x00, 0x08, 0x00, 0x40, 0x1F, 0xF0, 0x20, 0x01, 0x00, 0x08, 0x00, 0x40, + 0x04, 0x00, 0x20, 0x01, 0x00, 0x08, 0x03, 0xFE, 0x00, 0x0F, 0x31, 0x86, + 0x10, 0x10, 0x80, 0x88, 0x04, 0x40, 0x22, 0x02, 0x10, 0x10, 0x43, 0x81, + 0xE4, 0x00, 0x40, 0x02, 0x00, 0x20, 0x3E, 0x00, 0x1C, 0x00, 0x20, 0x03, + 0x00, 0x10, 0x00, 0x80, 0x05, 0xF0, 0x30, 0xC3, 0x02, 0x10, 0x10, 0x80, + 0x84, 0x0C, 0x20, 0x63, 0x02, 0x10, 0x13, 0xE3, 0xE0, 0x01, 0x80, 0x40, + 0x10, 0x00, 0x00, 0x07, 0xC0, 0x20, 0x08, 0x02, 0x00, 0x80, 0x20, 0x10, + 0x04, 0x01, 0x0F, 0xFC, 0x00, 0x40, 0x10, 0x0C, 0x00, 0x00, 0x07, 0xF0, + 0x04, 0x01, 0x00, 0x40, 0x20, 0x08, 0x02, 0x00, 0x80, 0x20, 0x10, 0x04, + 0x01, 0x00, 0x8F, 0xC0, 0x18, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x01, + 0x1F, 0x10, 0x81, 0x30, 0x14, 0x01, 0xC0, 0x26, 0x02, 0x20, 0x21, 0x02, + 0x08, 0xE1, 0xE0, 0x0F, 0x80, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x20, + 0x08, 0x02, 0x00, 0x80, 0x20, 0x10, 0x04, 0x01, 0x0F, 0xFC, 0x3B, 0xB8, + 0x33, 0x91, 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x12, 0x10, 0x48, 0x42, + 0x21, 0x0B, 0xC6, 0x30, 0x19, 0xE0, 0xE3, 0x08, 0x11, 0x01, 0x10, 0x11, + 0x02, 0x10, 0x21, 0x02, 0x20, 0x2F, 0x87, 0x0F, 0x86, 0x19, 0x80, 0xA0, + 0x18, 0x03, 0x00, 0x60, 0x14, 0x06, 0x61, 0x87, 0xC0, 0x19, 0xF0, 0x28, + 0x20, 0xC0, 0x42, 0x01, 0x10, 0x04, 0x40, 0x11, 0x00, 0x86, 0x06, 0x14, + 0x30, 0xCF, 0x02, 0x00, 0x08, 0x00, 0x20, 0x03, 0xF0, 0x00, 0x0F, 0x39, + 0x85, 0x18, 0x18, 0x80, 0x88, 0x04, 0x40, 0x22, 0x01, 0x18, 0x18, 0x63, + 0x81, 0xE4, 0x00, 0x20, 0x01, 0x00, 0x10, 0x07, 0xE0, 0x1C, 0x78, 0x2C, + 0x01, 0x80, 0x18, 0x00, 0x80, 0x04, 0x00, 0x20, 0x02, 0x00, 0x10, 0x07, + 0xFC, 0x00, 0x0F, 0x44, 0x32, 0x04, 0x80, 0x1E, 0x00, 0x60, 0x0A, 0x02, + 0xC1, 0x2F, 0x80, 0x10, 0x08, 0x04, 0x02, 0x0F, 0xF9, 0x00, 0x80, 0x40, + 0x20, 0x20, 0x10, 0x08, 0x04, 0x19, 0xF0, 0xE0, 0xF2, 0x02, 0x40, 0x24, + 0x02, 0x40, 0x24, 0x06, 0x40, 0x44, 0x04, 0x41, 0xC3, 0xE6, 0xF8, 0xFA, + 0x01, 0x08, 0x10, 0x41, 0x02, 0x08, 0x10, 0x80, 0x48, 0x02, 0x40, 0x14, + 0x00, 0xC0, 0x00, 0xE0, 0x7A, 0x01, 0x10, 0x08, 0x8C, 0x84, 0xA4, 0x25, + 0x21, 0x4A, 0x0A, 0x50, 0x63, 0x02, 0x18, 0x00, 0x1E, 0x3C, 0x20, 0x40, + 0x46, 0x00, 0xB0, 0x03, 0x00, 0x0E, 0x00, 0xC8, 0x06, 0x10, 0x20, 0x23, + 0xE3, 0xC0, 0x3C, 0x3C, 0x40, 0x20, 0x81, 0x02, 0x08, 0x08, 0x20, 0x31, + 0x00, 0x48, 0x01, 0x40, 0x05, 0x00, 0x08, 0x00, 0x40, 0x02, 0x00, 0x08, + 0x03, 0xF0, 0x00, 0x3F, 0xC4, 0x18, 0x06, 0x01, 0x80, 0x60, 0x10, 0x04, + 0x01, 0x00, 0x40, 0x9F, 0xF0, 0x06, 0x10, 0x20, 0x41, 0x02, 0x04, 0x08, + 0x21, 0x80, 0x81, 0x02, 0x08, 0x10, 0x20, 0x40, 0xC0, 0x01, 0x11, 0x12, + 0x22, 0x24, 0x44, 0x44, 0x88, 0x80, 0x0C, 0x08, 0x10, 0x20, 0x40, 0x82, + 0x04, 0x08, 0x0C, 0x20, 0x81, 0x02, 0x04, 0x08, 0x21, 0x80, 0x38, 0x28, + 0x88, 0x0E, 0x00}; + +const GFXglyph FreeMonoOblique12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 14, 0, 1}, // 0x20 ' ' + {0, 4, 15, 14, 6, -14}, // 0x21 '!' + {8, 8, 7, 14, 5, -14}, // 0x22 '"' + {15, 11, 16, 14, 3, -14}, // 0x23 '#' + {37, 10, 18, 14, 4, -15}, // 0x24 '$' + {60, 11, 15, 14, 3, -14}, // 0x25 '%' + {81, 9, 12, 14, 3, -11}, // 0x26 '&' + {95, 3, 7, 14, 8, -14}, // 0x27 ''' + {98, 5, 18, 14, 8, -14}, // 0x28 '(' + {110, 5, 18, 14, 4, -14}, // 0x29 ')' + {122, 9, 9, 14, 5, -14}, // 0x2A '*' + {133, 11, 11, 14, 3, -11}, // 0x2B '+' + {149, 6, 7, 14, 3, -3}, // 0x2C ',' + {155, 11, 1, 14, 3, -6}, // 0x2D '-' + {157, 3, 3, 14, 6, -2}, // 0x2E '.' + {159, 13, 18, 14, 2, -15}, // 0x2F '/' + {189, 10, 15, 14, 4, -14}, // 0x30 '0' + {208, 9, 15, 14, 3, -14}, // 0x31 '1' + {225, 12, 15, 14, 2, -14}, // 0x32 '2' + {248, 11, 15, 14, 3, -14}, // 0x33 '3' + {269, 10, 15, 14, 3, -14}, // 0x34 '4' + {288, 11, 15, 14, 3, -14}, // 0x35 '5' + {309, 11, 15, 14, 4, -14}, // 0x36 '6' + {330, 10, 15, 14, 5, -14}, // 0x37 '7' + {349, 11, 15, 14, 3, -14}, // 0x38 '8' + {370, 11, 15, 14, 3, -14}, // 0x39 '9' + {391, 5, 10, 14, 5, -9}, // 0x3A ':' + {398, 7, 13, 14, 3, -9}, // 0x3B ';' + {410, 12, 11, 14, 3, -11}, // 0x3C '<' + {427, 13, 4, 14, 2, -8}, // 0x3D '=' + {434, 12, 11, 14, 2, -11}, // 0x3E '>' + {451, 8, 14, 14, 6, -13}, // 0x3F '?' + {465, 10, 16, 14, 3, -14}, // 0x40 '@' + {485, 14, 14, 14, 0, -13}, // 0x41 'A' + {510, 13, 14, 14, 1, -13}, // 0x42 'B' + {533, 12, 14, 14, 3, -13}, // 0x43 'C' + {554, 13, 14, 14, 1, -13}, // 0x44 'D' + {577, 14, 14, 14, 1, -13}, // 0x45 'E' + {602, 14, 14, 14, 1, -13}, // 0x46 'F' + {627, 12, 14, 14, 3, -13}, // 0x47 'G' + {648, 15, 14, 14, 1, -13}, // 0x48 'H' + {675, 11, 14, 14, 3, -13}, // 0x49 'I' + {695, 15, 14, 14, 2, -13}, // 0x4A 'J' + {722, 15, 14, 14, 1, -13}, // 0x4B 'K' + {749, 12, 14, 14, 2, -13}, // 0x4C 'L' + {770, 17, 14, 14, 0, -13}, // 0x4D 'M' + {800, 15, 14, 14, 1, -13}, // 0x4E 'N' + {827, 13, 14, 14, 2, -13}, // 0x4F 'O' + {850, 13, 14, 14, 1, -13}, // 0x50 'P' + {873, 13, 17, 14, 2, -13}, // 0x51 'Q' + {901, 13, 14, 14, 1, -13}, // 0x52 'R' + {924, 11, 14, 14, 3, -13}, // 0x53 'S' + {944, 12, 14, 14, 4, -13}, // 0x54 'T' + {965, 13, 14, 14, 3, -13}, // 0x55 'U' + {988, 14, 14, 14, 3, -13}, // 0x56 'V' + {1013, 14, 14, 14, 3, -13}, // 0x57 'W' + {1038, 15, 14, 14, 1, -13}, // 0x58 'X' + {1065, 12, 14, 14, 4, -13}, // 0x59 'Y' + {1086, 12, 14, 14, 2, -13}, // 0x5A 'Z' + {1107, 7, 18, 14, 6, -14}, // 0x5B '[' + {1123, 5, 18, 14, 6, -15}, // 0x5C '\' + {1135, 7, 18, 14, 3, -14}, // 0x5D ']' + {1151, 9, 6, 14, 5, -14}, // 0x5E '^' + {1158, 15, 1, 14, -1, 3}, // 0x5F '_' + {1160, 3, 4, 14, 6, -15}, // 0x60 '`' + {1162, 12, 10, 14, 2, -9}, // 0x61 'a' + {1177, 13, 15, 14, 1, -14}, // 0x62 'b' + {1202, 12, 10, 14, 3, -9}, // 0x63 'c' + {1217, 13, 15, 14, 2, -14}, // 0x64 'd' + {1242, 11, 10, 14, 3, -9}, // 0x65 'e' + {1256, 13, 15, 14, 3, -14}, // 0x66 'f' + {1281, 13, 14, 14, 3, -9}, // 0x67 'g' + {1304, 13, 15, 14, 1, -14}, // 0x68 'h' + {1329, 10, 15, 14, 2, -14}, // 0x69 'i' + {1348, 10, 19, 14, 2, -14}, // 0x6A 'j' + {1372, 12, 15, 14, 2, -14}, // 0x6B 'k' + {1395, 10, 15, 14, 2, -14}, // 0x6C 'l' + {1414, 14, 10, 14, 0, -9}, // 0x6D 'm' + {1432, 12, 10, 14, 1, -9}, // 0x6E 'n' + {1447, 11, 10, 14, 3, -9}, // 0x6F 'o' + {1461, 14, 14, 14, 0, -9}, // 0x70 'p' + {1486, 13, 14, 14, 3, -9}, // 0x71 'q' + {1509, 13, 10, 14, 2, -9}, // 0x72 'r' + {1526, 10, 10, 14, 3, -9}, // 0x73 's' + {1539, 9, 14, 14, 3, -13}, // 0x74 't' + {1555, 12, 10, 14, 2, -9}, // 0x75 'u' + {1570, 13, 10, 14, 3, -9}, // 0x76 'v' + {1587, 13, 10, 14, 3, -9}, // 0x77 'w' + {1604, 14, 10, 14, 1, -9}, // 0x78 'x' + {1622, 14, 14, 14, 1, -9}, // 0x79 'y' + {1647, 11, 10, 14, 3, -9}, // 0x7A 'z' + {1661, 7, 18, 14, 5, -14}, // 0x7B '{' + {1677, 4, 17, 14, 6, -13}, // 0x7C '|' + {1686, 7, 18, 14, 4, -14}, // 0x7D '}' + {1702, 11, 3, 14, 3, -7}}; // 0x7E '~' + +const GFXfont FreeMonoOblique12pt7b PROGMEM = { + (uint8_t *)FreeMonoOblique12pt7bBitmaps, + (GFXglyph *)FreeMonoOblique12pt7bGlyphs, 0x20, 0x7E, 24}; + +// Approx. 2379 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique18pt7b.h new file mode 100644 index 0000000..93fa1cf --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique18pt7b.h @@ -0,0 +1,397 @@ +const uint8_t FreeMonoOblique18pt7bBitmaps[] PROGMEM = { + 0x00, 0x1C, 0x38, 0x70, 0xC1, 0x83, 0x06, 0x18, 0x30, 0x60, 0xC1, 0x02, + 0x04, 0x00, 0x00, 0x01, 0xC7, 0x8F, 0x1C, 0x00, 0x78, 0x7B, 0xC3, 0xFC, + 0x3D, 0xE1, 0xEF, 0x0F, 0x70, 0x73, 0x83, 0x98, 0x18, 0xC0, 0xC6, 0x06, + 0x00, 0x00, 0x8C, 0x01, 0x18, 0x06, 0x20, 0x08, 0x40, 0x11, 0x80, 0x62, + 0x00, 0xC4, 0x01, 0x18, 0x02, 0x30, 0x7F, 0xFC, 0x10, 0x80, 0x23, 0x00, + 0xC4, 0x01, 0x88, 0x3F, 0xFF, 0x04, 0x60, 0x18, 0x80, 0x21, 0x00, 0x46, + 0x01, 0x88, 0x03, 0x10, 0x04, 0x60, 0x08, 0xC0, 0x31, 0x00, 0x00, 0x30, + 0x00, 0x20, 0x00, 0x20, 0x00, 0xF9, 0x03, 0x0F, 0x06, 0x03, 0x04, 0x03, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x03, 0xC0, 0x00, 0x78, + 0x00, 0x0C, 0x00, 0x04, 0x00, 0x04, 0x40, 0x04, 0x40, 0x08, 0x40, 0x18, + 0xF0, 0x60, 0x9F, 0x80, 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x03, 0xC0, 0x0C, 0x60, 0x08, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x10, 0x40, 0x18, 0x80, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x78, 0x07, 0xC0, + 0x3C, 0x00, 0xE0, 0x00, 0x01, 0xE0, 0x02, 0x18, 0x04, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x10, 0x0C, 0x20, 0x07, 0xC0, 0x01, 0xF0, 0x11, 0x81, + 0x00, 0x10, 0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x80, 0x04, 0x00, 0xF0, + 0x09, 0x86, 0x84, 0x48, 0x32, 0x40, 0xA2, 0x07, 0x10, 0x30, 0x43, 0x81, + 0xE7, 0x80, 0x7B, 0xFD, 0xEF, 0x73, 0x98, 0xC6, 0x00, 0x01, 0x02, 0x06, + 0x0C, 0x0C, 0x18, 0x10, 0x30, 0x30, 0x60, 0x60, 0x60, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x40, 0x60, 0x60, 0x20, 0x04, 0x06, + 0x06, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, + 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x18, 0x10, 0x30, 0x60, 0x40, 0xC0, 0x01, + 0x00, 0x04, 0x00, 0x10, 0x00, 0xC6, 0xE3, 0xF8, 0x7E, 0x00, 0x70, 0x03, + 0x40, 0x19, 0x80, 0xC2, 0x06, 0x0C, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x02, + 0x00, 0x04, 0x00, 0x08, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0xFF, 0xFE, + 0x02, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x02, + 0x00, 0x04, 0x00, 0x0F, 0x87, 0x87, 0x83, 0x83, 0xC1, 0xC1, 0xC0, 0xC0, + 0xE0, 0x60, 0x00, 0xFF, 0xFF, 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x04, 0x00, + 0x01, 0x80, 0x00, 0x60, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0x20, 0x00, 0x0C, 0x00, 0x03, + 0x00, 0x00, 0x40, 0x00, 0x18, 0x00, 0x06, 0x00, 0x00, 0x80, 0x00, 0x20, + 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00, 0x08, 0x00, 0x00, 0x01, + 0xF0, 0x18, 0x60, 0x80, 0x86, 0x01, 0x10, 0x04, 0x80, 0x12, 0x00, 0x50, + 0x01, 0x40, 0x0D, 0x00, 0x24, 0x00, 0xA0, 0x02, 0x80, 0x1A, 0x00, 0x48, + 0x01, 0x20, 0x0C, 0x80, 0x22, 0x01, 0x84, 0x0C, 0x18, 0x60, 0x3E, 0x00, + 0x00, 0x60, 0x07, 0x00, 0x68, 0x06, 0x40, 0xE4, 0x04, 0x20, 0x01, 0x00, + 0x08, 0x00, 0x40, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08, 0x00, 0x80, 0x04, + 0x00, 0x20, 0x01, 0x00, 0x08, 0x00, 0x80, 0x04, 0x0F, 0xFF, 0x80, 0x00, + 0x3C, 0x00, 0x61, 0x80, 0x40, 0x40, 0x40, 0x10, 0x60, 0x08, 0x00, 0x04, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x00, 0x07, 0x00, 0x07, + 0x00, 0x06, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0x0C, + 0x00, 0x1C, 0x01, 0x1C, 0x00, 0x8F, 0xFF, 0xC0, 0x00, 0xFC, 0x03, 0x06, + 0x06, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x0C, 0x00, 0xF0, 0x00, 0x18, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x40, 0x18, 0x70, 0x30, + 0x0F, 0xC0, 0x00, 0x1C, 0x00, 0xD0, 0x06, 0x80, 0x32, 0x00, 0x88, 0x04, + 0x20, 0x30, 0x81, 0x84, 0x04, 0x10, 0x20, 0x41, 0x81, 0x0C, 0x08, 0x60, + 0x21, 0x00, 0x8F, 0xFF, 0x80, 0x18, 0x00, 0x40, 0x01, 0x00, 0x04, 0x00, + 0x10, 0x07, 0xE0, 0x03, 0xFF, 0x03, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, + 0x40, 0x00, 0x20, 0x00, 0x30, 0x00, 0x1B, 0xE0, 0x0E, 0x0C, 0x00, 0x02, + 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x04, 0x60, 0x04, 0x18, 0x04, 0x06, 0x0C, 0x00, 0xF8, 0x00, + 0x00, 0x3F, 0x00, 0xC0, 0x03, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x20, 0x00, 0x40, 0x00, 0x43, 0xE0, 0x4C, 0x30, 0xB0, 0x18, + 0xE0, 0x08, 0xC0, 0x08, 0x80, 0x08, 0x80, 0x08, 0x80, 0x10, 0xC0, 0x10, + 0x40, 0x20, 0x20, 0xC0, 0x1F, 0x00, 0xFF, 0xFC, 0x00, 0xE0, 0x04, 0x00, + 0x60, 0x02, 0x00, 0x30, 0x01, 0x00, 0x18, 0x00, 0x80, 0x0C, 0x00, 0x40, + 0x06, 0x00, 0x20, 0x03, 0x00, 0x10, 0x01, 0x80, 0x08, 0x00, 0xC0, 0x04, + 0x00, 0x60, 0x02, 0x00, 0x00, 0x00, 0xF0, 0x06, 0x18, 0x10, 0x18, 0x40, + 0x11, 0x00, 0x22, 0x00, 0x44, 0x00, 0x88, 0x02, 0x18, 0x08, 0x18, 0x60, + 0x1F, 0x80, 0xC1, 0x82, 0x01, 0x88, 0x01, 0x20, 0x02, 0x40, 0x04, 0x80, + 0x09, 0x00, 0x23, 0x00, 0x83, 0x06, 0x01, 0xF0, 0x00, 0x00, 0xF0, 0x06, + 0x18, 0x10, 0x10, 0x40, 0x30, 0x80, 0x22, 0x00, 0x44, 0x00, 0x88, 0x03, + 0x10, 0x0E, 0x30, 0x34, 0x30, 0xD0, 0x3E, 0x20, 0x00, 0x40, 0x01, 0x00, + 0x02, 0x00, 0x08, 0x00, 0x20, 0x00, 0xC0, 0x02, 0x00, 0x18, 0x0F, 0xC0, + 0x00, 0x1C, 0x7C, 0xF9, 0xF1, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xC7, 0xCF, + 0x9F, 0x1C, 0x00, 0x01, 0xC0, 0x7C, 0x0F, 0x81, 0xF0, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x07, 0x81, 0xE0, 0x3C, 0x0F, 0x01, + 0xC0, 0x70, 0x0E, 0x03, 0x80, 0x60, 0x00, 0x00, 0x01, 0x80, 0x03, 0x80, + 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x00, + 0xE0, 0x00, 0x1C, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x07, + 0x00, 0x00, 0xE0, 0x00, 0x38, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x18, 0x00, 0x03, 0x80, + 0x00, 0x38, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x0E, 0x00, 0x00, 0xE0, + 0x00, 0x0E, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, + 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x1F, 0xCE, 0x06, 0x80, + 0x38, 0x01, 0x80, 0x10, 0x01, 0x00, 0x20, 0x04, 0x01, 0x80, 0xF0, 0x18, + 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0F, 0x80, 0xF8, + 0x07, 0x00, 0x01, 0xF0, 0x0C, 0x30, 0x30, 0x30, 0x40, 0x21, 0x00, 0x44, + 0x00, 0x88, 0x01, 0x10, 0x1E, 0x40, 0xC4, 0x86, 0x11, 0x08, 0x22, 0x20, + 0x48, 0x40, 0x90, 0x82, 0x21, 0x84, 0x40, 0xFC, 0x80, 0x01, 0x00, 0x02, + 0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x18, 0x07, 0xC0, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x68, 0x00, 0x06, 0x40, 0x00, 0x32, 0x00, 0x03, 0x10, 0x00, + 0x10, 0x80, 0x01, 0x84, 0x00, 0x18, 0x10, 0x00, 0xC0, 0x80, 0x0C, 0x04, + 0x00, 0x60, 0x20, 0x06, 0x01, 0x00, 0x3F, 0xFC, 0x02, 0x00, 0x20, 0x10, + 0x01, 0x01, 0x00, 0x08, 0x08, 0x00, 0x40, 0x80, 0x02, 0x0C, 0x00, 0x09, + 0xFC, 0x07, 0xF0, 0x0F, 0xFF, 0x00, 0x40, 0x60, 0x20, 0x0C, 0x08, 0x01, + 0x02, 0x00, 0x40, 0x80, 0x10, 0x40, 0x08, 0x10, 0x06, 0x04, 0x03, 0x01, + 0xFF, 0x80, 0x40, 0x38, 0x20, 0x02, 0x08, 0x00, 0x42, 0x00, 0x10, 0x80, + 0x04, 0x40, 0x01, 0x10, 0x00, 0x84, 0x00, 0x41, 0x00, 0x23, 0xFF, 0xF0, + 0x00, 0xFC, 0x40, 0xC1, 0xF0, 0xC0, 0x1C, 0x60, 0x06, 0x10, 0x00, 0x88, + 0x00, 0x24, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x30, 0x00, 0x08, 0x00, + 0x02, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, + 0x40, 0x06, 0x08, 0x03, 0x01, 0x83, 0x80, 0x3F, 0x00, 0x0F, 0xFE, 0x00, + 0x80, 0xC0, 0x20, 0x18, 0x10, 0x02, 0x04, 0x00, 0x41, 0x00, 0x10, 0x40, + 0x04, 0x20, 0x01, 0x08, 0x00, 0x42, 0x00, 0x10, 0x80, 0x08, 0x20, 0x02, + 0x10, 0x00, 0x84, 0x00, 0x21, 0x00, 0x10, 0x40, 0x08, 0x20, 0x06, 0x08, + 0x03, 0x02, 0x01, 0x83, 0xFF, 0x80, 0x0F, 0xFF, 0xE0, 0x10, 0x02, 0x02, + 0x00, 0x60, 0x20, 0x06, 0x02, 0x00, 0x60, 0x20, 0x00, 0x04, 0x00, 0x00, + 0x40, 0x80, 0x04, 0x10, 0x00, 0x7F, 0x00, 0x04, 0x10, 0x00, 0x81, 0x00, + 0x08, 0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x81, 0x00, 0x08, 0x10, 0x00, + 0x81, 0x00, 0x18, 0x10, 0x01, 0x8F, 0xFF, 0xF0, 0x0F, 0xFF, 0xF0, 0x10, + 0x03, 0x02, 0x00, 0x30, 0x20, 0x03, 0x02, 0x00, 0x20, 0x20, 0x00, 0x04, + 0x00, 0x00, 0x40, 0x80, 0x04, 0x10, 0x00, 0x7F, 0x00, 0x04, 0x10, 0x00, + 0x81, 0x00, 0x08, 0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0xFE, + 0x40, 0xC0, 0xF0, 0x40, 0x1C, 0x20, 0x03, 0x10, 0x00, 0x88, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x10, 0x00, 0x08, 0x00, 0x02, 0x01, + 0xFE, 0x80, 0x02, 0x20, 0x00, 0x88, 0x00, 0x22, 0x00, 0x08, 0x40, 0x04, + 0x18, 0x01, 0x03, 0x81, 0xC0, 0x3F, 0x80, 0x07, 0xE1, 0xF8, 0x08, 0x02, + 0x00, 0x80, 0x10, 0x04, 0x00, 0x80, 0x20, 0x04, 0x01, 0x00, 0x20, 0x18, + 0x02, 0x00, 0x80, 0x10, 0x04, 0x00, 0x80, 0x3F, 0xFC, 0x01, 0x00, 0x60, + 0x10, 0x02, 0x00, 0x80, 0x10, 0x04, 0x00, 0x80, 0x20, 0x04, 0x02, 0x00, + 0x40, 0x10, 0x02, 0x00, 0x80, 0x10, 0x04, 0x00, 0x81, 0xF8, 0x3F, 0x00, + 0x0F, 0xFF, 0x80, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x1F, 0xFF, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x04, + 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x0C, 0x04, 0x00, + 0x80, 0x40, 0x08, 0x08, 0x00, 0x80, 0x80, 0x08, 0x08, 0x01, 0x00, 0x80, + 0x10, 0x0C, 0x02, 0x00, 0x60, 0xC0, 0x01, 0xF0, 0x00, 0x0F, 0xE1, 0xF8, + 0x08, 0x03, 0x00, 0x80, 0x60, 0x04, 0x06, 0x00, 0x20, 0x60, 0x01, 0x06, + 0x00, 0x10, 0xC0, 0x00, 0x8C, 0x00, 0x04, 0xC0, 0x00, 0x2F, 0x80, 0x01, + 0x8E, 0x00, 0x18, 0x30, 0x00, 0x80, 0xC0, 0x04, 0x06, 0x00, 0x20, 0x10, + 0x02, 0x00, 0xC0, 0x10, 0x06, 0x00, 0x80, 0x30, 0x04, 0x00, 0x81, 0xFC, + 0x07, 0x80, 0x07, 0xFC, 0x00, 0x10, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, + 0x80, 0x00, 0x20, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x40, + 0x00, 0x10, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x10, 0x20, 0x04, + 0x08, 0x01, 0x04, 0x00, 0x81, 0x00, 0x20, 0x40, 0x0B, 0xFF, 0xFE, 0x0F, + 0x00, 0x1E, 0x03, 0x00, 0x38, 0x05, 0x00, 0x68, 0x04, 0x80, 0x68, 0x04, + 0x80, 0xC8, 0x04, 0x80, 0x90, 0x04, 0x81, 0x90, 0x08, 0x43, 0x10, 0x08, + 0x42, 0x10, 0x08, 0x46, 0x10, 0x08, 0x4C, 0x20, 0x10, 0x2C, 0x20, 0x10, + 0x38, 0x20, 0x10, 0x30, 0x20, 0x10, 0x00, 0x40, 0x10, 0x00, 0x40, 0x20, + 0x00, 0x40, 0x20, 0x00, 0x40, 0x20, 0x00, 0x40, 0xFC, 0x07, 0xE0, 0x1F, + 0x01, 0xFC, 0x0C, 0x00, 0x80, 0x78, 0x02, 0x01, 0xE0, 0x18, 0x04, 0x80, + 0x60, 0x13, 0x01, 0x00, 0x4C, 0x04, 0x03, 0x18, 0x10, 0x0C, 0x60, 0xC0, + 0x20, 0x83, 0x00, 0x83, 0x08, 0x06, 0x0C, 0x20, 0x18, 0x18, 0x80, 0x40, + 0x66, 0x01, 0x00, 0x98, 0x04, 0x03, 0x40, 0x30, 0x0D, 0x00, 0xC0, 0x14, + 0x02, 0x00, 0x70, 0x3F, 0x80, 0xC0, 0x00, 0xF8, 0x01, 0x83, 0x01, 0x00, + 0xC1, 0x00, 0x21, 0x00, 0x19, 0x00, 0x04, 0x80, 0x02, 0x80, 0x01, 0x40, + 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x28, 0x00, 0x14, 0x00, 0x12, + 0x00, 0x09, 0x80, 0x08, 0x40, 0x08, 0x30, 0x08, 0x0C, 0x18, 0x01, 0xF0, + 0x00, 0x0F, 0xFE, 0x00, 0x40, 0x60, 0x20, 0x0C, 0x08, 0x01, 0x02, 0x00, + 0x40, 0x80, 0x10, 0x40, 0x04, 0x10, 0x02, 0x04, 0x01, 0x01, 0x01, 0x80, + 0x7F, 0x80, 0x20, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x40, + 0x00, 0x10, 0x00, 0x04, 0x00, 0x01, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xF8, + 0x01, 0x83, 0x01, 0x00, 0xC1, 0x00, 0x21, 0x00, 0x19, 0x00, 0x05, 0x00, + 0x02, 0x80, 0x01, 0x40, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x28, + 0x00, 0x14, 0x00, 0x12, 0x00, 0x09, 0x80, 0x08, 0x40, 0x08, 0x30, 0x08, + 0x0C, 0x18, 0x03, 0xF0, 0x00, 0xC0, 0x01, 0xC0, 0x01, 0xFE, 0x18, 0xC0, + 0xF0, 0x0F, 0xFE, 0x00, 0x40, 0x60, 0x20, 0x0C, 0x08, 0x01, 0x02, 0x00, + 0x40, 0x80, 0x10, 0x40, 0x04, 0x10, 0x02, 0x04, 0x01, 0x01, 0x01, 0x80, + 0x7F, 0x80, 0x20, 0x60, 0x08, 0x0C, 0x02, 0x03, 0x80, 0x80, 0x60, 0x40, + 0x18, 0x10, 0x03, 0x04, 0x00, 0xC1, 0x00, 0x1B, 0xF8, 0x07, 0x00, 0x7E, + 0x40, 0x60, 0xF0, 0x20, 0x1C, 0x10, 0x02, 0x08, 0x00, 0x82, 0x00, 0x00, + 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xC0, 0x00, + 0x18, 0x00, 0x01, 0x00, 0x00, 0x44, 0x00, 0x11, 0x00, 0x04, 0x40, 0x02, + 0x38, 0x01, 0x0B, 0x81, 0x82, 0x3F, 0x80, 0x3F, 0xFF, 0xA0, 0x20, 0x50, + 0x10, 0x28, 0x08, 0x24, 0x08, 0x10, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x1F, + 0xFC, 0x00, 0x7E, 0x0F, 0xC4, 0x00, 0x42, 0x00, 0x10, 0x80, 0x08, 0x20, + 0x02, 0x08, 0x00, 0x82, 0x00, 0x21, 0x00, 0x08, 0x40, 0x04, 0x10, 0x01, + 0x04, 0x00, 0x41, 0x00, 0x10, 0x80, 0x0C, 0x20, 0x02, 0x08, 0x00, 0x82, + 0x00, 0x60, 0x80, 0x10, 0x10, 0x08, 0x06, 0x0C, 0x00, 0x7C, 0x00, 0xFE, + 0x03, 0xF9, 0x80, 0x02, 0x0C, 0x00, 0x30, 0x20, 0x01, 0x01, 0x00, 0x10, + 0x08, 0x01, 0x80, 0x60, 0x08, 0x03, 0x00, 0xC0, 0x18, 0x04, 0x00, 0x40, + 0x60, 0x02, 0x06, 0x00, 0x10, 0x20, 0x00, 0xC3, 0x00, 0x06, 0x10, 0x00, + 0x31, 0x80, 0x00, 0x88, 0x00, 0x04, 0x80, 0x00, 0x2C, 0x00, 0x01, 0xC0, + 0x00, 0x0E, 0x00, 0x00, 0x7F, 0x07, 0xF2, 0x00, 0x04, 0x20, 0x00, 0xC2, + 0x00, 0x08, 0x20, 0xC0, 0x82, 0x0C, 0x18, 0x21, 0xA1, 0x02, 0x1A, 0x10, + 0x23, 0x23, 0x04, 0x32, 0x30, 0x46, 0x22, 0x04, 0x62, 0x60, 0x4C, 0x26, + 0x04, 0xC2, 0x40, 0x58, 0x24, 0x05, 0x82, 0xC0, 0x70, 0x28, 0x07, 0x02, + 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x00, 0x0F, 0xC1, 0xF8, 0x30, 0x03, 0x00, + 0xC0, 0x30, 0x06, 0x03, 0x00, 0x18, 0x10, 0x00, 0xC1, 0x00, 0x03, 0x18, + 0x00, 0x09, 0x80, 0x00, 0x78, 0x00, 0x01, 0x80, 0x00, 0x1C, 0x00, 0x01, + 0xA0, 0x00, 0x19, 0x80, 0x01, 0x84, 0x00, 0x18, 0x30, 0x01, 0x80, 0xC0, + 0x08, 0x06, 0x00, 0x80, 0x18, 0x08, 0x00, 0xC1, 0xF8, 0x3F, 0x80, 0x7E, + 0x0F, 0xC4, 0x00, 0xC1, 0x80, 0x60, 0x20, 0x30, 0x0C, 0x08, 0x03, 0x04, + 0x00, 0x43, 0x00, 0x19, 0x80, 0x02, 0xC0, 0x00, 0xE0, 0x00, 0x10, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x08, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x40, 0x03, 0xFF, 0x80, 0x0F, 0xFF, 0x86, 0x00, + 0x82, 0x00, 0x81, 0x00, 0xC1, 0x80, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0x00, + 0xC0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x10, 0x60, 0x18, 0x20, 0x08, 0x20, 0x04, 0x20, 0x02, 0x30, 0x03, + 0x1F, 0xFF, 0x80, 0x07, 0xE0, 0x80, 0x10, 0x02, 0x00, 0xC0, 0x18, 0x02, + 0x00, 0x40, 0x18, 0x03, 0x00, 0x40, 0x08, 0x01, 0x00, 0x60, 0x0C, 0x01, + 0x00, 0x20, 0x04, 0x01, 0x80, 0x30, 0x04, 0x00, 0x80, 0x10, 0x06, 0x00, + 0xFC, 0x00, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, + 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x07, 0xE0, 0x0C, 0x01, 0x00, 0x20, 0x04, + 0x01, 0x80, 0x30, 0x04, 0x00, 0x80, 0x30, 0x06, 0x00, 0x80, 0x10, 0x02, + 0x00, 0xC0, 0x18, 0x02, 0x00, 0x40, 0x18, 0x03, 0x00, 0x40, 0x08, 0x03, + 0x00, 0x60, 0xF8, 0x00, 0x01, 0x00, 0x1C, 0x01, 0xB0, 0x19, 0x81, 0x86, + 0x18, 0x11, 0x80, 0xD8, 0x03, 0x80, 0x18, 0xFF, 0xFF, 0xF8, 0xC7, 0x1C, + 0x71, 0x80, 0x03, 0xF8, 0x0C, 0x0C, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x02, 0x07, 0xFC, 0x18, 0x0C, 0x20, 0x04, 0x40, 0x04, 0x80, 0x04, + 0x80, 0x08, 0x80, 0x38, 0xC0, 0xE8, 0x3F, 0x0F, 0x0F, 0x00, 0x00, 0x20, + 0x00, 0x04, 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0x04, 0x00, 0x00, 0x87, + 0xC0, 0x13, 0x0C, 0x06, 0x80, 0x40, 0xE0, 0x0C, 0x18, 0x00, 0x82, 0x00, + 0x10, 0xC0, 0x02, 0x10, 0x00, 0x42, 0x00, 0x08, 0x40, 0x02, 0x08, 0x00, + 0x43, 0x80, 0x10, 0x70, 0x04, 0x09, 0x83, 0x0F, 0x1F, 0x80, 0x01, 0xFC, + 0x83, 0x03, 0xC6, 0x00, 0xE4, 0x00, 0x22, 0x00, 0x12, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x18, 0x00, 0x64, + 0x00, 0x61, 0x81, 0xC0, 0x7F, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x30, 0x00, + 0x0C, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x60, 0x3F, 0x18, 0x10, 0x64, + 0x18, 0x0D, 0x08, 0x01, 0xC2, 0x00, 0x71, 0x00, 0x0C, 0x80, 0x02, 0x20, + 0x00, 0x88, 0x00, 0x62, 0x00, 0x18, 0x80, 0x0E, 0x20, 0x03, 0x04, 0x03, + 0x40, 0xC1, 0xB0, 0x1F, 0x8F, 0x00, 0x01, 0xF0, 0x0E, 0x0C, 0x18, 0x06, + 0x30, 0x02, 0x60, 0x01, 0x40, 0x01, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x60, 0x06, 0x30, 0x1C, 0x0F, 0xE0, + 0x00, 0x1F, 0xE0, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00, 0x08, 0x00, + 0x02, 0x00, 0x07, 0xFF, 0xC0, 0x08, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, + 0x08, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x00, 0x80, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0F, + 0xFF, 0x00, 0x03, 0xE3, 0xE1, 0x83, 0x60, 0x40, 0x38, 0x10, 0x03, 0x04, + 0x00, 0x60, 0x80, 0x0C, 0x20, 0x01, 0x84, 0x00, 0x20, 0x80, 0x04, 0x10, + 0x01, 0x82, 0x00, 0x30, 0x60, 0x0C, 0x04, 0x02, 0x80, 0x61, 0x90, 0x07, + 0xC6, 0x00, 0x00, 0xC0, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, + 0x30, 0x00, 0x0C, 0x00, 0xFE, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x40, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x23, 0xE0, 0x0B, + 0x0C, 0x05, 0x00, 0x81, 0x80, 0x20, 0x40, 0x08, 0x10, 0x02, 0x08, 0x00, + 0x82, 0x00, 0x60, 0x80, 0x18, 0x20, 0x06, 0x10, 0x01, 0x84, 0x00, 0x61, + 0x00, 0x30, 0x40, 0x0C, 0xFC, 0x1F, 0xC0, 0x00, 0x30, 0x00, 0x60, 0x00, + 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x20, + 0x00, 0x40, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x08, 0x00, 0x10, 0x1F, 0xFF, + 0x80, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xFE, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, + 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x20, 0x00, + 0x40, 0x00, 0x80, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, + 0x00, 0x80, 0x03, 0x00, 0x0C, 0x0F, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x60, + 0x00, 0x10, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x30, 0xFC, + 0x08, 0x18, 0x02, 0x0C, 0x00, 0x8C, 0x00, 0x66, 0x00, 0x1B, 0x00, 0x05, + 0x80, 0x01, 0xB0, 0x00, 0x46, 0x00, 0x31, 0xC0, 0x0C, 0x30, 0x02, 0x06, + 0x00, 0x80, 0xC0, 0x60, 0x30, 0xF8, 0x1F, 0x80, 0x01, 0xF8, 0x00, 0x20, + 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x20, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x20, + 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x04, 0x00, 0x08, 0x0F, 0xFF, 0xC0, + 0x1C, 0xF1, 0xE0, 0xF1, 0xE3, 0x0E, 0x1C, 0x10, 0xC1, 0x81, 0x08, 0x10, + 0x30, 0x81, 0x03, 0x18, 0x10, 0x21, 0x83, 0x02, 0x10, 0x30, 0x21, 0x02, + 0x06, 0x10, 0x20, 0x63, 0x02, 0x04, 0x30, 0x60, 0x42, 0x06, 0x04, 0xF8, + 0x70, 0xF0, 0x0E, 0x3E, 0x01, 0x60, 0x81, 0xC0, 0x20, 0xC0, 0x10, 0x40, + 0x08, 0x20, 0x04, 0x30, 0x02, 0x10, 0x02, 0x08, 0x01, 0x04, 0x00, 0x82, + 0x00, 0x42, 0x00, 0x21, 0x00, 0x20, 0x80, 0x13, 0xF0, 0x3E, 0x01, 0xF0, + 0x06, 0x0C, 0x18, 0x06, 0x20, 0x03, 0x60, 0x01, 0x40, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x02, 0x80, 0x06, 0xC0, 0x04, 0x40, 0x18, + 0x30, 0x60, 0x1F, 0x80, 0x0F, 0x1F, 0x80, 0x16, 0x0C, 0x01, 0xC0, 0x20, + 0x30, 0x03, 0x03, 0x00, 0x10, 0x20, 0x01, 0x02, 0x00, 0x10, 0x40, 0x01, + 0x04, 0x00, 0x10, 0x40, 0x02, 0x06, 0x00, 0x60, 0x60, 0x04, 0x0B, 0x00, + 0x80, 0x98, 0x30, 0x08, 0xFC, 0x00, 0x80, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x0F, 0xF0, 0x00, 0x03, + 0xF1, 0xE1, 0x83, 0x20, 0x40, 0x34, 0x10, 0x03, 0x84, 0x00, 0x30, 0x80, + 0x04, 0x20, 0x00, 0x84, 0x00, 0x10, 0x80, 0x06, 0x10, 0x00, 0xC2, 0x00, + 0x30, 0x60, 0x0E, 0x04, 0x03, 0x40, 0x60, 0xC8, 0x07, 0xE2, 0x00, 0x00, + 0x40, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x01, + 0x00, 0x03, 0xFC, 0x00, 0x0F, 0x87, 0xC0, 0x23, 0x08, 0x04, 0xC0, 0x00, + 0xE0, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x02, + 0x00, 0x00, 0x40, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x40, 0x00, 0x08, + 0x00, 0x3F, 0xFE, 0x00, 0x01, 0xFA, 0x0C, 0x1C, 0x20, 0x08, 0x80, 0x11, + 0x00, 0x03, 0x00, 0x03, 0xF8, 0x00, 0x7C, 0x00, 0x0C, 0x00, 0x09, 0x00, + 0x16, 0x00, 0x2C, 0x00, 0x9E, 0x06, 0x27, 0xF0, 0x00, 0x08, 0x00, 0x40, + 0x02, 0x00, 0x10, 0x00, 0x80, 0x7F, 0xFC, 0x40, 0x02, 0x00, 0x10, 0x00, + 0x80, 0x08, 0x00, 0x40, 0x02, 0x00, 0x10, 0x01, 0x00, 0x08, 0x00, 0x40, + 0x02, 0x00, 0xD8, 0x1C, 0x3F, 0x00, 0xF0, 0x1E, 0x20, 0x04, 0x80, 0x09, + 0x00, 0x12, 0x00, 0x24, 0x00, 0xC8, 0x01, 0x20, 0x02, 0x40, 0x04, 0x80, + 0x09, 0x00, 0x12, 0x00, 0x64, 0x03, 0x8C, 0x1D, 0x0F, 0xC3, 0x80, 0xFE, + 0x0F, 0xE6, 0x00, 0x20, 0x40, 0x08, 0x08, 0x03, 0x01, 0x80, 0x40, 0x30, + 0x18, 0x06, 0x02, 0x00, 0x40, 0x80, 0x08, 0x30, 0x01, 0x84, 0x00, 0x31, + 0x80, 0x02, 0x20, 0x00, 0x48, 0x00, 0x09, 0x00, 0x01, 0xC0, 0x00, 0xF8, + 0x0F, 0xA0, 0x01, 0x90, 0x00, 0x88, 0x40, 0xC4, 0x30, 0x42, 0x18, 0x61, + 0x1A, 0x20, 0x8D, 0x10, 0x4C, 0x98, 0x26, 0x48, 0x16, 0x2C, 0x0B, 0x14, + 0x07, 0x0A, 0x03, 0x07, 0x01, 0x81, 0x00, 0x0F, 0x83, 0xE0, 0xC0, 0x18, + 0x0C, 0x0C, 0x01, 0x83, 0x00, 0x18, 0xC0, 0x01, 0xB0, 0x00, 0x1C, 0x00, + 0x03, 0x00, 0x00, 0xF0, 0x00, 0x63, 0x00, 0x18, 0x30, 0x06, 0x06, 0x01, + 0x80, 0x60, 0x60, 0x06, 0x3F, 0x07, 0xE0, 0x0F, 0xC0, 0xF8, 0x30, 0x01, + 0x00, 0x80, 0x18, 0x04, 0x00, 0x80, 0x30, 0x0C, 0x01, 0x80, 0xC0, 0x04, + 0x04, 0x00, 0x30, 0x60, 0x01, 0x86, 0x00, 0x04, 0x20, 0x00, 0x23, 0x00, + 0x01, 0xB0, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x00, 0x01, 0x80, 0x00, 0x08, + 0x00, 0x00, 0xC0, 0x00, 0x04, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, + 0x20, 0x00, 0x7F, 0xE0, 0x00, 0x1F, 0xFF, 0x10, 0x06, 0x10, 0x0C, 0x10, + 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, + 0x00, 0x0C, 0x00, 0x18, 0x04, 0x30, 0x0C, 0x60, 0x0C, 0xFF, 0xF8, 0x00, + 0xE0, 0x20, 0x08, 0x01, 0x00, 0x20, 0x04, 0x01, 0x00, 0x20, 0x04, 0x00, + 0x80, 0x20, 0x08, 0x0E, 0x00, 0x60, 0x04, 0x00, 0x80, 0x10, 0x02, 0x00, + 0x40, 0x08, 0x02, 0x00, 0x40, 0x08, 0x01, 0x00, 0x18, 0x00, 0x00, 0x10, + 0xC3, 0x08, 0x20, 0x86, 0x18, 0x41, 0x04, 0x30, 0xC2, 0x08, 0x21, 0x86, + 0x10, 0x43, 0x0C, 0x20, 0x06, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, + 0x10, 0x04, 0x02, 0x00, 0x80, 0x20, 0x0C, 0x01, 0xC0, 0xC0, 0x40, 0x10, + 0x04, 0x03, 0x00, 0x80, 0x20, 0x08, 0x02, 0x01, 0x00, 0xC0, 0xE0, 0x00, + 0x1E, 0x02, 0x66, 0x0D, 0x86, 0x16, 0x06, 0x48, 0x07, 0x00}; + +const GFXglyph FreeMonoOblique18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 21, 0, 1}, // 0x20 ' ' + {0, 7, 22, 21, 9, -21}, // 0x21 '!' + {20, 13, 10, 21, 7, -20}, // 0x22 '"' + {37, 15, 24, 21, 5, -21}, // 0x23 '#' + {82, 16, 26, 21, 4, -22}, // 0x24 '$' + {134, 16, 21, 21, 5, -20}, // 0x25 '%' + {176, 13, 18, 21, 5, -17}, // 0x26 '&' + {206, 5, 10, 21, 12, -20}, // 0x27 ''' + {213, 8, 25, 21, 12, -20}, // 0x28 '(' + {238, 8, 25, 21, 5, -20}, // 0x29 ')' + {263, 14, 11, 21, 7, -19}, // 0x2A '*' + {283, 15, 17, 21, 5, -17}, // 0x2B '+' + {315, 9, 10, 21, 4, -4}, // 0x2C ',' + {327, 16, 1, 21, 5, -9}, // 0x2D '-' + {329, 5, 5, 21, 8, -4}, // 0x2E '.' + {333, 19, 26, 21, 3, -22}, // 0x2F '/' + {395, 14, 21, 21, 5, -20}, // 0x30 '0' + {432, 13, 21, 21, 4, -20}, // 0x31 '1' + {467, 17, 21, 21, 3, -20}, // 0x32 '2' + {512, 16, 21, 21, 3, -20}, // 0x33 '3' + {554, 14, 21, 21, 5, -20}, // 0x34 '4' + {591, 17, 21, 21, 4, -20}, // 0x35 '5' + {636, 16, 21, 21, 6, -20}, // 0x36 '6' + {678, 13, 21, 21, 8, -20}, // 0x37 '7' + {713, 15, 21, 21, 5, -20}, // 0x38 '8' + {753, 15, 21, 21, 5, -20}, // 0x39 '9' + {793, 7, 15, 21, 8, -14}, // 0x3A ':' + {807, 11, 20, 21, 4, -14}, // 0x3B ';' + {835, 17, 16, 21, 5, -17}, // 0x3C '<' + {869, 19, 6, 21, 3, -12}, // 0x3D '=' + {884, 18, 16, 21, 3, -17}, // 0x3E '>' + {920, 12, 20, 21, 8, -19}, // 0x3F '?' + {950, 15, 23, 21, 5, -20}, // 0x40 '@' + {994, 21, 20, 21, 0, -19}, // 0x41 'A' + {1047, 18, 20, 21, 2, -19}, // 0x42 'B' + {1092, 18, 20, 21, 4, -19}, // 0x43 'C' + {1137, 18, 20, 21, 2, -19}, // 0x44 'D' + {1182, 20, 20, 21, 2, -19}, // 0x45 'E' + {1232, 20, 20, 21, 2, -19}, // 0x46 'F' + {1282, 18, 20, 21, 4, -19}, // 0x47 'G' + {1327, 21, 20, 21, 2, -19}, // 0x48 'H' + {1380, 17, 20, 21, 4, -19}, // 0x49 'I' + {1423, 20, 20, 21, 4, -19}, // 0x4A 'J' + {1473, 21, 20, 21, 2, -19}, // 0x4B 'K' + {1526, 18, 20, 21, 2, -19}, // 0x4C 'L' + {1571, 24, 20, 21, 1, -19}, // 0x4D 'M' + {1631, 22, 20, 21, 2, -19}, // 0x4E 'N' + {1686, 17, 20, 21, 4, -19}, // 0x4F 'O' + {1729, 18, 20, 21, 2, -19}, // 0x50 'P' + {1774, 17, 24, 21, 4, -19}, // 0x51 'Q' + {1825, 18, 20, 21, 2, -19}, // 0x52 'R' + {1870, 18, 20, 21, 3, -19}, // 0x53 'S' + {1915, 17, 20, 21, 5, -19}, // 0x54 'T' + {1958, 18, 20, 21, 5, -19}, // 0x55 'U' + {2003, 21, 20, 21, 4, -19}, // 0x56 'V' + {2056, 20, 20, 21, 4, -19}, // 0x57 'W' + {2106, 21, 20, 21, 2, -19}, // 0x58 'X' + {2159, 18, 20, 21, 5, -19}, // 0x59 'Y' + {2204, 17, 20, 21, 4, -19}, // 0x5A 'Z' + {2247, 11, 25, 21, 9, -20}, // 0x5B '[' + {2282, 8, 27, 21, 9, -22}, // 0x5C '\' + {2309, 11, 25, 21, 5, -20}, // 0x5D ']' + {2344, 13, 9, 21, 7, -20}, // 0x5E '^' + {2359, 21, 1, 21, -1, 4}, // 0x5F '_' + {2362, 5, 5, 21, 9, -21}, // 0x60 '`' + {2366, 16, 15, 21, 3, -14}, // 0x61 'a' + {2396, 19, 21, 21, 1, -20}, // 0x62 'b' + {2446, 17, 15, 21, 4, -14}, // 0x63 'c' + {2478, 18, 21, 21, 4, -20}, // 0x64 'd' + {2526, 16, 15, 21, 4, -14}, // 0x65 'e' + {2556, 19, 21, 21, 4, -20}, // 0x66 'f' + {2606, 19, 22, 21, 4, -14}, // 0x67 'g' + {2659, 18, 21, 21, 2, -20}, // 0x68 'h' + {2707, 15, 22, 21, 3, -21}, // 0x69 'i' + {2749, 15, 29, 21, 3, -21}, // 0x6A 'j' + {2804, 18, 21, 21, 2, -20}, // 0x6B 'k' + {2852, 15, 21, 21, 3, -20}, // 0x6C 'l' + {2892, 20, 15, 21, 1, -14}, // 0x6D 'm' + {2930, 17, 15, 21, 2, -14}, // 0x6E 'n' + {2962, 16, 15, 21, 4, -14}, // 0x6F 'o' + {2992, 20, 22, 21, 0, -14}, // 0x70 'p' + {3047, 19, 22, 21, 4, -14}, // 0x71 'q' + {3100, 19, 15, 21, 3, -14}, // 0x72 'r' + {3136, 15, 15, 21, 4, -14}, // 0x73 's' + {3165, 13, 20, 21, 5, -19}, // 0x74 't' + {3198, 15, 15, 21, 4, -14}, // 0x75 'u' + {3227, 19, 15, 21, 4, -14}, // 0x76 'v' + {3263, 17, 15, 21, 5, -14}, // 0x77 'w' + {3295, 19, 15, 21, 2, -14}, // 0x78 'x' + {3331, 21, 22, 21, 1, -14}, // 0x79 'y' + {3389, 16, 15, 21, 4, -14}, // 0x7A 'z' + {3419, 11, 25, 21, 8, -20}, // 0x7B '{' + {3454, 6, 24, 21, 9, -19}, // 0x7C '|' + {3472, 10, 25, 21, 6, -20}, // 0x7D '}' + {3504, 15, 5, 21, 5, -11}}; // 0x7E '~' + +const GFXfont FreeMonoOblique18pt7b PROGMEM = { + (uint8_t *)FreeMonoOblique18pt7bBitmaps, + (GFXglyph *)FreeMonoOblique18pt7bGlyphs, 0x20, 0x7E, 35}; + +// Approx. 4186 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique24pt7b.h new file mode 100644 index 0000000..ca09739 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique24pt7b.h @@ -0,0 +1,642 @@ +const uint8_t FreeMonoOblique24pt7bBitmaps[] PROGMEM = { + 0x01, 0xC0, 0xF0, 0x3C, 0x0E, 0x03, 0x81, 0xE0, 0x78, 0x1C, 0x07, 0x01, + 0xC0, 0xE0, 0x38, 0x0E, 0x03, 0x00, 0xC0, 0x70, 0x1C, 0x06, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0F, 0x83, 0xE0, 0xF8, + 0x1C, 0x00, 0x7E, 0x3F, 0x7E, 0x3F, 0x7C, 0x3E, 0x7C, 0x3E, 0x7C, 0x3E, + 0x78, 0x3C, 0xF8, 0x7C, 0xF0, 0x78, 0xF0, 0x78, 0xF0, 0x78, 0xE0, 0x70, + 0xE0, 0x70, 0xE0, 0x70, 0xC0, 0x60, 0x00, 0x18, 0x30, 0x00, 0x61, 0x80, + 0x01, 0x86, 0x00, 0x04, 0x18, 0x00, 0x30, 0xC0, 0x00, 0xC3, 0x00, 0x03, + 0x0C, 0x00, 0x18, 0x30, 0x00, 0x61, 0x80, 0x01, 0x86, 0x00, 0x06, 0x18, + 0x07, 0xFF, 0xFF, 0x1F, 0xFF, 0xFC, 0x03, 0x0C, 0x00, 0x18, 0x30, 0x00, + 0x61, 0x80, 0x01, 0x86, 0x00, 0x06, 0x18, 0x00, 0x30, 0xC0, 0x1F, 0xFF, + 0xF8, 0x7F, 0xFF, 0xE0, 0x18, 0x30, 0x00, 0x61, 0x80, 0x01, 0x86, 0x00, + 0x06, 0x18, 0x00, 0x30, 0x40, 0x00, 0xC3, 0x00, 0x03, 0x0C, 0x00, 0x18, + 0x30, 0x00, 0x61, 0x80, 0x01, 0x86, 0x00, 0x06, 0x18, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x80, 0x00, 0x3F, 0x00, 0x07, 0xFD, 0x80, + 0x70, 0x7C, 0x06, 0x00, 0xE0, 0x60, 0x02, 0x07, 0x00, 0x10, 0x30, 0x00, + 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x00, 0x01, 0xF0, 0x00, 0x07, + 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x18, + 0x00, 0x00, 0xC2, 0x00, 0x06, 0x30, 0x00, 0x61, 0x80, 0x03, 0x1E, 0x00, + 0x30, 0xFC, 0x07, 0x06, 0x7F, 0xF0, 0x00, 0xFE, 0x00, 0x01, 0x80, 0x00, + 0x0C, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x78, 0x00, 0x07, 0xF8, 0x00, 0x38, 0x60, 0x01, 0xC0, 0xC0, + 0x06, 0x03, 0x00, 0x30, 0x0C, 0x00, 0xC0, 0x30, 0x03, 0x01, 0x80, 0x0C, + 0x0E, 0x00, 0x38, 0x70, 0x00, 0x7F, 0x81, 0xC0, 0xF8, 0x3F, 0x00, 0x07, + 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xC0, 0x00, 0x78, 0x00, 0x01, + 0x00, 0x78, 0x00, 0x07, 0xF8, 0x00, 0x38, 0x60, 0x01, 0x80, 0xC0, 0x06, + 0x03, 0x00, 0x30, 0x0C, 0x00, 0xC0, 0x30, 0x03, 0x01, 0x80, 0x0C, 0x0E, + 0x00, 0x18, 0x70, 0x00, 0x7F, 0x80, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, + 0x0F, 0xF8, 0x03, 0x8E, 0x00, 0xC0, 0x00, 0x38, 0x00, 0x06, 0x00, 0x00, + 0xC0, 0x00, 0x18, 0x00, 0x01, 0x00, 0x00, 0x30, 0x00, 0x06, 0x00, 0x03, + 0xE0, 0x01, 0xCC, 0x0E, 0x60, 0xC3, 0xD8, 0x18, 0x63, 0x03, 0x18, 0xC0, + 0x33, 0x18, 0x06, 0xC3, 0x00, 0x70, 0x60, 0x0E, 0x0C, 0x01, 0xC0, 0xC0, + 0x78, 0x1C, 0x3B, 0xE1, 0xFE, 0x3C, 0x1F, 0x00, 0x00, 0x7E, 0xFD, 0xF3, + 0xE7, 0xCF, 0x3E, 0x78, 0xF1, 0xE3, 0x87, 0x0E, 0x18, 0x00, 0x00, 0x60, + 0x18, 0x07, 0x00, 0xC0, 0x30, 0x0E, 0x01, 0x80, 0x70, 0x0C, 0x03, 0x80, + 0x60, 0x1C, 0x03, 0x80, 0xE0, 0x1C, 0x03, 0x80, 0xF0, 0x1C, 0x03, 0x80, + 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x30, + 0x06, 0x00, 0xC0, 0x1C, 0x01, 0x80, 0x30, 0x02, 0x00, 0x01, 0x80, 0x30, + 0x06, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07, 0x00, 0xE0, 0x1C, 0x03, + 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x38, 0x07, 0x00, + 0xE0, 0x3C, 0x07, 0x00, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x38, 0x0E, 0x01, + 0x80, 0x70, 0x0C, 0x03, 0x00, 0xC0, 0x10, 0x00, 0x00, 0x20, 0x00, 0x18, + 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x30, 0x0E, 0x0C, 0x0B, + 0xF3, 0x3E, 0x3F, 0xFE, 0x01, 0xFC, 0x00, 0x3C, 0x00, 0x1F, 0x00, 0x0E, + 0x60, 0x07, 0x18, 0x01, 0x83, 0x00, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, + 0x00, 0xC0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x30, 0x00, 0x01, + 0x80, 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x01, 0x80, + 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, + 0x03, 0xF0, 0x7E, 0x07, 0xC0, 0xF8, 0x0F, 0x81, 0xF0, 0x1E, 0x03, 0xE0, + 0x3C, 0x07, 0x80, 0x70, 0x0F, 0x00, 0xE0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x3C, 0xFF, 0xFF, 0xFF, 0xCF, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, + 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x0F, 0xF8, 0x01, + 0xC1, 0xC0, 0x38, 0x0E, 0x07, 0x00, 0x60, 0xE0, 0x03, 0x0C, 0x00, 0x31, + 0x80, 0x03, 0x18, 0x00, 0x33, 0x00, 0x03, 0x30, 0x00, 0x33, 0x00, 0x03, + 0x20, 0x00, 0x26, 0x00, 0x06, 0x60, 0x00, 0x66, 0x00, 0x06, 0x40, 0x00, + 0x4C, 0x00, 0x0C, 0xC0, 0x00, 0xCC, 0x00, 0x0C, 0xC0, 0x01, 0x8C, 0x00, + 0x18, 0xC0, 0x01, 0x8C, 0x00, 0x30, 0xC0, 0x07, 0x06, 0x00, 0xE0, 0x60, + 0x1C, 0x03, 0x87, 0x80, 0x3F, 0xF0, 0x00, 0xFC, 0x00, 0x00, 0x0E, 0x00, + 0x0F, 0x00, 0x0F, 0x80, 0x0E, 0xC0, 0x1C, 0xC0, 0x1C, 0x60, 0x1C, 0x30, + 0x08, 0x18, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, + 0x80, 0x01, 0xC0, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, 0x80, 0x01, 0x80, + 0x00, 0xC0, 0x00, 0x60, 0x00, 0x30, 0x1F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, + 0x07, 0xE0, 0x00, 0x3F, 0xE0, 0x01, 0xE0, 0xE0, 0x07, 0x00, 0xE0, 0x1C, + 0x00, 0xE0, 0x30, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x00, 0x03, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x70, 0x00, 0x01, 0xC0, 0x00, 0x07, 0x00, + 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x00, 0x0E, 0x00, 0x00, + 0x70, 0x00, 0x01, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xE0, + 0x00, 0xC3, 0x80, 0x01, 0x87, 0xFF, 0xFF, 0x0F, 0xFF, 0xFC, 0x00, 0x00, + 0x0F, 0xC0, 0x01, 0xFF, 0xC0, 0x1E, 0x07, 0x80, 0xE0, 0x06, 0x03, 0x00, + 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x60, 0x00, 0x03, 0x80, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x00, + 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x01, + 0x80, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, + 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xE3, 0x00, 0x07, 0x0E, 0x00, + 0x38, 0x1E, 0x03, 0xC0, 0x3F, 0xFC, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x03, + 0xE0, 0x00, 0xF8, 0x00, 0x1B, 0x00, 0x06, 0x60, 0x01, 0x8C, 0x00, 0x63, + 0x00, 0x18, 0x60, 0x07, 0x0C, 0x00, 0xC1, 0x80, 0x30, 0x30, 0x0C, 0x0C, + 0x03, 0x01, 0x80, 0xC0, 0x30, 0x18, 0x06, 0x06, 0x00, 0xC1, 0x80, 0x30, + 0x60, 0x06, 0x18, 0x00, 0xC3, 0xFF, 0xFE, 0x7F, 0xFF, 0xC0, 0x00, 0xC0, + 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x18, 0x00, 0x03, 0x00, + 0x0F, 0xFC, 0x01, 0xFF, 0x80, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xC0, 0x40, + 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, + 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0xBF, 0xC0, 0x0F, 0xFF, + 0x80, 0xF8, 0x1E, 0x02, 0x00, 0x30, 0x00, 0x01, 0xC0, 0x00, 0x06, 0x00, + 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x60, 0x00, 0x06, + 0x00, 0x00, 0x30, 0x00, 0x03, 0x80, 0x00, 0x18, 0xC0, 0x01, 0x87, 0x00, + 0x38, 0x1E, 0x07, 0x80, 0x7F, 0xF8, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0xFF, 0xC0, 0x1F, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x01, + 0x80, 0x00, 0x18, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x00, 0xC0, 0x00, + 0x0E, 0x00, 0x00, 0x60, 0x00, 0x07, 0x0F, 0x80, 0x31, 0xFF, 0x01, 0x9C, + 0x3C, 0x0D, 0x80, 0x60, 0xD8, 0x03, 0x87, 0x80, 0x0C, 0x38, 0x00, 0x61, + 0xC0, 0x03, 0x0C, 0x00, 0x18, 0x60, 0x00, 0xC3, 0x00, 0x0C, 0x18, 0x00, + 0x60, 0xE0, 0x06, 0x03, 0x00, 0x30, 0x1C, 0x07, 0x00, 0x70, 0x70, 0x01, + 0xFF, 0x00, 0x07, 0xE0, 0x00, 0x7F, 0xFF, 0xDF, 0xFF, 0xFC, 0x00, 0x0F, + 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x18, 0x00, 0x0C, + 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x30, 0x00, 0x0C, 0x00, + 0x06, 0x00, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x18, 0x00, 0x0C, + 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x00, 0x3F, 0x00, 0x0F, + 0xFC, 0x01, 0xC1, 0xE0, 0x70, 0x06, 0x06, 0x00, 0x30, 0xC0, 0x03, 0x1C, + 0x00, 0x31, 0x80, 0x03, 0x18, 0x00, 0x31, 0x80, 0x06, 0x18, 0x00, 0xE0, + 0xC0, 0x1C, 0x0F, 0x07, 0x80, 0x3F, 0xE0, 0x03, 0xFE, 0x00, 0xE0, 0x70, + 0x18, 0x03, 0x83, 0x00, 0x1C, 0x60, 0x00, 0xC6, 0x00, 0x0C, 0xC0, 0x00, + 0xCC, 0x00, 0x0C, 0xC0, 0x00, 0xCC, 0x00, 0x18, 0xC0, 0x03, 0x8E, 0x00, + 0x70, 0x60, 0x0E, 0x07, 0x83, 0xC0, 0x3F, 0xF0, 0x00, 0xFC, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0xFF, 0x80, 0x0F, 0x07, 0x00, 0x70, 0x0E, 0x03, 0x80, + 0x18, 0x0C, 0x00, 0x70, 0x60, 0x00, 0xC1, 0x80, 0x03, 0x0C, 0x00, 0x0C, + 0x30, 0x00, 0x30, 0xC0, 0x01, 0xC3, 0x00, 0x0F, 0x0C, 0x00, 0x6C, 0x38, + 0x03, 0xF0, 0x60, 0x1D, 0x81, 0xE1, 0xE6, 0x03, 0xFE, 0x18, 0x03, 0xE0, + 0xC0, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, 0x38, 0x00, 0x03, + 0xC0, 0x00, 0x7C, 0x00, 0xFF, 0xC0, 0x01, 0xF8, 0x00, 0x00, 0x07, 0x83, + 0xF1, 0xFC, 0x7F, 0x1F, 0x83, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7E, 0x3F, 0x8F, 0xE3, 0xF0, 0x78, + 0x00, 0x00, 0x3C, 0x00, 0xFC, 0x03, 0xF8, 0x07, 0xF0, 0x0F, 0xC0, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x00, 0xFC, 0x03, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x3E, + 0x00, 0xF8, 0x01, 0xE0, 0x07, 0x80, 0x0F, 0x00, 0x3C, 0x00, 0x70, 0x01, + 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x3C, 0x00, 0x01, + 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, + 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x03, + 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x78, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0C, 0x00, 0x3F, 0xFF, 0xFF, 0x9F, + 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, + 0x00, 0x06, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, + 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0x70, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x07, 0xF0, 0x3F, 0xFC, 0x78, + 0x1E, 0xC0, 0x07, 0xC0, 0x03, 0xC0, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x1C, 0x00, 0x38, 0x00, 0xE0, 0x07, 0xC0, 0x07, + 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x7E, 0x00, 0xFE, 0x00, 0xFE, + 0x00, 0x7C, 0x00, 0x00, 0x3F, 0x00, 0x1F, 0xF0, 0x07, 0x07, 0x01, 0xC0, + 0x70, 0x60, 0x06, 0x1C, 0x00, 0xC3, 0x00, 0x18, 0xC0, 0x03, 0x18, 0x00, + 0x66, 0x00, 0xFC, 0xC0, 0x7F, 0x98, 0x1C, 0x66, 0x06, 0x0C, 0xC1, 0x81, + 0x98, 0x30, 0x33, 0x0C, 0x0E, 0x61, 0x81, 0x98, 0x30, 0x33, 0x06, 0x06, + 0x60, 0xF0, 0xCC, 0x0F, 0xF9, 0x80, 0x7F, 0x30, 0x00, 0x06, 0x00, 0x00, + 0xC0, 0x00, 0x18, 0x00, 0x03, 0x80, 0x00, 0x30, 0x00, 0x07, 0x00, 0x00, + 0x70, 0x18, 0x0F, 0xFE, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x0F, + 0xFE, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x31, 0x80, + 0x00, 0x06, 0x30, 0x00, 0x01, 0x86, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x0C, + 0x1C, 0x00, 0x03, 0x01, 0x80, 0x00, 0x40, 0x30, 0x00, 0x18, 0x06, 0x00, + 0x06, 0x00, 0xC0, 0x00, 0xC0, 0x18, 0x00, 0x30, 0x01, 0x80, 0x07, 0xFF, + 0xF0, 0x01, 0xFF, 0xFE, 0x00, 0x60, 0x00, 0xC0, 0x0C, 0x00, 0x18, 0x03, + 0x00, 0x03, 0x00, 0x40, 0x00, 0x30, 0x18, 0x00, 0x06, 0x06, 0x00, 0x00, + 0xC0, 0xC0, 0x00, 0x18, 0xFF, 0x80, 0x7F, 0xFF, 0xF0, 0x0F, 0xFC, 0x03, + 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xC0, 0x06, 0x00, 0x38, 0x01, 0x80, 0x07, + 0x00, 0xC0, 0x00, 0xC0, 0x30, 0x00, 0x30, 0x0C, 0x00, 0x0C, 0x03, 0x00, + 0x03, 0x00, 0xC0, 0x01, 0x80, 0x60, 0x00, 0xC0, 0x18, 0x01, 0xE0, 0x07, + 0xFF, 0xE0, 0x01, 0xFF, 0xFC, 0x00, 0xE0, 0x03, 0x80, 0x30, 0x00, 0x70, + 0x0C, 0x00, 0x0E, 0x03, 0x00, 0x01, 0x80, 0xC0, 0x00, 0x60, 0x60, 0x00, + 0x18, 0x18, 0x00, 0x06, 0x06, 0x00, 0x03, 0x01, 0x80, 0x01, 0xC0, 0x60, + 0x00, 0xE0, 0x30, 0x00, 0x70, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xF8, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x3F, 0xFC, 0xC0, 0x3C, 0x0F, 0x60, 0x78, 0x01, + 0xF0, 0x70, 0x00, 0x70, 0x70, 0x00, 0x18, 0x30, 0x00, 0x0C, 0x30, 0x00, + 0x06, 0x38, 0x00, 0x02, 0x18, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x60, + 0x60, 0x00, 0x60, 0x38, 0x00, 0xE0, 0x0F, 0x01, 0xE0, 0x03, 0xFF, 0xC0, + 0x00, 0x3F, 0x00, 0x00, 0x03, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0x00, 0x0C, + 0x00, 0xF0, 0x03, 0x00, 0x1C, 0x01, 0xC0, 0x03, 0x80, 0x60, 0x00, 0x60, + 0x18, 0x00, 0x1C, 0x06, 0x00, 0x03, 0x01, 0x80, 0x00, 0xC0, 0xC0, 0x00, + 0x30, 0x30, 0x00, 0x0C, 0x0C, 0x00, 0x03, 0x03, 0x00, 0x00, 0xC0, 0xC0, + 0x00, 0x60, 0x60, 0x00, 0x18, 0x18, 0x00, 0x06, 0x06, 0x00, 0x03, 0x01, + 0x80, 0x00, 0xC0, 0xE0, 0x00, 0x70, 0x30, 0x00, 0x18, 0x0C, 0x00, 0x0C, + 0x03, 0x00, 0x06, 0x00, 0xC0, 0x07, 0x00, 0x60, 0x07, 0x80, 0xFF, 0xFF, + 0xC0, 0x3F, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFC, + 0x01, 0x80, 0x01, 0x80, 0x30, 0x00, 0x60, 0x0C, 0x00, 0x0C, 0x01, 0x80, + 0x01, 0x80, 0x30, 0x00, 0x30, 0x06, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, + 0x30, 0x18, 0x00, 0x06, 0x03, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x1F, 0xF8, + 0x00, 0x07, 0x03, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x18, 0x0C, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, 0x0C, 0x03, 0x00, 0x01, + 0x80, 0x60, 0x00, 0x30, 0x0C, 0x00, 0x0C, 0x01, 0x80, 0x01, 0x80, 0x60, + 0x00, 0x30, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, + 0xF0, 0x7F, 0xFF, 0xFF, 0x00, 0x60, 0x00, 0x30, 0x06, 0x00, 0x06, 0x00, + 0xC0, 0x00, 0x60, 0x0C, 0x00, 0x06, 0x00, 0xC0, 0x00, 0x60, 0x0C, 0x00, + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x18, 0x0C, 0x00, 0x01, 0x80, 0xC0, 0x00, + 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x38, 0x18, 0x00, 0x03, 0x01, + 0x80, 0x00, 0x30, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xFF, 0xFC, 0x00, + 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x3F, 0xFC, 0xC0, 0x3C, + 0x0F, 0xE0, 0x78, 0x01, 0xF0, 0x70, 0x00, 0x30, 0x70, 0x00, 0x18, 0x70, + 0x00, 0x0C, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x1F, 0xFE, 0xC0, 0x0F, 0xFF, 0x60, 0x00, 0x06, + 0x30, 0x00, 0x06, 0x18, 0x00, 0x03, 0x0C, 0x00, 0x01, 0x87, 0x00, 0x00, + 0xC1, 0x80, 0x00, 0xE0, 0xE0, 0x00, 0x60, 0x38, 0x00, 0x70, 0x0F, 0x00, + 0xF8, 0x03, 0xFF, 0xF0, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xFC, 0x1F, 0xE0, + 0x7F, 0x83, 0xFC, 0x03, 0x00, 0x06, 0x00, 0x60, 0x01, 0x80, 0x1C, 0x00, + 0x30, 0x03, 0x00, 0x06, 0x00, 0x60, 0x00, 0xC0, 0x0C, 0x00, 0x38, 0x01, + 0x80, 0x06, 0x00, 0x60, 0x00, 0xC0, 0x0C, 0x00, 0x18, 0x01, 0xFF, 0xFF, + 0x00, 0x3F, 0xFF, 0xC0, 0x06, 0x00, 0x18, 0x01, 0x80, 0x03, 0x00, 0x30, + 0x00, 0x60, 0x06, 0x00, 0x0C, 0x00, 0xC0, 0x03, 0x00, 0x38, 0x00, 0x60, + 0x06, 0x00, 0x0C, 0x00, 0xC0, 0x01, 0x80, 0x18, 0x00, 0x70, 0x03, 0x00, + 0x0C, 0x00, 0xE0, 0x01, 0x80, 0xFF, 0x83, 0xFE, 0x1F, 0xF0, 0x7F, 0xC0, + 0x07, 0xFF, 0xFC, 0x1F, 0xFF, 0xF0, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x70, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, + 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x01, + 0x80, 0x00, 0x06, 0x00, 0x00, 0x38, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0xFF, 0xFF, 0x83, 0xFF, 0xFE, 0x00, + 0x00, 0x0F, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x38, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x07, + 0x00, 0x20, 0x00, 0x60, 0x06, 0x00, 0x06, 0x00, 0x60, 0x00, 0x60, 0x06, + 0x00, 0x06, 0x00, 0x60, 0x00, 0xC0, 0x0C, 0x00, 0x0C, 0x00, 0xC0, 0x00, + 0xC0, 0x0C, 0x00, 0x18, 0x00, 0xE0, 0x03, 0x00, 0x07, 0x00, 0x70, 0x00, + 0x3C, 0x1C, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x03, + 0xFF, 0x07, 0xF8, 0x3F, 0xF8, 0x3F, 0xC0, 0x18, 0x00, 0x70, 0x00, 0xC0, + 0x07, 0x00, 0x0C, 0x00, 0x60, 0x00, 0x60, 0x0E, 0x00, 0x03, 0x00, 0xE0, + 0x00, 0x18, 0x0C, 0x00, 0x00, 0xC1, 0xC0, 0x00, 0x0C, 0x1C, 0x00, 0x00, + 0x61, 0x80, 0x00, 0x03, 0x3C, 0x00, 0x00, 0x1B, 0x78, 0x00, 0x01, 0xF0, + 0xE0, 0x00, 0x0F, 0x03, 0x80, 0x00, 0x60, 0x0C, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x18, 0x01, 0x80, 0x01, 0x80, 0x0C, 0x00, 0x0C, 0x00, 0x60, 0x00, + 0x60, 0x01, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x60, 0x01, 0x80, + 0x03, 0x00, 0xFF, 0xE0, 0x1F, 0x87, 0xFF, 0x00, 0x7C, 0x00, 0x07, 0xFF, + 0xE0, 0x03, 0xFF, 0xF0, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x03, 0x00, 0x60, 0x01, 0x80, 0x60, 0x00, 0xC0, 0x30, 0x00, + 0x60, 0x18, 0x00, 0x30, 0x0C, 0x00, 0x30, 0x0E, 0x00, 0x18, 0x06, 0x00, + 0x0C, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0x00, 0x07, 0xF0, 0x00, 0x3F, + 0x07, 0xF0, 0x00, 0x7F, 0x01, 0xB0, 0x00, 0xD8, 0x01, 0xB0, 0x00, 0xD8, + 0x01, 0x98, 0x01, 0x98, 0x01, 0x98, 0x03, 0x30, 0x01, 0x98, 0x03, 0x30, + 0x03, 0x18, 0x06, 0x30, 0x03, 0x1C, 0x0C, 0x30, 0x03, 0x0C, 0x0C, 0x30, + 0x03, 0x0C, 0x18, 0x60, 0x07, 0x0C, 0x30, 0x60, 0x06, 0x0C, 0x30, 0x60, + 0x06, 0x06, 0x60, 0x60, 0x06, 0x06, 0xC0, 0x60, 0x06, 0x06, 0xC0, 0xC0, + 0x0C, 0x07, 0x80, 0xC0, 0x0C, 0x03, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, + 0x0C, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x80, 0x18, 0x00, 0x01, 0x80, + 0x18, 0x00, 0x01, 0x80, 0x18, 0x00, 0x01, 0x80, 0xFF, 0x80, 0x3F, 0xE0, + 0xFF, 0x80, 0x3F, 0xE0, 0x07, 0xE0, 0x0F, 0xFC, 0x3F, 0x80, 0x3F, 0xF0, + 0x0F, 0x00, 0x06, 0x00, 0x3C, 0x00, 0x10, 0x01, 0x98, 0x00, 0xC0, 0x06, + 0x60, 0x03, 0x00, 0x19, 0xC0, 0x0C, 0x00, 0x63, 0x00, 0x30, 0x01, 0x0C, + 0x01, 0x80, 0x0C, 0x18, 0x06, 0x00, 0x30, 0x60, 0x18, 0x00, 0xC1, 0xC0, + 0x60, 0x03, 0x03, 0x01, 0x00, 0x08, 0x0C, 0x0C, 0x00, 0x60, 0x18, 0x30, + 0x01, 0x80, 0x60, 0xC0, 0x06, 0x01, 0xC3, 0x00, 0x18, 0x03, 0x18, 0x00, + 0xC0, 0x0C, 0x60, 0x03, 0x00, 0x19, 0x80, 0x0C, 0x00, 0x66, 0x00, 0x30, + 0x01, 0xD8, 0x00, 0x80, 0x03, 0xC0, 0x06, 0x00, 0x0F, 0x00, 0xFF, 0xC0, + 0x1C, 0x03, 0xFE, 0x00, 0x70, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x7F, 0xF0, + 0x00, 0xF0, 0x78, 0x03, 0x80, 0x1C, 0x07, 0x00, 0x0E, 0x0E, 0x00, 0x06, + 0x0C, 0x00, 0x06, 0x18, 0x00, 0x07, 0x38, 0x00, 0x03, 0x30, 0x00, 0x03, + 0x60, 0x00, 0x03, 0x60, 0x00, 0x03, 0x60, 0x00, 0x03, 0xC0, 0x00, 0x03, + 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x06, 0xC0, 0x00, 0x06, 0xC0, 0x00, 0x06, + 0xC0, 0x00, 0x0C, 0xC0, 0x00, 0x1C, 0xC0, 0x00, 0x18, 0x60, 0x00, 0x30, + 0x60, 0x00, 0x70, 0x70, 0x00, 0xE0, 0x38, 0x01, 0xC0, 0x1E, 0x0F, 0x00, + 0x0F, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, + 0xC0, 0x06, 0x00, 0x78, 0x01, 0x80, 0x06, 0x00, 0xC0, 0x01, 0xC0, 0x30, + 0x00, 0x30, 0x0C, 0x00, 0x0C, 0x03, 0x00, 0x03, 0x00, 0xC0, 0x01, 0xC0, + 0x60, 0x00, 0x60, 0x18, 0x00, 0x30, 0x06, 0x00, 0x18, 0x01, 0x80, 0x3C, + 0x00, 0xFF, 0xFE, 0x00, 0x3F, 0xFC, 0x00, 0x0C, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, + 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x7F, + 0xF0, 0x00, 0xF0, 0x78, 0x03, 0x80, 0x1C, 0x07, 0x00, 0x0E, 0x0E, 0x00, + 0x06, 0x0C, 0x00, 0x06, 0x18, 0x00, 0x03, 0x38, 0x00, 0x03, 0x30, 0x00, + 0x03, 0x60, 0x00, 0x03, 0x60, 0x00, 0x03, 0x60, 0x00, 0x03, 0xC0, 0x00, + 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x06, 0xC0, 0x00, 0x06, 0xC0, 0x00, + 0x06, 0xC0, 0x00, 0x0C, 0xC0, 0x00, 0x1C, 0xC0, 0x00, 0x18, 0x60, 0x00, + 0x30, 0x60, 0x00, 0x70, 0x30, 0x00, 0xE0, 0x38, 0x01, 0xC0, 0x0E, 0x0F, + 0x00, 0x07, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x1F, 0xF8, + 0x30, 0x3F, 0xFF, 0xF0, 0x78, 0x0F, 0x80, 0x07, 0xFF, 0xFC, 0x01, 0xFF, + 0xFF, 0xC0, 0x06, 0x00, 0x78, 0x01, 0x80, 0x0E, 0x00, 0xC0, 0x01, 0xC0, + 0x30, 0x00, 0x30, 0x0C, 0x00, 0x0C, 0x03, 0x00, 0x03, 0x00, 0xC0, 0x00, + 0xC0, 0x60, 0x00, 0x60, 0x18, 0x00, 0x30, 0x06, 0x00, 0x38, 0x01, 0x80, + 0x3C, 0x00, 0xFF, 0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x0C, 0x07, 0x80, 0x03, + 0x00, 0x70, 0x00, 0xC0, 0x0E, 0x00, 0x60, 0x01, 0x80, 0x18, 0x00, 0x70, + 0x06, 0x00, 0x0C, 0x01, 0x80, 0x03, 0x80, 0x60, 0x00, 0x60, 0x30, 0x00, + 0x1C, 0xFF, 0xE0, 0x07, 0xFF, 0xF8, 0x00, 0xF0, 0x00, 0x1F, 0xC0, 0x00, + 0x7F, 0xF3, 0x00, 0xE0, 0x3B, 0x03, 0x80, 0x0F, 0x07, 0x00, 0x0E, 0x06, + 0x00, 0x06, 0x0C, 0x00, 0x06, 0x0C, 0x00, 0x06, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x20, 0x00, 0x18, 0x60, 0x00, 0x18, 0x60, + 0x00, 0x30, 0x60, 0x00, 0x70, 0xF0, 0x00, 0xE0, 0xF8, 0x01, 0xC0, 0xDC, + 0x07, 0x80, 0x8F, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0xFF, 0xFE, 0x3F, + 0xFF, 0xFE, 0x30, 0x18, 0x06, 0x60, 0x18, 0x06, 0x60, 0x18, 0x06, 0x60, + 0x38, 0x0C, 0x60, 0x30, 0x04, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0xFF, 0xFE, 0x00, 0xFF, + 0xFC, 0x00, 0x7F, 0xC0, 0xFF, 0xDF, 0xF0, 0x3F, 0xF1, 0x80, 0x00, 0x60, + 0x60, 0x00, 0x30, 0x18, 0x00, 0x0C, 0x06, 0x00, 0x03, 0x03, 0x80, 0x00, + 0xC0, 0xC0, 0x00, 0x30, 0x30, 0x00, 0x18, 0x0C, 0x00, 0x06, 0x03, 0x00, + 0x01, 0x81, 0xC0, 0x00, 0x60, 0x60, 0x00, 0x18, 0x18, 0x00, 0x0C, 0x06, + 0x00, 0x03, 0x01, 0x80, 0x00, 0xC0, 0xC0, 0x00, 0x30, 0x30, 0x00, 0x1C, + 0x0C, 0x00, 0x06, 0x03, 0x00, 0x01, 0x80, 0xC0, 0x00, 0xC0, 0x30, 0x00, + 0x70, 0x0E, 0x00, 0x38, 0x01, 0xC0, 0x1C, 0x00, 0x38, 0x1E, 0x00, 0x07, + 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x80, 0x3F, 0xFF, 0xF0, 0x07, + 0xFC, 0xE0, 0x00, 0x0C, 0x0C, 0x00, 0x03, 0x01, 0x80, 0x00, 0x60, 0x30, + 0x00, 0x18, 0x06, 0x00, 0x02, 0x00, 0xC0, 0x00, 0xC0, 0x0C, 0x00, 0x30, + 0x01, 0x80, 0x06, 0x00, 0x30, 0x01, 0x80, 0x06, 0x00, 0x60, 0x00, 0xC0, + 0x0C, 0x00, 0x18, 0x03, 0x00, 0x01, 0x80, 0xC0, 0x00, 0x30, 0x18, 0x00, + 0x06, 0x06, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x18, 0x30, 0x00, 0x03, 0x8C, + 0x00, 0x00, 0x31, 0x80, 0x00, 0x06, 0x60, 0x00, 0x00, 0xD8, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x38, 0x00, 0x00, 0xFF, 0xC0, + 0x7F, 0xFF, 0xF8, 0x0F, 0xF8, 0xC0, 0x00, 0x0C, 0x18, 0x00, 0x01, 0x83, + 0x00, 0x00, 0x30, 0x60, 0x08, 0x0C, 0x0C, 0x07, 0x01, 0x81, 0x81, 0xE0, + 0x30, 0x60, 0x2C, 0x0C, 0x0C, 0x0D, 0x81, 0x81, 0x81, 0x30, 0x30, 0x30, + 0x66, 0x0C, 0x06, 0x08, 0xC1, 0x80, 0xC3, 0x0C, 0x30, 0x18, 0x41, 0x8C, + 0x03, 0x18, 0x31, 0x80, 0x62, 0x06, 0x30, 0x0C, 0xC0, 0xCC, 0x03, 0x10, + 0x19, 0x80, 0x66, 0x03, 0x30, 0x0C, 0x80, 0x6C, 0x01, 0xB0, 0x0D, 0x80, + 0x34, 0x01, 0xB0, 0x07, 0x80, 0x3C, 0x00, 0xE0, 0x07, 0x80, 0x1C, 0x00, + 0xF0, 0x00, 0x03, 0xF8, 0x03, 0xF8, 0x1F, 0xC0, 0x3F, 0xC0, 0x30, 0x00, + 0x30, 0x01, 0xC0, 0x03, 0x00, 0x06, 0x00, 0x30, 0x00, 0x18, 0x03, 0x00, + 0x00, 0xE0, 0x30, 0x00, 0x03, 0x03, 0x00, 0x00, 0x1C, 0x30, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x03, 0xB0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x36, 0x00, 0x00, 0x03, 0x38, 0x00, + 0x00, 0x30, 0xC0, 0x00, 0x03, 0x07, 0x00, 0x00, 0x30, 0x18, 0x00, 0x03, + 0x00, 0x60, 0x00, 0x30, 0x03, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, + 0x70, 0x03, 0x00, 0x01, 0x80, 0xFF, 0x80, 0xFF, 0x07, 0xFC, 0x07, 0xF8, + 0x00, 0x7F, 0x80, 0x7F, 0x7F, 0x00, 0x7F, 0x1C, 0x00, 0x18, 0x0C, 0x00, + 0x30, 0x0C, 0x00, 0x70, 0x06, 0x00, 0xE0, 0x06, 0x00, 0xC0, 0x03, 0x01, + 0x80, 0x03, 0x03, 0x00, 0x01, 0x86, 0x00, 0x01, 0x8C, 0x00, 0x00, 0xD8, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x00, 0xFF, 0xFE, 0x00, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFE, 0x07, 0xFF, + 0xF8, 0x0C, 0x00, 0x30, 0x10, 0x00, 0xC0, 0x60, 0x03, 0x80, 0xC0, 0x0E, + 0x01, 0x80, 0x38, 0x03, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x00, 0x0E, 0x00, + 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x60, 0x18, + 0x00, 0xC0, 0x60, 0x01, 0x81, 0x80, 0x02, 0x06, 0x00, 0x0C, 0x18, 0x00, + 0x18, 0x60, 0x00, 0x30, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0x80, 0x01, 0xFE, + 0x03, 0xFC, 0x06, 0x00, 0x08, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, + 0x80, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x40, 0x01, 0x80, + 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x10, 0x00, 0x60, 0x00, 0xC0, 0x01, + 0x80, 0x03, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x80, + 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x20, 0x00, 0xFF, 0x01, + 0xFE, 0x00, 0xC0, 0x30, 0x0E, 0x01, 0x80, 0x60, 0x18, 0x07, 0x00, 0xC0, + 0x30, 0x0C, 0x03, 0x80, 0x60, 0x18, 0x06, 0x00, 0xC0, 0x30, 0x0C, 0x03, + 0x00, 0x60, 0x18, 0x06, 0x01, 0x80, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x18, + 0x06, 0x01, 0x80, 0x60, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x04, 0x01, 0xFE, + 0x03, 0xFC, 0x00, 0x10, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, + 0x04, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x03, 0x00, 0x06, + 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, + 0x06, 0x00, 0x08, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x06, + 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x01, 0x80, 0xFF, 0x01, + 0xFE, 0x00, 0x00, 0x10, 0x00, 0x0C, 0x00, 0x07, 0x80, 0x03, 0x60, 0x01, + 0x8C, 0x00, 0xC3, 0x80, 0xE0, 0x60, 0x70, 0x1C, 0x38, 0x03, 0x1C, 0x00, + 0x6E, 0x00, 0x1F, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC3, 0x86, 0x0C, 0x18, 0x70, 0xC0, 0x00, 0x3F, 0x80, 0x0F, 0xFF, 0x80, + 0x78, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, + 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x38, 0x03, 0xFC, 0xC0, 0x7F, 0xFF, + 0x07, 0xC0, 0x0C, 0x38, 0x00, 0x31, 0xC0, 0x01, 0xCE, 0x00, 0x06, 0x30, + 0x00, 0x18, 0xC0, 0x00, 0xE3, 0x00, 0x07, 0x8E, 0x00, 0x7C, 0x1C, 0x0F, + 0x3F, 0x3F, 0xF0, 0xFC, 0x7F, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x06, 0x07, 0xE0, 0x00, 0xC3, + 0xFF, 0x00, 0x19, 0xC0, 0xF0, 0x03, 0x60, 0x07, 0x00, 0xD8, 0x00, 0x60, + 0x1E, 0x00, 0x0E, 0x03, 0x80, 0x00, 0xC0, 0x60, 0x00, 0x18, 0x0C, 0x00, + 0x03, 0x03, 0x00, 0x00, 0x60, 0x60, 0x00, 0x0C, 0x0C, 0x00, 0x01, 0x81, + 0x80, 0x00, 0x60, 0x70, 0x00, 0x0C, 0x0E, 0x00, 0x03, 0x01, 0xC0, 0x00, + 0x60, 0x3C, 0x00, 0x18, 0x05, 0x80, 0x06, 0x01, 0xB8, 0x01, 0x83, 0xF3, + 0xC1, 0xE0, 0x7E, 0x3F, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x3F, 0x00, + 0x07, 0xFF, 0x30, 0x38, 0x0F, 0xC1, 0x80, 0x1F, 0x0C, 0x00, 0x18, 0x60, + 0x00, 0x63, 0x00, 0x01, 0x9C, 0x00, 0x06, 0x60, 0x00, 0x01, 0x80, 0x00, + 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x30, 0x00, 0x00, 0xE0, 0x00, 0x01, 0x80, 0x00, 0xC7, 0x00, + 0x0E, 0x0F, 0x01, 0xF0, 0x1F, 0xFF, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0xFC, 0x0C, 0x01, + 0xFF, 0x86, 0x01, 0xC0, 0xE3, 0x03, 0x80, 0x1B, 0x03, 0x80, 0x05, 0x81, + 0x80, 0x03, 0xC1, 0x80, 0x00, 0xE1, 0x80, 0x00, 0x60, 0xC0, 0x00, 0x30, + 0x60, 0x00, 0x18, 0x60, 0x00, 0x0C, 0x30, 0x00, 0x06, 0x18, 0x00, 0x02, + 0x0C, 0x00, 0x03, 0x06, 0x00, 0x01, 0x83, 0x00, 0x01, 0xC1, 0xC0, 0x01, + 0xE0, 0x60, 0x01, 0xE0, 0x38, 0x01, 0xB0, 0x0F, 0x03, 0x9F, 0x03, 0xFF, + 0x0F, 0x80, 0x7E, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x07, 0xFF, 0x80, 0x78, + 0x0F, 0x03, 0x80, 0x0E, 0x1C, 0x00, 0x18, 0xE0, 0x00, 0x73, 0x00, 0x00, + 0xD8, 0x00, 0x03, 0x60, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x00, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x60, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x03, 0x07, 0x80, 0xF8, + 0x0F, 0xFF, 0x80, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0xFF, + 0xF0, 0x00, 0xF0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x07, 0xFF, 0xFC, 0x03, + 0xFF, 0xFF, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x0F, 0xFF, 0xFC, 0x03, 0xFF, 0xFE, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x87, 0xC1, 0xE0, 0xF3, 0xE1, 0xC0, + 0x1B, 0x01, 0xC0, 0x07, 0x81, 0xC0, 0x03, 0xC0, 0xC0, 0x00, 0xE0, 0xC0, + 0x00, 0x60, 0x60, 0x00, 0x30, 0x60, 0x00, 0x18, 0x30, 0x00, 0x0C, 0x18, + 0x00, 0x06, 0x0C, 0x00, 0x06, 0x06, 0x00, 0x03, 0x03, 0x00, 0x03, 0x81, + 0xC0, 0x01, 0xC0, 0x60, 0x01, 0xC0, 0x38, 0x03, 0x60, 0x0F, 0x07, 0x30, + 0x03, 0xFF, 0x18, 0x00, 0x7E, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x03, 0x80, 0x03, 0xFF, 0x80, 0x01, 0xFF, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x83, 0xF0, 0x01, + 0x8F, 0xF8, 0x01, 0x98, 0x1C, 0x03, 0xB0, 0x0E, 0x03, 0x40, 0x06, 0x03, + 0x80, 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x06, 0x07, 0x00, 0x06, 0x06, + 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x06, 0x00, 0x0C, 0x0C, + 0x00, 0x0C, 0x0C, 0x00, 0x1C, 0x0C, 0x00, 0x1C, 0x0C, 0x00, 0x18, 0x0C, + 0x00, 0x18, 0x18, 0x00, 0x18, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x00, + 0x07, 0x00, 0x00, 0xC0, 0x00, 0x38, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x1F, + 0xF0, 0x00, 0x06, 0x00, 0x01, 0xC0, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, + 0xC0, 0x00, 0x18, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x03, + 0x00, 0x00, 0x60, 0x00, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x0C, + 0x00, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x70, 0x00, + 0x07, 0x00, 0x00, 0x70, 0x00, 0x06, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x03, 0xFF, 0xF0, + 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0x60, 0x00, 0x06, + 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0x00, + 0xC0, 0x00, 0x0C, 0x00, 0x01, 0xC0, 0x00, 0x18, 0x00, 0x01, 0x80, 0x00, + 0x18, 0x00, 0x01, 0x80, 0x00, 0x38, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, + 0x03, 0x00, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, + 0x03, 0x80, 0xFF, 0xF0, 0x0F, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0xC1, + 0xFF, 0x00, 0x80, 0x70, 0x01, 0x80, 0xC0, 0x01, 0x83, 0x80, 0x01, 0x87, + 0x00, 0x01, 0x8C, 0x00, 0x03, 0x38, 0x00, 0x03, 0x70, 0x00, 0x03, 0xF8, + 0x00, 0x03, 0x9C, 0x00, 0x03, 0x0C, 0x00, 0x06, 0x0E, 0x00, 0x06, 0x07, + 0x00, 0x06, 0x03, 0x80, 0x06, 0x01, 0x80, 0x04, 0x00, 0xC0, 0x0C, 0x00, + 0xE0, 0xFC, 0x03, 0xFE, 0xFC, 0x03, 0xFC, 0x01, 0xFF, 0x00, 0x3F, 0xE0, + 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x0C, 0x00, 0x01, 0x80, + 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, + 0x01, 0x80, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x18, 0x00, + 0x06, 0x00, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0xE0, 0x00, + 0x18, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00, 0x0C, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x1E, 0x07, 0x81, 0xE7, 0xE1, 0xF8, 0x3D, 0x8E, 0xE3, + 0x81, 0xE0, 0xF8, 0x30, 0x38, 0x1E, 0x06, 0x06, 0x03, 0x80, 0xC1, 0x80, + 0x60, 0x18, 0x30, 0x0C, 0x03, 0x06, 0x01, 0x80, 0x60, 0xC0, 0x30, 0x08, + 0x18, 0x0C, 0x03, 0x06, 0x01, 0x80, 0x60, 0xC0, 0x30, 0x0C, 0x18, 0x06, + 0x01, 0x83, 0x00, 0x80, 0x60, 0x40, 0x30, 0x0C, 0x18, 0x06, 0x01, 0x83, + 0x00, 0xC0, 0x30, 0x60, 0x18, 0x06, 0x7F, 0x03, 0xC1, 0xFF, 0xE0, 0xF8, + 0x3E, 0x00, 0x03, 0xE0, 0x1F, 0x1F, 0xF0, 0x3E, 0x60, 0x70, 0x0F, 0x80, + 0x70, 0x3C, 0x00, 0x60, 0x70, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, 0x03, + 0x07, 0x00, 0x06, 0x0C, 0x00, 0x1C, 0x18, 0x00, 0x30, 0x30, 0x00, 0x60, + 0x60, 0x00, 0xC1, 0xC0, 0x01, 0x83, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, + 0x00, 0x18, 0x18, 0x00, 0x30, 0x70, 0x00, 0x67, 0xFC, 0x07, 0xFF, 0xF0, + 0x0F, 0xE0, 0x00, 0x3F, 0x00, 0x07, 0xFF, 0x00, 0x3C, 0x0F, 0x01, 0xC0, + 0x1C, 0x0C, 0x00, 0x38, 0x60, 0x00, 0x63, 0x00, 0x00, 0xDC, 0x00, 0x03, + 0x60, 0x00, 0x0D, 0x80, 0x00, 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, + 0x00, 0x1B, 0x00, 0x00, 0x6C, 0x00, 0x03, 0xB0, 0x00, 0x0C, 0x60, 0x00, + 0x61, 0xC0, 0x03, 0x03, 0x80, 0x38, 0x0F, 0x03, 0xC0, 0x0F, 0xFE, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x1F, 0x8F, 0xFE, 0x00, 0xFC, + 0xE0, 0x78, 0x00, 0xCC, 0x00, 0xE0, 0x06, 0xC0, 0x03, 0x00, 0x3C, 0x00, + 0x1C, 0x01, 0xC0, 0x00, 0x60, 0x0C, 0x00, 0x03, 0x00, 0xE0, 0x00, 0x18, + 0x06, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x06, 0x01, 0x80, 0x00, 0x30, 0x0C, + 0x00, 0x03, 0x00, 0xE0, 0x00, 0x18, 0x07, 0x00, 0x01, 0x80, 0x3C, 0x00, + 0x1C, 0x01, 0xE0, 0x01, 0xC0, 0x0D, 0x80, 0x1C, 0x00, 0xCF, 0x03, 0xC0, + 0x06, 0x3F, 0xF8, 0x00, 0x30, 0x7F, 0x00, 0x01, 0x80, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, + 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7F, 0xE1, 0xF0, 0x78, + 0x1C, 0xFC, 0x38, 0x01, 0xB0, 0x1C, 0x00, 0x2C, 0x0E, 0x00, 0x0F, 0x03, + 0x00, 0x01, 0xC1, 0x80, 0x00, 0x60, 0x60, 0x00, 0x18, 0x30, 0x00, 0x06, + 0x0C, 0x00, 0x01, 0x83, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x30, 0x30, 0x00, + 0x0C, 0x0C, 0x00, 0x07, 0x03, 0x80, 0x03, 0xC0, 0x60, 0x01, 0xB0, 0x1C, + 0x00, 0xD8, 0x03, 0xC0, 0xE6, 0x00, 0x7F, 0xF1, 0x80, 0x07, 0xE0, 0x60, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x7F, 0xF8, 0x00, 0x1F, 0xFE, 0x00, 0x07, 0xF0, 0x3E, 0x03, 0xF8, 0x7F, + 0xC0, 0x18, 0xF0, 0x60, 0x0C, 0xE0, 0x00, 0x07, 0xE0, 0x00, 0x03, 0xC0, + 0x00, 0x03, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x3F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x3F, 0x00, + 0x0F, 0xFE, 0xC0, 0xF0, 0x3E, 0x0E, 0x00, 0x70, 0xE0, 0x01, 0x06, 0x00, + 0x08, 0x30, 0x00, 0x41, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x3F, 0xF0, 0x00, + 0x3F, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, + 0xCC, 0x00, 0x06, 0x60, 0x00, 0x33, 0x00, 0x03, 0x3C, 0x00, 0x71, 0xF8, + 0x0F, 0x0D, 0xFF, 0xF0, 0x01, 0xFC, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01, + 0x80, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x70, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xC0, + 0x00, 0xE0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x0E, + 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xC0, 0x03, 0x38, 0x0F, + 0x9F, 0xFF, 0x03, 0xF8, 0x00, 0xFC, 0x03, 0xFF, 0xE0, 0x1F, 0xC6, 0x00, + 0x0C, 0x30, 0x00, 0x61, 0x80, 0x03, 0x0C, 0x00, 0x18, 0x60, 0x01, 0x86, + 0x00, 0x0C, 0x30, 0x00, 0x61, 0x80, 0x03, 0x0C, 0x00, 0x18, 0x60, 0x01, + 0x86, 0x00, 0x0C, 0x30, 0x00, 0x61, 0x80, 0x03, 0x0C, 0x00, 0x38, 0x60, + 0x07, 0x83, 0x80, 0x6C, 0x1E, 0x1E, 0x7C, 0x7F, 0xE3, 0xE0, 0xF8, 0x00, + 0x00, 0x7F, 0xC0, 0xFF, 0xFF, 0xF0, 0x3F, 0xF1, 0xC0, 0x00, 0xC0, 0x30, + 0x00, 0x60, 0x0C, 0x00, 0x18, 0x03, 0x00, 0x0C, 0x00, 0xE0, 0x06, 0x00, + 0x18, 0x01, 0x80, 0x06, 0x00, 0xC0, 0x01, 0x80, 0x30, 0x00, 0x60, 0x18, + 0x00, 0x0C, 0x0C, 0x00, 0x03, 0x03, 0x00, 0x00, 0xC1, 0x80, 0x00, 0x30, + 0xC0, 0x00, 0x06, 0x30, 0x00, 0x01, 0x98, 0x00, 0x00, 0x6C, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x07, 0x80, 0x00, 0xFE, 0x00, 0x7F, 0xFF, 0x00, 0x3F, + 0xCC, 0x00, 0x03, 0x06, 0x00, 0x01, 0x83, 0x00, 0x01, 0x81, 0x81, 0x80, + 0xC0, 0xC1, 0xE0, 0x60, 0x60, 0xF0, 0x60, 0x30, 0xD8, 0x30, 0x18, 0x6C, + 0x30, 0x0C, 0x66, 0x18, 0x06, 0x33, 0x18, 0x03, 0x31, 0x8C, 0x01, 0x98, + 0x66, 0x00, 0xD8, 0x36, 0x00, 0x6C, 0x1B, 0x00, 0x3C, 0x0F, 0x00, 0x1E, + 0x07, 0x80, 0x0E, 0x03, 0x80, 0x07, 0x01, 0xC0, 0x00, 0x07, 0xF0, 0x3F, + 0xC3, 0xFC, 0x0F, 0xF0, 0x38, 0x00, 0x60, 0x07, 0x00, 0x70, 0x00, 0xE0, + 0x38, 0x00, 0x18, 0x1C, 0x00, 0x03, 0x0C, 0x00, 0x00, 0xEE, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xDC, 0x00, + 0x00, 0xE3, 0x80, 0x00, 0x70, 0x70, 0x00, 0x38, 0x0E, 0x00, 0x18, 0x01, + 0x80, 0x1C, 0x00, 0x30, 0x0E, 0x00, 0x0E, 0x0F, 0xF0, 0x3F, 0xE3, 0xFC, + 0x0F, 0xF8, 0x03, 0xF8, 0x07, 0xF8, 0x3F, 0xC0, 0x3F, 0xC0, 0x60, 0x00, + 0x30, 0x01, 0x80, 0x01, 0x80, 0x0C, 0x00, 0x18, 0x00, 0x60, 0x01, 0x80, + 0x03, 0x80, 0x0C, 0x00, 0x0C, 0x00, 0xC0, 0x00, 0x60, 0x0C, 0x00, 0x03, + 0x00, 0x60, 0x00, 0x0C, 0x06, 0x00, 0x00, 0x60, 0x60, 0x00, 0x03, 0x06, + 0x00, 0x00, 0x1C, 0x30, 0x00, 0x00, 0x63, 0x00, 0x00, 0x03, 0x30, 0x00, + 0x00, 0x19, 0x80, 0x00, 0x00, 0x78, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xF8, 0x3F, 0xFF, 0xC3, 0x00, 0x0C, 0x18, 0x00, 0xC0, 0xC0, + 0x0C, 0x00, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, + 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x18, 0x00, 0x01, 0x80, + 0x00, 0x18, 0x00, 0x01, 0x80, 0x0C, 0x18, 0x00, 0x61, 0x80, 0x02, 0x1F, + 0xFF, 0xF0, 0xFF, 0xFF, 0x80, 0x00, 0x0E, 0x00, 0x7C, 0x01, 0xC0, 0x03, + 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x01, 0xC0, 0x03, 0x00, + 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x60, 0x01, 0xC0, 0x0F, 0x00, 0xF8, + 0x01, 0xF0, 0x00, 0x30, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x03, 0x80, + 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0xE0, 0x01, 0x80, 0x03, + 0x00, 0x06, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x01, 0x80, 0xC0, + 0x60, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x40, + 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0xC0, 0x60, + 0x30, 0x18, 0x08, 0x0C, 0x06, 0x03, 0x01, 0x80, 0x80, 0xC0, 0x60, 0x30, + 0x00, 0x01, 0xC0, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, + 0x00, 0x0C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, + 0x0C, 0x00, 0x18, 0x00, 0x38, 0x00, 0x38, 0x00, 0x3E, 0x00, 0x7C, 0x03, + 0xC0, 0x0E, 0x00, 0x18, 0x00, 0x70, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, + 0x06, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x03, 0x00, 0x0E, + 0x00, 0xF8, 0x01, 0xC0, 0x00, 0x0F, 0x00, 0x01, 0xFC, 0x03, 0x70, 0xE0, + 0x7E, 0x07, 0x1E, 0xC0, 0x3F, 0x80, 0x01, 0xE0}; + +const GFXglyph FreeMonoOblique24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 28, 0, 1}, // 0x20 ' ' + {0, 10, 30, 28, 12, -28}, // 0x21 '!' + {38, 16, 14, 28, 10, -28}, // 0x22 '"' + {66, 22, 32, 28, 6, -29}, // 0x23 '#' + {154, 21, 33, 28, 6, -29}, // 0x24 '$' + {241, 22, 29, 28, 6, -27}, // 0x25 '%' + {321, 19, 25, 28, 6, -23}, // 0x26 '&' + {381, 7, 14, 28, 16, -28}, // 0x27 ''' + {394, 11, 34, 28, 16, -27}, // 0x28 '(' + {441, 11, 34, 28, 7, -27}, // 0x29 ')' + {488, 18, 17, 28, 10, -28}, // 0x2A '*' + {527, 22, 22, 28, 6, -23}, // 0x2B '+' + {588, 12, 14, 28, 5, -6}, // 0x2C ',' + {609, 22, 2, 28, 6, -13}, // 0x2D '-' + {615, 7, 6, 28, 11, -4}, // 0x2E '.' + {621, 24, 35, 28, 5, -30}, // 0x2F '/' + {726, 20, 30, 28, 7, -28}, // 0x30 '0' + {801, 17, 29, 28, 6, -28}, // 0x31 '1' + {863, 23, 29, 28, 4, -28}, // 0x32 '2' + {947, 22, 30, 28, 5, -28}, // 0x33 '3' + {1030, 19, 28, 28, 7, -27}, // 0x34 '4' + {1097, 21, 29, 28, 6, -27}, // 0x35 '5' + {1174, 21, 30, 28, 9, -28}, // 0x36 '6' + {1253, 18, 28, 28, 10, -27}, // 0x37 '7' + {1316, 20, 30, 28, 7, -28}, // 0x38 '8' + {1391, 22, 30, 28, 6, -28}, // 0x39 '9' + {1474, 10, 21, 28, 11, -19}, // 0x3A ':' + {1501, 15, 27, 28, 5, -19}, // 0x3B ';' + {1552, 23, 22, 28, 6, -23}, // 0x3C '<' + {1616, 25, 9, 28, 4, -17}, // 0x3D '=' + {1645, 24, 22, 28, 4, -23}, // 0x3E '>' + {1711, 16, 28, 28, 11, -26}, // 0x3F '?' + {1767, 19, 32, 28, 7, -28}, // 0x40 '@' + {1843, 27, 26, 28, 1, -25}, // 0x41 'A' + {1931, 26, 26, 28, 2, -25}, // 0x42 'B' + {2016, 25, 28, 28, 5, -26}, // 0x43 'C' + {2104, 26, 26, 28, 2, -25}, // 0x44 'D' + {2189, 27, 26, 28, 2, -25}, // 0x45 'E' + {2277, 28, 26, 28, 2, -25}, // 0x46 'F' + {2368, 25, 28, 28, 5, -26}, // 0x47 'G' + {2456, 27, 26, 28, 3, -25}, // 0x48 'H' + {2544, 22, 26, 28, 6, -25}, // 0x49 'I' + {2616, 28, 27, 28, 5, -25}, // 0x4A 'J' + {2711, 29, 26, 28, 2, -25}, // 0x4B 'K' + {2806, 25, 26, 28, 3, -25}, // 0x4C 'L' + {2888, 32, 26, 28, 1, -25}, // 0x4D 'M' + {2992, 30, 26, 28, 2, -25}, // 0x4E 'N' + {3090, 24, 28, 28, 5, -26}, // 0x4F 'O' + {3174, 26, 26, 28, 2, -25}, // 0x50 'P' + {3259, 24, 32, 28, 5, -26}, // 0x51 'Q' + {3355, 26, 26, 28, 2, -25}, // 0x52 'R' + {3440, 24, 28, 28, 5, -26}, // 0x53 'S' + {3524, 24, 26, 28, 7, -25}, // 0x54 'T' + {3602, 26, 27, 28, 6, -25}, // 0x55 'U' + {3690, 27, 26, 28, 6, -25}, // 0x56 'V' + {3778, 27, 26, 28, 6, -25}, // 0x57 'W' + {3866, 29, 26, 28, 2, -25}, // 0x58 'X' + {3961, 24, 26, 28, 7, -25}, // 0x59 'Y' + {4039, 23, 26, 28, 5, -25}, // 0x5A 'Z' + {4114, 15, 34, 28, 12, -27}, // 0x5B '[' + {4178, 10, 35, 28, 12, -30}, // 0x5C '\' + {4222, 15, 34, 28, 6, -27}, // 0x5D ']' + {4286, 18, 12, 28, 9, -28}, // 0x5E '^' + {4313, 28, 2, 28, -1, 5}, // 0x5F '_' + {4320, 6, 7, 28, 13, -29}, // 0x60 '`' + {4326, 22, 22, 28, 4, -20}, // 0x61 'a' + {4387, 27, 29, 28, 1, -27}, // 0x62 'b' + {4485, 22, 22, 28, 6, -20}, // 0x63 'c' + {4546, 25, 29, 28, 5, -27}, // 0x64 'd' + {4637, 22, 22, 28, 5, -20}, // 0x65 'e' + {4698, 26, 28, 28, 5, -27}, // 0x66 'f' + {4789, 25, 30, 28, 5, -20}, // 0x67 'g' + {4883, 24, 28, 28, 3, -27}, // 0x68 'h' + {4967, 19, 29, 28, 5, -28}, // 0x69 'i' + {5036, 20, 38, 28, 4, -28}, // 0x6A 'j' + {5131, 24, 28, 28, 3, -27}, // 0x6B 'k' + {5215, 19, 28, 28, 5, -27}, // 0x6C 'l' + {5282, 27, 21, 28, 1, -20}, // 0x6D 'm' + {5353, 23, 21, 28, 3, -20}, // 0x6E 'n' + {5414, 22, 22, 28, 5, -20}, // 0x6F 'o' + {5475, 29, 30, 28, -1, -20}, // 0x70 'p' + {5584, 26, 30, 28, 5, -20}, // 0x71 'q' + {5682, 25, 20, 28, 4, -19}, // 0x72 'r' + {5745, 21, 22, 28, 5, -20}, // 0x73 's' + {5803, 17, 27, 28, 7, -25}, // 0x74 't' + {5861, 21, 21, 28, 6, -19}, // 0x75 'u' + {5917, 26, 20, 28, 5, -19}, // 0x76 'v' + {5982, 25, 20, 28, 6, -19}, // 0x77 'w' + {6045, 26, 20, 28, 3, -19}, // 0x78 'x' + {6110, 29, 29, 28, 1, -19}, // 0x79 'y' + {6216, 21, 20, 28, 5, -19}, // 0x7A 'z' + {6269, 15, 34, 28, 10, -27}, // 0x7B '{' + {6333, 9, 35, 28, 12, -28}, // 0x7C '|' + {6373, 15, 34, 28, 8, -27}, // 0x7D '}' + {6437, 20, 6, 28, 7, -15}}; // 0x7E '~' + +const GFXfont FreeMonoOblique24pt7b PROGMEM = { + (uint8_t *)FreeMonoOblique24pt7bBitmaps, + (GFXglyph *)FreeMonoOblique24pt7bGlyphs, 0x20, 0x7E, 47}; + +// Approx. 7124 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique9pt7b.h new file mode 100644 index 0000000..e39ef49 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeMonoOblique9pt7b.h @@ -0,0 +1,186 @@ +const uint8_t FreeMonoOblique9pt7bBitmaps[] PROGMEM = { + 0x11, 0x22, 0x24, 0x40, 0x00, 0xC0, 0xDE, 0xE5, 0x29, 0x00, 0x09, 0x05, + 0x02, 0x82, 0x47, 0xF8, 0xA0, 0x51, 0xFE, 0x28, 0x14, 0x0A, 0x09, 0x00, + 0x08, 0x1D, 0x23, 0x40, 0x70, 0x1C, 0x02, 0x82, 0x84, 0x78, 0x20, 0x20, + 0x1C, 0x11, 0x08, 0x83, 0x80, 0x18, 0x71, 0xC0, 0x1C, 0x11, 0x08, 0x83, + 0x80, 0x1E, 0x60, 0x81, 0x03, 0x0A, 0x65, 0x46, 0x88, 0xE8, 0xFA, 0x80, + 0x12, 0x24, 0x48, 0x88, 0x88, 0x88, 0x80, 0x01, 0x11, 0x11, 0x11, 0x22, + 0x44, 0x80, 0x10, 0x22, 0x5B, 0xC3, 0x0A, 0x22, 0x00, 0x04, 0x02, 0x02, + 0x1F, 0xF0, 0x80, 0x40, 0x20, 0x00, 0x36, 0x4C, 0x80, 0xFF, 0x80, 0xF0, + 0x00, 0x80, 0x80, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, + 0x08, 0x08, 0x00, 0x1C, 0x45, 0x0A, 0x18, 0x30, 0x61, 0x42, 0x85, 0x11, + 0xC0, 0x04, 0x38, 0x90, 0x20, 0x81, 0x02, 0x04, 0x08, 0x23, 0xF8, 0x07, + 0x04, 0xC4, 0x20, 0x10, 0x10, 0x30, 0x20, 0x20, 0x60, 0x40, 0x3F, 0x80, + 0x0F, 0x00, 0x40, 0x20, 0x20, 0x60, 0x18, 0x04, 0x02, 0x01, 0x43, 0x1E, + 0x00, 0x03, 0x05, 0x0A, 0x12, 0x22, 0x22, 0x42, 0x7F, 0x04, 0x04, 0x1E, + 0x1F, 0x88, 0x08, 0x05, 0xC3, 0x30, 0x08, 0x04, 0x02, 0x02, 0x42, 0x1E, + 0x00, 0x07, 0x18, 0x20, 0x40, 0x5C, 0xA6, 0xC2, 0x82, 0x82, 0xC4, 0x78, + 0xFF, 0x04, 0x10, 0x20, 0x82, 0x04, 0x10, 0x20, 0x81, 0x00, 0x1E, 0x23, + 0x41, 0x41, 0x62, 0x1C, 0x66, 0x82, 0x82, 0x84, 0x78, 0x1E, 0x23, 0x41, + 0x41, 0x43, 0x65, 0x3A, 0x02, 0x04, 0x18, 0xE0, 0x6C, 0x00, 0x36, 0x18, + 0xC0, 0x00, 0x19, 0x8C, 0xC4, 0x00, 0x01, 0x83, 0x06, 0x0C, 0x06, 0x00, + 0x80, 0x30, 0x04, 0xFF, 0x80, 0x00, 0x1F, 0xF0, 0x20, 0x0C, 0x01, 0x00, + 0x60, 0x20, 0x60, 0xC1, 0x80, 0x3D, 0x8E, 0x08, 0x10, 0xC6, 0x08, 0x00, + 0x01, 0x80, 0x1C, 0x45, 0x0A, 0x79, 0x34, 0x69, 0x4E, 0x81, 0x03, 0x03, + 0xC0, 0x0F, 0x00, 0x60, 0x12, 0x02, 0x40, 0x88, 0x21, 0x07, 0xE1, 0x04, + 0x20, 0x5E, 0x3C, 0x3F, 0x84, 0x11, 0x04, 0x82, 0x3F, 0x88, 0x32, 0x04, + 0x81, 0x60, 0xBF, 0xC0, 0x1E, 0x98, 0xD0, 0x28, 0x08, 0x04, 0x02, 0x01, + 0x00, 0x41, 0x1F, 0x00, 0x3F, 0x0C, 0x22, 0x04, 0x81, 0x20, 0x48, 0x12, + 0x09, 0x02, 0x43, 0x3F, 0x00, 0x3F, 0xC4, 0x11, 0x00, 0x88, 0x3E, 0x08, + 0x82, 0x00, 0x82, 0x60, 0xBF, 0xE0, 0x3F, 0xE2, 0x08, 0x40, 0x11, 0x03, + 0xE0, 0x44, 0x08, 0x01, 0x00, 0x60, 0x1F, 0x00, 0x1E, 0x98, 0xD0, 0x08, + 0x08, 0x04, 0x7A, 0x05, 0x02, 0x41, 0x1F, 0x00, 0x3D, 0xE2, 0x18, 0x42, + 0x08, 0x43, 0xF8, 0x41, 0x08, 0x21, 0x08, 0x21, 0x1E, 0xF0, 0x3F, 0x82, + 0x02, 0x01, 0x00, 0x80, 0x40, 0x20, 0x20, 0x10, 0x7F, 0x00, 0x0F, 0xE0, + 0x20, 0x04, 0x00, 0x80, 0x10, 0x02, 0x20, 0x84, 0x10, 0x84, 0x0F, 0x00, + 0x3C, 0xE2, 0x10, 0x44, 0x11, 0x02, 0xC0, 0x64, 0x08, 0x81, 0x08, 0x61, + 0x1E, 0x38, 0x3E, 0x02, 0x00, 0x80, 0x20, 0x10, 0x04, 0x01, 0x04, 0x42, + 0x10, 0xBF, 0xE0, 0x38, 0x38, 0xC3, 0x05, 0x28, 0x29, 0x42, 0x52, 0x13, + 0x10, 0x99, 0x84, 0x08, 0x20, 0x47, 0x8F, 0x00, 0x70, 0xE6, 0x08, 0xA1, + 0x14, 0x22, 0x48, 0x49, 0x11, 0x22, 0x14, 0x43, 0x1E, 0x20, 0x1E, 0x18, + 0x90, 0x28, 0x18, 0x0C, 0x06, 0x05, 0x02, 0x46, 0x1E, 0x00, 0x3F, 0x84, + 0x31, 0x04, 0x81, 0x20, 0x8F, 0xC2, 0x00, 0x80, 0x60, 0x3E, 0x00, 0x1E, + 0x18, 0x90, 0x28, 0x18, 0x0C, 0x06, 0x05, 0x02, 0x46, 0x1E, 0x08, 0x0F, + 0x44, 0x60, 0x3F, 0x84, 0x31, 0x04, 0x81, 0x20, 0x8F, 0xC2, 0x10, 0x84, + 0x60, 0xBC, 0x10, 0x0F, 0x88, 0xC8, 0x24, 0x01, 0x80, 0x38, 0x05, 0x02, + 0xC2, 0x5E, 0x00, 0xFF, 0xC4, 0x44, 0x02, 0x01, 0x00, 0x80, 0x40, 0x60, + 0x20, 0x7E, 0x00, 0xF1, 0xD0, 0x24, 0x09, 0x02, 0x41, 0xA0, 0x48, 0x12, + 0x04, 0xC6, 0x1F, 0x00, 0xF1, 0xE8, 0x11, 0x02, 0x20, 0x82, 0x20, 0x44, + 0x09, 0x01, 0x40, 0x28, 0x02, 0x00, 0xF1, 0xE8, 0x09, 0x12, 0x26, 0x45, + 0x48, 0xAA, 0x29, 0x45, 0x28, 0xC6, 0x18, 0xC0, 0x38, 0xE2, 0x08, 0x26, + 0x05, 0x00, 0x40, 0x18, 0x04, 0x81, 0x08, 0x41, 0x1C, 0x70, 0xE3, 0xA0, + 0x90, 0x84, 0x81, 0x80, 0x80, 0x40, 0x20, 0x20, 0x7E, 0x00, 0x3F, 0x90, + 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x82, 0x7F, 0x00, 0x39, 0x08, + 0x44, 0x21, 0x08, 0x42, 0x21, 0x0E, 0x00, 0x88, 0x44, 0x44, 0x22, 0x22, + 0x11, 0x11, 0x38, 0x42, 0x11, 0x08, 0x42, 0x10, 0x84, 0x2E, 0x00, 0x08, + 0x28, 0x92, 0x18, 0x20, 0xFF, 0xC0, 0xA4, 0x3E, 0x00, 0x80, 0x47, 0xA4, + 0x34, 0x12, 0x18, 0xF7, 0x38, 0x01, 0x00, 0x40, 0x09, 0xE1, 0xC6, 0x20, + 0x44, 0x09, 0x01, 0x30, 0x46, 0x13, 0xBC, 0x00, 0x1F, 0x48, 0x74, 0x0A, + 0x00, 0x80, 0x20, 0x0C, 0x18, 0xF8, 0x01, 0x80, 0x40, 0x23, 0x96, 0x32, + 0x0A, 0x05, 0x02, 0x81, 0x61, 0x1F, 0xE0, 0x1F, 0x30, 0xD0, 0x3F, 0xF8, + 0x04, 0x01, 0x00, 0x7C, 0x07, 0xC3, 0x00, 0x80, 0xFE, 0x10, 0x04, 0x01, + 0x00, 0x40, 0x10, 0x08, 0x0F, 0xE0, 0x1D, 0xD8, 0xC4, 0x12, 0x04, 0x82, + 0x20, 0x8C, 0x61, 0xE8, 0x02, 0x01, 0x07, 0x80, 0x30, 0x04, 0x01, 0x00, + 0x5C, 0x38, 0x88, 0x22, 0x08, 0x82, 0x21, 0x18, 0x4F, 0x3C, 0x04, 0x04, + 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0xFF, 0x01, 0x00, 0x80, + 0x03, 0xF0, 0x10, 0x08, 0x04, 0x02, 0x02, 0x01, 0x00, 0x80, 0x40, 0x47, + 0xC0, 0x38, 0x08, 0x04, 0x02, 0x71, 0x20, 0xA0, 0xA0, 0x68, 0x24, 0x11, + 0x38, 0xE0, 0x3C, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, + 0xFF, 0x3E, 0xE2, 0x64, 0x88, 0x91, 0x12, 0x24, 0x48, 0x91, 0x17, 0x33, + 0x37, 0x14, 0x4C, 0x24, 0x12, 0x09, 0x08, 0x85, 0xE3, 0x1E, 0x10, 0x90, + 0x30, 0x18, 0x0C, 0x0B, 0x08, 0x78, 0x33, 0xC3, 0x8C, 0x40, 0x88, 0x12, + 0x02, 0x60, 0x8C, 0x31, 0x78, 0x20, 0x08, 0x03, 0xE0, 0x00, 0x1C, 0xD8, + 0xC4, 0x12, 0x04, 0x81, 0x20, 0x4C, 0x21, 0xF8, 0x02, 0x00, 0x81, 0xF0, + 0x73, 0x8E, 0x04, 0x04, 0x02, 0x01, 0x00, 0x81, 0xFC, 0x1F, 0x61, 0x40, + 0x3C, 0x03, 0x81, 0x82, 0xFC, 0x10, 0x63, 0xF9, 0x02, 0x04, 0x10, 0x20, + 0x40, 0x7C, 0xE3, 0x10, 0x90, 0x48, 0x24, 0x22, 0x11, 0x18, 0xF6, 0xF3, + 0xD0, 0x44, 0x10, 0x88, 0x24, 0x09, 0x02, 0x80, 0x40, 0xE1, 0xD0, 0x24, + 0x91, 0x24, 0x55, 0x19, 0x86, 0x61, 0x10, 0x39, 0xC4, 0x20, 0xB0, 0x30, + 0x0C, 0x04, 0x86, 0x13, 0x8E, 0x3C, 0x71, 0x04, 0x10, 0x40, 0x88, 0x09, + 0x00, 0xA0, 0x06, 0x00, 0x40, 0x08, 0x01, 0x00, 0xFC, 0x00, 0x7F, 0x42, + 0x04, 0x08, 0x10, 0x20, 0x42, 0xFE, 0x0C, 0x41, 0x04, 0x30, 0x8C, 0x08, + 0x21, 0x04, 0x10, 0x60, 0x24, 0x94, 0x92, 0x52, 0x40, 0x18, 0x20, 0x82, + 0x10, 0x40, 0xC4, 0x10, 0x82, 0x08, 0xC0, 0x61, 0x24, 0x30}; + +const GFXglyph FreeMonoOblique9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 11, 0, 1}, // 0x20 ' ' + {0, 4, 11, 11, 4, -10}, // 0x21 '!' + {6, 5, 5, 11, 4, -10}, // 0x22 '"' + {10, 9, 12, 11, 2, -10}, // 0x23 '#' + {24, 8, 12, 11, 3, -10}, // 0x24 '$' + {36, 9, 11, 11, 2, -10}, // 0x25 '%' + {49, 7, 10, 11, 2, -9}, // 0x26 '&' + {58, 2, 5, 11, 6, -10}, // 0x27 ''' + {60, 4, 13, 11, 6, -10}, // 0x28 '(' + {67, 4, 13, 11, 3, -10}, // 0x29 ')' + {74, 7, 7, 11, 4, -10}, // 0x2A '*' + {81, 9, 8, 11, 2, -8}, // 0x2B '+' + {90, 4, 5, 11, 2, -1}, // 0x2C ',' + {93, 9, 1, 11, 2, -5}, // 0x2D '-' + {95, 2, 2, 11, 4, -1}, // 0x2E '.' + {96, 9, 13, 11, 2, -11}, // 0x2F '/' + {111, 7, 11, 11, 3, -10}, // 0x30 '0' + {121, 7, 11, 11, 2, -10}, // 0x31 '1' + {131, 9, 11, 11, 2, -10}, // 0x32 '2' + {144, 9, 11, 11, 2, -10}, // 0x33 '3' + {157, 8, 11, 11, 2, -10}, // 0x34 '4' + {168, 9, 11, 11, 2, -10}, // 0x35 '5' + {181, 8, 11, 11, 3, -10}, // 0x36 '6' + {192, 7, 11, 11, 4, -10}, // 0x37 '7' + {202, 8, 11, 11, 3, -10}, // 0x38 '8' + {213, 8, 11, 11, 3, -10}, // 0x39 '9' + {224, 3, 8, 11, 4, -7}, // 0x3A ':' + {227, 5, 11, 11, 2, -7}, // 0x3B ';' + {234, 9, 8, 11, 2, -8}, // 0x3C '<' + {243, 9, 4, 11, 2, -6}, // 0x3D '=' + {248, 9, 8, 11, 2, -8}, // 0x3E '>' + {257, 7, 10, 11, 4, -9}, // 0x3F '?' + {266, 7, 12, 11, 3, -10}, // 0x40 '@' + {277, 11, 10, 11, 0, -9}, // 0x41 'A' + {291, 10, 10, 11, 1, -9}, // 0x42 'B' + {304, 9, 10, 11, 2, -9}, // 0x43 'C' + {316, 10, 10, 11, 1, -9}, // 0x44 'D' + {329, 10, 10, 11, 1, -9}, // 0x45 'E' + {342, 11, 10, 11, 1, -9}, // 0x46 'F' + {356, 9, 10, 11, 2, -9}, // 0x47 'G' + {368, 11, 10, 11, 1, -9}, // 0x48 'H' + {382, 9, 10, 11, 2, -9}, // 0x49 'I' + {394, 11, 10, 11, 2, -9}, // 0x4A 'J' + {408, 11, 10, 11, 1, -9}, // 0x4B 'K' + {422, 10, 10, 11, 1, -9}, // 0x4C 'L' + {435, 13, 10, 11, 0, -9}, // 0x4D 'M' + {452, 11, 10, 11, 1, -9}, // 0x4E 'N' + {466, 9, 10, 11, 2, -9}, // 0x4F 'O' + {478, 10, 10, 11, 1, -9}, // 0x50 'P' + {491, 9, 13, 11, 2, -9}, // 0x51 'Q' + {506, 10, 10, 11, 1, -9}, // 0x52 'R' + {519, 9, 10, 11, 2, -9}, // 0x53 'S' + {531, 9, 10, 11, 3, -9}, // 0x54 'T' + {543, 10, 10, 11, 2, -9}, // 0x55 'U' + {556, 11, 10, 11, 2, -9}, // 0x56 'V' + {570, 11, 10, 11, 2, -9}, // 0x57 'W' + {584, 11, 10, 11, 1, -9}, // 0x58 'X' + {598, 9, 10, 11, 3, -9}, // 0x59 'Y' + {610, 9, 10, 11, 2, -9}, // 0x5A 'Z' + {622, 5, 13, 11, 5, -10}, // 0x5B '[' + {631, 4, 14, 11, 4, -11}, // 0x5C '\' + {638, 5, 13, 11, 2, -10}, // 0x5D ']' + {647, 7, 5, 11, 3, -10}, // 0x5E '^' + {652, 11, 1, 11, 0, 2}, // 0x5F '_' + {654, 2, 3, 11, 5, -11}, // 0x60 '`' + {655, 9, 8, 11, 2, -7}, // 0x61 'a' + {664, 11, 11, 11, 0, -10}, // 0x62 'b' + {680, 10, 8, 11, 2, -7}, // 0x63 'c' + {690, 9, 11, 11, 2, -10}, // 0x64 'd' + {703, 9, 8, 11, 2, -7}, // 0x65 'e' + {712, 10, 11, 11, 2, -10}, // 0x66 'f' + {726, 10, 11, 11, 2, -7}, // 0x67 'g' + {740, 10, 11, 11, 1, -10}, // 0x68 'h' + {754, 8, 11, 11, 2, -10}, // 0x69 'i' + {765, 9, 14, 11, 1, -10}, // 0x6A 'j' + {781, 9, 11, 11, 1, -10}, // 0x6B 'k' + {794, 8, 11, 11, 2, -10}, // 0x6C 'l' + {805, 11, 8, 11, 0, -7}, // 0x6D 'm' + {816, 9, 8, 11, 1, -7}, // 0x6E 'n' + {825, 9, 8, 11, 2, -7}, // 0x6F 'o' + {834, 11, 11, 11, 0, -7}, // 0x70 'p' + {850, 10, 11, 11, 2, -7}, // 0x71 'q' + {864, 9, 8, 11, 2, -7}, // 0x72 'r' + {873, 8, 8, 11, 2, -7}, // 0x73 's' + {881, 7, 10, 11, 2, -9}, // 0x74 't' + {890, 9, 8, 11, 2, -7}, // 0x75 'u' + {899, 10, 8, 11, 2, -7}, // 0x76 'v' + {909, 10, 8, 11, 2, -7}, // 0x77 'w' + {919, 10, 8, 11, 1, -7}, // 0x78 'x' + {929, 12, 11, 11, 0, -7}, // 0x79 'y' + {946, 8, 8, 11, 2, -7}, // 0x7A 'z' + {954, 6, 13, 11, 4, -10}, // 0x7B '{' + {964, 3, 12, 11, 5, -9}, // 0x7C '|' + {969, 6, 13, 11, 3, -10}, // 0x7D '}' + {979, 7, 3, 11, 3, -6}}; // 0x7E '~' + +const GFXfont FreeMonoOblique9pt7b PROGMEM = { + (uint8_t *)FreeMonoOblique9pt7bBitmaps, + (GFXglyph *)FreeMonoOblique9pt7bGlyphs, 0x20, 0x7E, 18}; + +// Approx. 1654 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSans12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSans12pt7b.h new file mode 100644 index 0000000..4e027fc --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSans12pt7b.h @@ -0,0 +1,269 @@ +const uint8_t FreeSans12pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xCF, 0x3C, 0xF3, 0x8A, 0x20, 0x06, 0x30, + 0x31, 0x03, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x03, 0x18, 0x18, + 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30, + 0x04, 0x03, 0xE1, 0xFF, 0x72, 0x6C, 0x47, 0x88, 0xF1, 0x07, 0x20, 0x7E, + 0x03, 0xF0, 0x17, 0x02, 0x3C, 0x47, 0x88, 0xF1, 0x1B, 0x26, 0x7F, 0xC3, + 0xE0, 0x10, 0x02, 0x00, 0x00, 0x06, 0x03, 0xC0, 0x40, 0x7E, 0x0C, 0x0E, + 0x70, 0x80, 0xC3, 0x18, 0x0C, 0x31, 0x00, 0xE7, 0x30, 0x07, 0xE6, 0x00, + 0x3C, 0x40, 0x00, 0x0C, 0x7C, 0x00, 0x8F, 0xE0, 0x19, 0xC7, 0x01, 0x18, + 0x30, 0x31, 0x83, 0x02, 0x1C, 0x70, 0x40, 0xFE, 0x04, 0x07, 0xC0, 0x0F, + 0x00, 0x7E, 0x03, 0x9C, 0x0C, 0x30, 0x30, 0xC0, 0xE7, 0x01, 0xF8, 0x03, + 0x80, 0x3E, 0x01, 0xCC, 0x6E, 0x19, 0xB0, 0x7C, 0xC0, 0xF3, 0x03, 0xCE, + 0x1F, 0x9F, 0xE6, 0x1E, 0x1C, 0xFF, 0xA0, 0x08, 0x8C, 0x66, 0x31, 0x98, + 0xC6, 0x31, 0x8C, 0x63, 0x08, 0x63, 0x08, 0x61, 0x0C, 0x20, 0x82, 0x18, + 0xC3, 0x18, 0xC3, 0x18, 0xC6, 0x31, 0x8C, 0x62, 0x31, 0x88, 0xC4, 0x62, + 0x00, 0x10, 0x23, 0x5B, 0xE3, 0x8D, 0x91, 0x00, 0x0C, 0x03, 0x00, 0xC0, + 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0xF5, 0x60, + 0xFF, 0xF0, 0xF0, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, + 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x30, + 0x6E, 0x0F, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, + 0x0E, 0xC1, 0x9C, 0x71, 0xFC, 0x1F, 0x00, 0x08, 0xCF, 0xFF, 0x8C, 0x63, + 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0x1F, 0x0F, 0xF9, 0x87, 0x60, 0x7C, + 0x06, 0x00, 0xC0, 0x18, 0x07, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x06, 0x00, + 0x80, 0x30, 0x07, 0xFF, 0xFF, 0xE0, 0x3F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, + 0x0C, 0x01, 0x80, 0x70, 0x7C, 0x0F, 0x80, 0x18, 0x01, 0x80, 0x3C, 0x07, + 0x80, 0xD8, 0x73, 0xFC, 0x1F, 0x00, 0x01, 0x80, 0x70, 0x0E, 0x03, 0xC0, + 0xD8, 0x1B, 0x06, 0x61, 0x8C, 0x21, 0x8C, 0x33, 0x06, 0x7F, 0xFF, 0xFE, + 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xCF, 0xF9, 0x80, 0x30, 0x06, + 0x00, 0xDE, 0x1F, 0xE7, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07, + 0x81, 0xF8, 0x73, 0xFC, 0x1F, 0x00, 0x0F, 0x07, 0xF9, 0xC3, 0x30, 0x74, + 0x01, 0x80, 0x33, 0xC7, 0xFE, 0xF0, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x06, + 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00, 0xFF, 0xFF, 0xFC, 0x01, 0x00, 0x60, + 0x18, 0x02, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x04, 0x01, 0x80, + 0x30, 0x06, 0x01, 0x80, 0x30, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x66, + 0x0C, 0xC1, 0x8C, 0x61, 0xFC, 0x3F, 0x8E, 0x3B, 0x01, 0xE0, 0x3C, 0x07, + 0x80, 0xD8, 0x31, 0xFC, 0x1F, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x6C, + 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x61, 0xEF, 0xFC, 0x79, 0x80, 0x30, 0x05, + 0x81, 0x98, 0x73, 0xFC, 0x1E, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0xF0, 0x00, + 0x0F, 0x56, 0x00, 0x00, 0x07, 0x01, 0xE0, 0xF8, 0x3C, 0x0F, 0x00, 0xE0, + 0x07, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x78, 0x01, 0xF0, 0x07, + 0xC0, 0x0F, 0x00, 0x70, 0x1E, 0x0F, 0x03, 0xC0, 0xF0, 0x08, 0x00, 0x1F, + 0x1F, 0xEE, 0x1B, 0x03, 0xC0, 0xC0, 0x30, 0x0C, 0x06, 0x03, 0x81, 0xC0, + 0xE0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xFE, + 0x00, 0x0F, 0xFE, 0x00, 0xF0, 0x3E, 0x07, 0x00, 0x3C, 0x38, 0x00, 0x30, + 0xC1, 0xE0, 0x66, 0x0F, 0xD9, 0xD8, 0x61, 0xC3, 0xC3, 0x07, 0x0F, 0x1C, + 0x1C, 0x3C, 0x60, 0x60, 0xF1, 0x81, 0x83, 0xC6, 0x06, 0x1B, 0x18, 0x38, + 0xEE, 0x71, 0xE7, 0x18, 0xFD, 0xF8, 0x71, 0xE7, 0xC0, 0xE0, 0x00, 0x01, + 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x01, 0xFC, 0x00, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x07, 0xE0, 0x06, 0x60, 0x06, 0x60, 0x0E, 0x70, 0x0C, 0x30, + 0x0C, 0x30, 0x1C, 0x38, 0x18, 0x18, 0x1F, 0xF8, 0x3F, 0xFC, 0x30, 0x1C, + 0x30, 0x0C, 0x70, 0x0E, 0x60, 0x06, 0x60, 0x06, 0xFF, 0xC7, 0xFF, 0x30, + 0x19, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF, + 0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, + 0xFE, 0x00, 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x66, 0x00, 0x6C, + 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0, + 0xFF, 0x83, 0xFF, 0x8C, 0x07, 0x30, 0x0E, 0xC0, 0x1B, 0x00, 0x7C, 0x00, + 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x00, + 0x6C, 0x03, 0xB0, 0x1C, 0xFF, 0xE3, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, + 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, + 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xFF, 0xDF, + 0xFB, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00, + 0x07, 0xF0, 0x1F, 0xFC, 0x3C, 0x1E, 0x70, 0x06, 0x60, 0x03, 0xE0, 0x00, + 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x03, + 0x60, 0x03, 0x60, 0x07, 0x30, 0x0F, 0x3C, 0x1F, 0x1F, 0xFB, 0x07, 0xE1, + 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, + 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, + 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, + 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, + 0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00, 0xC0, 0x3B, 0x01, 0xCC, + 0x0E, 0x30, 0x70, 0xC3, 0x83, 0x1C, 0x0C, 0xE0, 0x33, 0x80, 0xDE, 0x03, + 0xDC, 0x0E, 0x38, 0x30, 0x60, 0xC1, 0xC3, 0x03, 0x8C, 0x06, 0x30, 0x1C, + 0xC0, 0x3B, 0x00, 0x60, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, + 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, + 0xFF, 0xFF, 0xF0, 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0, + 0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC, + 0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC1, 0x83, 0xE0, 0x1F, 0x00, 0xFC, 0x07, 0xE0, 0x3D, 0x81, 0xEE, + 0x0F, 0x30, 0x79, 0xC3, 0xC6, 0x1E, 0x18, 0xF0, 0xE7, 0x83, 0x3C, 0x1D, + 0xE0, 0x6F, 0x01, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, 0xC0, 0x03, 0xE0, 0x0F, + 0xFC, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, + 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, + 0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00, + 0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x07, + 0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, + 0xC0, 0x0C, 0x00, 0x03, 0xE0, 0x0F, 0xFC, 0x0F, 0x07, 0x86, 0x00, 0xC6, + 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, + 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x01, 0x98, 0xC0, 0xFC, 0x78, + 0x3C, 0x1F, 0xFF, 0x03, 0xF9, 0x80, 0x00, 0x40, 0xFF, 0xC3, 0xFF, 0xCC, + 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3, + 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, + 0xC0, 0x1B, 0x00, 0x70, 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x9C, 0x07, 0x60, + 0x0D, 0x80, 0x06, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, + 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80, + 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, + 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, + 0xE0, 0x0F, 0x80, 0xEE, 0x0E, 0x3F, 0xE0, 0x7C, 0x00, 0x60, 0x06, 0xC0, + 0x1D, 0xC0, 0x31, 0x80, 0x63, 0x01, 0xC7, 0x03, 0x06, 0x06, 0x0C, 0x1C, + 0x1C, 0x30, 0x18, 0x60, 0x31, 0xC0, 0x73, 0x00, 0x66, 0x00, 0xDC, 0x01, + 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0xE0, 0x30, 0x1D, 0x80, 0xE0, + 0x76, 0x07, 0x81, 0xD8, 0x1E, 0x06, 0x70, 0x7C, 0x18, 0xC1, 0xB0, 0xE3, + 0x0C, 0xC3, 0x8C, 0x33, 0x0C, 0x38, 0xC6, 0x30, 0x67, 0x18, 0xC1, 0x98, + 0x67, 0x06, 0x61, 0xD8, 0x1D, 0x83, 0x60, 0x3C, 0x0D, 0x80, 0xF0, 0x3E, + 0x03, 0xC0, 0x70, 0x0F, 0x01, 0xC0, 0x18, 0x07, 0x00, 0x70, 0x0E, 0x60, + 0x38, 0xE0, 0x60, 0xE1, 0xC0, 0xC3, 0x01, 0xCC, 0x01, 0xF8, 0x01, 0xE0, + 0x03, 0x80, 0x07, 0x80, 0x1F, 0x00, 0x33, 0x00, 0xE7, 0x03, 0x86, 0x06, + 0x0E, 0x1C, 0x0E, 0x70, 0x0C, 0xC0, 0x1C, 0x60, 0x06, 0x70, 0x0E, 0x30, + 0x1C, 0x38, 0x18, 0x1C, 0x38, 0x0C, 0x30, 0x0E, 0x70, 0x06, 0x60, 0x03, + 0xC0, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xC0, 0x0E, + 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, + 0x80, 0x38, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x1C, 0x00, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, + 0xF0, 0x81, 0x81, 0x02, 0x06, 0x04, 0x08, 0x18, 0x10, 0x20, 0x60, 0x40, + 0x81, 0x81, 0x02, 0x06, 0x04, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x3F, 0xF0, 0x0C, 0x0E, 0x05, 0x86, 0xC3, 0x21, 0x19, + 0x8C, 0x83, 0xC1, 0x80, 0xFF, 0xFE, 0xE3, 0x8C, 0x30, 0x3F, 0x07, 0xF8, + 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, + 0xE3, 0xC7, 0xEF, 0x3C, 0x70, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, + 0x0C, 0xF8, 0xDF, 0xCF, 0x0E, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xE0, 0x6F, 0x0E, 0xDF, 0xCC, 0xF8, 0x1F, 0x0F, 0xE7, 0x1B, + 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, + 0x00, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x3C, 0xCF, 0xFB, 0x8F, + 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8F, 0x3F, + 0x63, 0xCC, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE, + 0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xE1, 0xF0, 0x3B, 0xD8, 0xC6, 0x7F, + 0xEC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x00, 0x1E, 0x67, 0xFD, 0xC7, + 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, + 0xB1, 0xE6, 0x00, 0xC0, 0x3E, 0x0E, 0x7F, 0xC7, 0xE0, 0xC0, 0x30, 0x0C, + 0x03, 0x00, 0xC0, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, + 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30, 0xF0, 0x3F, 0xFF, 0xFF, + 0xF0, 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, + 0xE0, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x83, 0x30, 0xC6, 0x30, + 0xCC, 0x1B, 0x83, 0xF0, 0x77, 0x0C, 0x61, 0x8E, 0x30, 0xE6, 0x0C, 0xC1, + 0xD8, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xCF, 0x1F, 0x6F, 0xDF, 0xFC, + 0x78, 0xFC, 0x18, 0x3C, 0x0C, 0x1E, 0x06, 0x0F, 0x03, 0x07, 0x81, 0x83, + 0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x3C, 0x0C, 0x18, 0xCF, + 0x37, 0xEF, 0x1F, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, + 0x0F, 0x03, 0xC0, 0xC0, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, + 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0, 0xCF, 0x8D, + 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, + 0x07, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00, + 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, + 0x7C, 0x1D, 0xC7, 0x9F, 0xF1, 0xE6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, + 0xCF, 0x7F, 0x38, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0, 0x3E, 0x1F, + 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3E, 0x01, 0xF0, 0x3E, 0x1D, + 0xFE, 0x3E, 0x00, 0x63, 0x19, 0xFF, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31, + 0xE7, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, + 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0, 0xE0, 0x66, 0x06, 0x60, 0x67, 0x0C, + 0x30, 0xC3, 0x0C, 0x39, 0x81, 0x98, 0x19, 0x81, 0xF0, 0x0F, 0x00, 0xE0, + 0x0E, 0x00, 0xC1, 0xC1, 0xB0, 0xE1, 0xD8, 0x70, 0xCC, 0x2C, 0x66, 0x36, + 0x31, 0x9B, 0x18, 0xCD, 0x98, 0x64, 0x6C, 0x16, 0x36, 0x0F, 0x1A, 0x07, + 0x8F, 0x03, 0x83, 0x80, 0xC1, 0xC0, 0x60, 0xEE, 0x18, 0xC6, 0x0C, 0xC1, + 0xF0, 0x1C, 0x01, 0x80, 0x78, 0x1B, 0x03, 0x30, 0xC7, 0x30, 0x66, 0x06, + 0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0E, 0x60, 0xCC, 0x1B, + 0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E, + 0x00, 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30, + 0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0, 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x31, + 0x99, 0x86, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1C, 0x60, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0xC7, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x0C, 0x33, 0x31, + 0x8C, 0x63, 0x18, 0xC6, 0x73, 0x00, 0x70, 0x3E, 0x09, 0xE4, 0x1F, 0x03, + 0x80}; + +const GFXglyph FreeSans12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 6, 0, 1}, // 0x20 ' ' + {0, 2, 18, 8, 3, -17}, // 0x21 '!' + {5, 6, 6, 8, 1, -16}, // 0x22 '"' + {10, 13, 16, 13, 0, -15}, // 0x23 '#' + {36, 11, 20, 13, 1, -17}, // 0x24 '$' + {64, 20, 17, 21, 1, -16}, // 0x25 '%' + {107, 14, 17, 16, 1, -16}, // 0x26 '&' + {137, 2, 6, 5, 1, -16}, // 0x27 ''' + {139, 5, 23, 8, 2, -17}, // 0x28 '(' + {154, 5, 23, 8, 1, -17}, // 0x29 ')' + {169, 7, 7, 9, 1, -17}, // 0x2A '*' + {176, 10, 11, 14, 2, -10}, // 0x2B '+' + {190, 2, 6, 7, 2, -1}, // 0x2C ',' + {192, 6, 2, 8, 1, -7}, // 0x2D '-' + {194, 2, 2, 6, 2, -1}, // 0x2E '.' + {195, 7, 18, 7, 0, -17}, // 0x2F '/' + {211, 11, 17, 13, 1, -16}, // 0x30 '0' + {235, 5, 17, 13, 3, -16}, // 0x31 '1' + {246, 11, 17, 13, 1, -16}, // 0x32 '2' + {270, 11, 17, 13, 1, -16}, // 0x33 '3' + {294, 11, 17, 13, 1, -16}, // 0x34 '4' + {318, 11, 17, 13, 1, -16}, // 0x35 '5' + {342, 11, 17, 13, 1, -16}, // 0x36 '6' + {366, 11, 17, 13, 1, -16}, // 0x37 '7' + {390, 11, 17, 13, 1, -16}, // 0x38 '8' + {414, 11, 17, 13, 1, -16}, // 0x39 '9' + {438, 2, 13, 6, 2, -12}, // 0x3A ':' + {442, 2, 16, 6, 2, -11}, // 0x3B ';' + {446, 12, 12, 14, 1, -11}, // 0x3C '<' + {464, 12, 6, 14, 1, -8}, // 0x3D '=' + {473, 12, 12, 14, 1, -11}, // 0x3E '>' + {491, 10, 18, 13, 2, -17}, // 0x3F '?' + {514, 22, 21, 24, 1, -17}, // 0x40 '@' + {572, 16, 18, 16, 0, -17}, // 0x41 'A' + {608, 13, 18, 16, 2, -17}, // 0x42 'B' + {638, 15, 18, 17, 1, -17}, // 0x43 'C' + {672, 14, 18, 17, 2, -17}, // 0x44 'D' + {704, 12, 18, 15, 2, -17}, // 0x45 'E' + {731, 11, 18, 14, 2, -17}, // 0x46 'F' + {756, 16, 18, 18, 1, -17}, // 0x47 'G' + {792, 13, 18, 17, 2, -17}, // 0x48 'H' + {822, 2, 18, 7, 2, -17}, // 0x49 'I' + {827, 9, 18, 13, 1, -17}, // 0x4A 'J' + {848, 14, 18, 16, 2, -17}, // 0x4B 'K' + {880, 10, 18, 14, 2, -17}, // 0x4C 'L' + {903, 16, 18, 20, 2, -17}, // 0x4D 'M' + {939, 13, 18, 18, 2, -17}, // 0x4E 'N' + {969, 17, 18, 19, 1, -17}, // 0x4F 'O' + {1008, 12, 18, 16, 2, -17}, // 0x50 'P' + {1035, 17, 19, 19, 1, -17}, // 0x51 'Q' + {1076, 14, 18, 17, 2, -17}, // 0x52 'R' + {1108, 14, 18, 16, 1, -17}, // 0x53 'S' + {1140, 12, 18, 15, 1, -17}, // 0x54 'T' + {1167, 13, 18, 17, 2, -17}, // 0x55 'U' + {1197, 15, 18, 15, 0, -17}, // 0x56 'V' + {1231, 22, 18, 22, 0, -17}, // 0x57 'W' + {1281, 15, 18, 16, 0, -17}, // 0x58 'X' + {1315, 16, 18, 16, 0, -17}, // 0x59 'Y' + {1351, 13, 18, 15, 1, -17}, // 0x5A 'Z' + {1381, 4, 23, 7, 2, -17}, // 0x5B '[' + {1393, 7, 18, 7, 0, -17}, // 0x5C '\' + {1409, 4, 23, 7, 1, -17}, // 0x5D ']' + {1421, 9, 9, 11, 1, -16}, // 0x5E '^' + {1432, 15, 1, 13, -1, 4}, // 0x5F '_' + {1434, 5, 4, 6, 1, -17}, // 0x60 '`' + {1437, 12, 13, 13, 1, -12}, // 0x61 'a' + {1457, 12, 18, 13, 1, -17}, // 0x62 'b' + {1484, 10, 13, 12, 1, -12}, // 0x63 'c' + {1501, 11, 18, 13, 1, -17}, // 0x64 'd' + {1526, 11, 13, 13, 1, -12}, // 0x65 'e' + {1544, 5, 18, 7, 1, -17}, // 0x66 'f' + {1556, 11, 18, 13, 1, -12}, // 0x67 'g' + {1581, 10, 18, 13, 1, -17}, // 0x68 'h' + {1604, 2, 18, 5, 2, -17}, // 0x69 'i' + {1609, 4, 23, 6, 0, -17}, // 0x6A 'j' + {1621, 11, 18, 12, 1, -17}, // 0x6B 'k' + {1646, 2, 18, 5, 1, -17}, // 0x6C 'l' + {1651, 17, 13, 19, 1, -12}, // 0x6D 'm' + {1679, 10, 13, 13, 1, -12}, // 0x6E 'n' + {1696, 11, 13, 13, 1, -12}, // 0x6F 'o' + {1714, 12, 17, 13, 1, -12}, // 0x70 'p' + {1740, 11, 17, 13, 1, -12}, // 0x71 'q' + {1764, 6, 13, 8, 1, -12}, // 0x72 'r' + {1774, 10, 13, 12, 1, -12}, // 0x73 's' + {1791, 5, 16, 7, 1, -15}, // 0x74 't' + {1801, 10, 13, 13, 1, -12}, // 0x75 'u' + {1818, 12, 13, 12, 0, -12}, // 0x76 'v' + {1838, 17, 13, 17, 0, -12}, // 0x77 'w' + {1866, 11, 13, 11, 0, -12}, // 0x78 'x' + {1884, 11, 18, 11, 0, -12}, // 0x79 'y' + {1909, 10, 13, 12, 1, -12}, // 0x7A 'z' + {1926, 5, 23, 8, 1, -17}, // 0x7B '{' + {1941, 2, 23, 6, 2, -17}, // 0x7C '|' + {1947, 5, 23, 8, 2, -17}, // 0x7D '}' + {1962, 10, 5, 12, 1, -10}}; // 0x7E '~' + +const GFXfont FreeSans12pt7b PROGMEM = {(uint8_t *)FreeSans12pt7bBitmaps, + (GFXglyph *)FreeSans12pt7bGlyphs, 0x20, + 0x7E, 29}; + +// Approx. 2641 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSans18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSans18pt7b.h new file mode 100644 index 0000000..82ac479 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSans18pt7b.h @@ -0,0 +1,451 @@ +const uint8_t FreeSans18pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE9, 0x20, 0x3F, 0xFC, 0xE3, 0xF1, + 0xF8, 0xFC, 0x7E, 0x3F, 0x1F, 0x8E, 0x82, 0x41, 0x00, 0x01, 0xC3, 0x80, + 0x38, 0x70, 0x06, 0x0E, 0x00, 0xC1, 0x80, 0x38, 0x70, 0x07, 0x0E, 0x0F, + 0xFF, 0xF9, 0xFF, 0xFF, 0x3F, 0xFF, 0xE0, 0xE1, 0xC0, 0x1C, 0x38, 0x03, + 0x87, 0x00, 0x70, 0xE0, 0x0C, 0x18, 0x3F, 0xFF, 0xF7, 0xFF, 0xFE, 0xFF, + 0xFF, 0xC1, 0xC3, 0x80, 0x30, 0x60, 0x06, 0x0C, 0x01, 0xC3, 0x80, 0x38, + 0x70, 0x07, 0x0E, 0x00, 0xC1, 0x80, 0x03, 0x00, 0x0F, 0xC0, 0x3F, 0xF0, + 0x3F, 0xF8, 0x7B, 0x3C, 0xF3, 0x1C, 0xE3, 0x0E, 0xE3, 0x0E, 0xE3, 0x0E, + 0xE3, 0x00, 0xE3, 0x00, 0xF3, 0x00, 0x7B, 0x00, 0x7F, 0x80, 0x1F, 0xF0, + 0x07, 0xFC, 0x03, 0x7E, 0x03, 0x0F, 0x03, 0x07, 0xE3, 0x07, 0xE3, 0x07, + 0xE3, 0x07, 0xE3, 0x0F, 0x73, 0x3E, 0x7F, 0xFC, 0x3F, 0xF8, 0x0F, 0xE0, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x78, 0x00, + 0xE0, 0x0F, 0xF0, 0x06, 0x00, 0xFF, 0xC0, 0x70, 0x07, 0x0E, 0x07, 0x00, + 0x70, 0x38, 0x38, 0x03, 0x00, 0xC3, 0x80, 0x18, 0x06, 0x1C, 0x00, 0xE0, + 0x71, 0xC0, 0x03, 0x87, 0x8C, 0x00, 0x1F, 0xF8, 0xE0, 0x00, 0x7F, 0x86, + 0x00, 0x01, 0xF8, 0x70, 0x00, 0x00, 0x03, 0x03, 0xC0, 0x00, 0x38, 0x7F, + 0x80, 0x01, 0x87, 0xFE, 0x00, 0x1C, 0x38, 0x70, 0x00, 0xC3, 0x81, 0xC0, + 0x0E, 0x18, 0x06, 0x00, 0xE0, 0xC0, 0x30, 0x07, 0x07, 0x03, 0x80, 0x70, + 0x1C, 0x38, 0x03, 0x80, 0xFF, 0xC0, 0x38, 0x03, 0xFC, 0x01, 0x80, 0x07, + 0x80, 0x01, 0xF0, 0x00, 0x7F, 0x80, 0x0F, 0xFC, 0x01, 0xE1, 0xE0, 0x1C, + 0x0E, 0x01, 0xC0, 0xE0, 0x1C, 0x0E, 0x01, 0xE1, 0xE0, 0x0E, 0x3C, 0x00, + 0x77, 0x80, 0x07, 0xF0, 0x00, 0x7C, 0x00, 0x0F, 0xE0, 0x03, 0xCF, 0x1C, + 0x78, 0x79, 0xC7, 0x03, 0xDC, 0xE0, 0x1F, 0x8E, 0x00, 0xF8, 0xE0, 0x0F, + 0x0E, 0x00, 0x70, 0xF0, 0x0F, 0x87, 0xC3, 0xFC, 0x7F, 0xFD, 0xC3, 0xFF, + 0x0E, 0x0F, 0xC0, 0xF0, 0xFF, 0xFF, 0xFA, 0x40, 0x06, 0x06, 0x0C, 0x0C, + 0x18, 0x18, 0x38, 0x30, 0x70, 0x70, 0x70, 0x60, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x60, 0x70, 0x70, 0x70, 0x30, 0x38, 0x18, + 0x18, 0x0C, 0x0C, 0x06, 0x03, 0xC0, 0x60, 0x30, 0x30, 0x38, 0x18, 0x1C, + 0x0C, 0x0E, 0x0E, 0x0E, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x06, 0x0E, 0x0E, 0x0E, 0x0C, 0x1C, 0x18, 0x38, 0x30, 0x30, + 0x60, 0xC0, 0x0C, 0x03, 0x00, 0xC3, 0xB7, 0xFF, 0xC7, 0x81, 0xE0, 0xEC, + 0x73, 0x88, 0x40, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, + 0xF6, 0xDA, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xC0, 0x30, 0x18, + 0x06, 0x01, 0x80, 0xC0, 0x30, 0x0C, 0x06, 0x01, 0x80, 0x60, 0x30, 0x0C, + 0x03, 0x00, 0xC0, 0x60, 0x18, 0x06, 0x03, 0x00, 0xC0, 0x30, 0x18, 0x06, + 0x01, 0x80, 0xC0, 0x30, 0x00, 0x07, 0xE0, 0x0F, 0xF8, 0x1F, 0xFC, 0x3C, + 0x3C, 0x78, 0x1E, 0x70, 0x0E, 0x70, 0x0E, 0xE0, 0x07, 0xE0, 0x07, 0xE0, + 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, + 0x07, 0xE0, 0x07, 0xE0, 0x0F, 0x70, 0x0E, 0x70, 0x0E, 0x78, 0x1E, 0x3C, + 0x3C, 0x1F, 0xF8, 0x1F, 0xF0, 0x07, 0xE0, 0x03, 0x03, 0x07, 0x0F, 0x3F, + 0xFF, 0xFF, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xE0, 0x1F, 0xF8, + 0x3F, 0xFC, 0x7C, 0x3E, 0x70, 0x0F, 0xF0, 0x0F, 0xE0, 0x07, 0xE0, 0x07, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0xF8, + 0x03, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x3C, 0x00, 0x38, 0x00, 0x70, 0x00, + 0x60, 0x00, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF0, + 0x07, 0xFE, 0x07, 0xFF, 0x87, 0x83, 0xC3, 0x80, 0xF3, 0x80, 0x39, 0xC0, + 0x1C, 0xE0, 0x0E, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x7F, 0x00, 0x3F, 0x00, + 0x1F, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, 0xF0, 0x01, + 0xF8, 0x00, 0xFE, 0x00, 0x77, 0x00, 0x73, 0xE0, 0xF8, 0xFF, 0xF8, 0x3F, + 0xF8, 0x07, 0xF0, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x78, 0x00, 0xF8, + 0x00, 0xF8, 0x01, 0xF8, 0x03, 0xB8, 0x03, 0x38, 0x07, 0x38, 0x0E, 0x38, + 0x1C, 0x38, 0x18, 0x38, 0x38, 0x38, 0x70, 0x38, 0x60, 0x38, 0xE0, 0x38, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, + 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x1F, 0xFF, 0x0F, 0xFF, 0x8F, 0xFF, + 0xC7, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x39, + 0xF0, 0x3F, 0xFE, 0x1F, 0xFF, 0x8F, 0x83, 0xE7, 0x00, 0xF0, 0x00, 0x3C, + 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xFC, 0x00, + 0xEF, 0x00, 0x73, 0xC0, 0xF0, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xE0, 0x00, + 0x03, 0xE0, 0x0F, 0xF8, 0x1F, 0xFC, 0x3C, 0x1E, 0x38, 0x0E, 0x70, 0x0E, + 0x70, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE3, 0xE0, 0xEF, 0xF8, 0xFF, 0xFC, + 0xFC, 0x3E, 0xF0, 0x0E, 0xF0, 0x0F, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, + 0x60, 0x07, 0x70, 0x0F, 0x70, 0x0E, 0x3C, 0x3E, 0x3F, 0xFC, 0x1F, 0xF8, + 0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x06, 0x00, 0x0E, + 0x00, 0x1C, 0x00, 0x18, 0x00, 0x38, 0x00, 0x70, 0x00, 0x60, 0x00, 0xE0, + 0x00, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x07, 0x00, + 0x07, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0C, 0x00, + 0x1C, 0x00, 0x1C, 0x00, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x87, 0x83, + 0xC7, 0x80, 0xF3, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x78, 0x0F, 0x1E, + 0x0F, 0x07, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xE0, 0xF9, 0xC0, 0x1D, + 0xC0, 0x0F, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0xF7, 0x00, + 0x73, 0xE0, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x07, 0xE0, + 0x1F, 0xF8, 0x3F, 0xFC, 0x7C, 0x3C, 0x70, 0x0E, 0xF0, 0x0E, 0xE0, 0x06, + 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x0F, 0x70, 0x0F, 0x78, 0x3F, + 0x3F, 0xFF, 0x1F, 0xF7, 0x07, 0xC7, 0x00, 0x07, 0x00, 0x06, 0x00, 0x0E, + 0x70, 0x0E, 0x70, 0x1C, 0x78, 0x3C, 0x3F, 0xF8, 0x1F, 0xF0, 0x07, 0xC0, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, 0xFF, 0xF0, 0x00, 0x00, + 0x00, 0x07, 0xFF, 0xB6, 0xD6, 0x00, 0x00, 0x80, 0x03, 0xC0, 0x07, 0xE0, + 0x0F, 0xC0, 0x3F, 0x80, 0x7E, 0x00, 0xFC, 0x01, 0xF0, 0x00, 0xE0, 0x00, + 0x7C, 0x00, 0x1F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x80, 0x07, 0xF0, 0x00, + 0x7E, 0x00, 0x0F, 0x00, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x80, 0x80, 0x00, 0x70, 0x00, 0x3E, 0x00, 0x0F, 0xE0, 0x00, 0xFC, + 0x00, 0x1F, 0xC0, 0x03, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0x80, 0x0F, 0xC0, + 0x1F, 0x80, 0x7F, 0x00, 0xFC, 0x01, 0xF8, 0x03, 0xF0, 0x01, 0xC0, 0x00, + 0x80, 0x00, 0x00, 0x0F, 0xC0, 0x7F, 0xE1, 0xFF, 0xE3, 0xC3, 0xEF, 0x01, + 0xFC, 0x01, 0xF8, 0x03, 0xF0, 0x07, 0x00, 0x0E, 0x00, 0x38, 0x00, 0xF0, + 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x0E, + 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x03, 0x80, + 0x07, 0x00, 0x0E, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xC0, 0x01, 0xF8, 0x0F, 0xE0, 0x03, 0xE0, 0x01, 0xF0, + 0x07, 0x80, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x3C, 0x1E, 0x00, 0x00, 0x1E, + 0x3C, 0x03, 0xE0, 0x1E, 0x38, 0x0F, 0xF3, 0x8E, 0x78, 0x1E, 0x3F, 0x0F, + 0x70, 0x38, 0x1F, 0x07, 0x70, 0x78, 0x0F, 0x07, 0xE0, 0x70, 0x0E, 0x07, + 0xE0, 0x70, 0x0E, 0x07, 0xE0, 0xE0, 0x0E, 0x07, 0xE0, 0xE0, 0x1C, 0x07, + 0xE0, 0xE0, 0x1C, 0x0E, 0xE0, 0xE0, 0x1C, 0x0E, 0xE0, 0xE0, 0x38, 0x1C, + 0xF0, 0x70, 0x78, 0x3C, 0x70, 0x78, 0xFC, 0x78, 0x78, 0x3F, 0xDF, 0xF0, + 0x38, 0x1F, 0x0F, 0xC0, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x00, 0x07, 0xF0, 0x0E, 0x00, 0x01, 0xFF, 0xFE, 0x00, + 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x03, + 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xDC, 0x00, 0x07, 0x78, + 0x00, 0x3C, 0xE0, 0x00, 0xE3, 0x80, 0x03, 0x8F, 0x00, 0x1E, 0x1C, 0x00, + 0x70, 0x70, 0x01, 0xC1, 0xE0, 0x0E, 0x03, 0x80, 0x38, 0x0E, 0x00, 0xE0, + 0x3C, 0x07, 0xFF, 0xF0, 0x1F, 0xFF, 0xE0, 0xFF, 0xFF, 0x83, 0xC0, 0x0E, + 0x0E, 0x00, 0x3C, 0x78, 0x00, 0xF1, 0xE0, 0x01, 0xC7, 0x00, 0x07, 0xBC, + 0x00, 0x1E, 0xF0, 0x00, 0x3B, 0x80, 0x00, 0xF0, 0xFF, 0xFC, 0x1F, 0xFF, + 0xE3, 0xFF, 0xFE, 0x70, 0x03, 0xCE, 0x00, 0x3D, 0xC0, 0x03, 0xB8, 0x00, + 0x77, 0x00, 0x0E, 0xE0, 0x01, 0xDC, 0x00, 0x73, 0x80, 0x1E, 0x7F, 0xFF, + 0x8F, 0xFF, 0xF1, 0xFF, 0xFF, 0x38, 0x00, 0xF7, 0x00, 0x0E, 0xE0, 0x00, + 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x03, + 0xF8, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0xFF, 0x1F, 0xFF, 0x80, 0x00, 0xFF, + 0x00, 0x0F, 0xFF, 0x00, 0xFF, 0xFE, 0x07, 0xE0, 0x7C, 0x3E, 0x00, 0x78, + 0xF0, 0x00, 0xE7, 0x80, 0x03, 0xDC, 0x00, 0x07, 0x70, 0x00, 0x03, 0x80, + 0x00, 0x0E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x00, + 0x0E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x1D, 0xC0, 0x00, 0x77, + 0x00, 0x03, 0xDE, 0x00, 0x0E, 0x3C, 0x00, 0x78, 0xF8, 0x03, 0xC1, 0xF8, + 0x1F, 0x03, 0xFF, 0xF8, 0x03, 0xFF, 0xC0, 0x03, 0xF8, 0x00, 0xFF, 0xF8, + 0x0F, 0xFF, 0xE0, 0xFF, 0xFF, 0x0E, 0x00, 0xF8, 0xE0, 0x03, 0xCE, 0x00, + 0x1C, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xFE, 0x00, 0x07, 0xE0, + 0x00, 0x7E, 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x07, 0xE0, 0x00, 0x7E, + 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x0F, 0xE0, 0x00, 0xEE, 0x00, 0x0E, + 0xE0, 0x01, 0xEE, 0x00, 0x3C, 0xE0, 0x0F, 0x8F, 0xFF, 0xF0, 0xFF, 0xFE, + 0x0F, 0xFF, 0x80, 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0xFF, 0xFB, 0x80, 0x00, + 0xE0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xE0, 0x00, 0x38, + 0x00, 0x0E, 0x00, 0x03, 0xFF, 0xFE, 0xFF, 0xFF, 0xBF, 0xFF, 0xEE, 0x00, + 0x03, 0x80, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, + 0xE0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x0E, 0x00, + 0x07, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, + 0x00, 0x1F, 0xFF, 0xCF, 0xFF, 0xE7, 0xFF, 0xF3, 0x80, 0x01, 0xC0, 0x00, + 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, + 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x00, 0x00, 0x7F, + 0x80, 0x03, 0xFF, 0xE0, 0x07, 0xFF, 0xF8, 0x0F, 0x80, 0xFC, 0x1E, 0x00, + 0x3E, 0x3C, 0x00, 0x0E, 0x78, 0x00, 0x0F, 0x70, 0x00, 0x07, 0x70, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x03, + 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x00, 0x07, 0xF0, 0x00, + 0x07, 0x70, 0x00, 0x07, 0x70, 0x00, 0x0F, 0x78, 0x00, 0x0F, 0x3C, 0x00, + 0x1F, 0x1E, 0x00, 0x3F, 0x0F, 0xC0, 0xF7, 0x07, 0xFF, 0xE7, 0x03, 0xFF, + 0xC3, 0x00, 0xFF, 0x03, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, + 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, + 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, + 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, + 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, + 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, + 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1F, 0x80, 0x7E, + 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0xF7, 0x87, 0x9F, 0xFE, 0x3F, 0xF0, + 0x3F, 0x00, 0xE0, 0x01, 0xEE, 0x00, 0x3C, 0xE0, 0x07, 0x8E, 0x00, 0xF0, + 0xE0, 0x1E, 0x0E, 0x03, 0xE0, 0xE0, 0x7C, 0x0E, 0x0F, 0x80, 0xE1, 0xF0, + 0x0E, 0x1E, 0x00, 0xE3, 0xC0, 0x0E, 0x7C, 0x00, 0xEF, 0xE0, 0x0F, 0xCE, + 0x00, 0xF8, 0xF0, 0x0F, 0x07, 0x80, 0xE0, 0x3C, 0x0E, 0x03, 0xC0, 0xE0, + 0x1E, 0x0E, 0x00, 0xF0, 0xE0, 0x0F, 0x0E, 0x00, 0x78, 0xE0, 0x03, 0xCE, + 0x00, 0x3C, 0xE0, 0x01, 0xEE, 0x00, 0x0F, 0xE0, 0x01, 0xC0, 0x03, 0x80, + 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x01, + 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, + 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, + 0x38, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF8, 0x00, 0x1F, 0xF8, + 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xFC, 0x00, 0x3F, 0xFC, 0x00, 0x3F, 0xFC, + 0x00, 0x3F, 0xEE, 0x00, 0x77, 0xEE, 0x00, 0x77, 0xEE, 0x00, 0x77, 0xE7, + 0x00, 0xE7, 0xE7, 0x00, 0xE7, 0xE7, 0x00, 0xE7, 0xE3, 0x81, 0xC7, 0xE3, + 0x81, 0xC7, 0xE3, 0x81, 0xC7, 0xE1, 0xC3, 0x87, 0xE1, 0xC3, 0x87, 0xE1, + 0xC3, 0x87, 0xE0, 0xE7, 0x07, 0xE0, 0xE7, 0x07, 0xE0, 0xE7, 0x07, 0xE0, + 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x3C, 0x07, 0xE0, + 0x3C, 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x07, 0xF8, 0x00, 0x7F, 0xC0, 0x07, + 0xFC, 0x00, 0x7F, 0xE0, 0x07, 0xEF, 0x00, 0x7E, 0x70, 0x07, 0xE7, 0x80, + 0x7E, 0x3C, 0x07, 0xE1, 0xC0, 0x7E, 0x1E, 0x07, 0xE0, 0xE0, 0x7E, 0x0F, + 0x07, 0xE0, 0x78, 0x7E, 0x03, 0x87, 0xE0, 0x3C, 0x7E, 0x01, 0xE7, 0xE0, + 0x0E, 0x7E, 0x00, 0xF7, 0xE0, 0x07, 0xFE, 0x00, 0x3F, 0xE0, 0x03, 0xFE, + 0x00, 0x1F, 0xE0, 0x01, 0xFE, 0x00, 0x0F, 0x00, 0x7F, 0x00, 0x01, 0xFF, + 0xF0, 0x01, 0xFF, 0xFC, 0x01, 0xF0, 0x1F, 0x01, 0xE0, 0x03, 0xC1, 0xE0, + 0x00, 0xF1, 0xE0, 0x00, 0x3C, 0xE0, 0x00, 0x0E, 0x70, 0x00, 0x07, 0x70, + 0x00, 0x03, 0xF8, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x03, + 0xB8, 0x00, 0x03, 0x9C, 0x00, 0x01, 0xCF, 0x00, 0x01, 0xE3, 0xC0, 0x01, + 0xE0, 0xF0, 0x01, 0xE0, 0x3E, 0x03, 0xE0, 0x0F, 0xFF, 0xE0, 0x03, 0xFF, + 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFF, 0xFC, 0x3F, 0xFF, 0x8F, 0xFF, 0xF3, + 0x80, 0x3E, 0xE0, 0x03, 0xF8, 0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xE0, + 0x01, 0xF8, 0x00, 0x7E, 0x00, 0x3F, 0x80, 0x1E, 0xFF, 0xFF, 0x3F, 0xFF, + 0x8F, 0xFF, 0xC3, 0x80, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, + 0x80, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xE0, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x01, 0xFF, 0xF0, 0x01, 0xFF, + 0xFC, 0x01, 0xF0, 0x1F, 0x01, 0xE0, 0x03, 0xC1, 0xE0, 0x00, 0xF1, 0xE0, + 0x00, 0x3C, 0xE0, 0x00, 0x0E, 0x70, 0x00, 0x07, 0x70, 0x00, 0x01, 0xF8, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x07, 0xB8, 0x00, 0x03, + 0x9C, 0x00, 0x01, 0xCF, 0x00, 0x39, 0xE3, 0xC0, 0x1F, 0xE0, 0xF0, 0x07, + 0xE0, 0x3E, 0x03, 0xF0, 0x0F, 0xFF, 0xFC, 0x03, 0xFF, 0xEE, 0x00, 0x3F, + 0x83, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x20, 0xFF, 0xFE, 0x0F, 0xFF, + 0xF8, 0xFF, 0xFF, 0xCE, 0x00, 0x3C, 0xE0, 0x01, 0xEE, 0x00, 0x0E, 0xE0, + 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x01, 0xCE, + 0x00, 0x3C, 0xFF, 0xFF, 0x8F, 0xFF, 0xF0, 0xFF, 0xFF, 0x8E, 0x00, 0x3C, + 0xE0, 0x01, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, + 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xFE, 0x00, + 0x0F, 0x03, 0xFC, 0x00, 0xFF, 0xF0, 0x1F, 0xFF, 0x83, 0xE0, 0x7C, 0x38, + 0x01, 0xE7, 0x00, 0x0E, 0x70, 0x00, 0xE7, 0x00, 0x00, 0x70, 0x00, 0x07, + 0x80, 0x00, 0x3E, 0x00, 0x01, 0xFE, 0x00, 0x0F, 0xFE, 0x00, 0x3F, 0xF8, + 0x00, 0x3F, 0xE0, 0x00, 0x3E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0xE0, 0x00, + 0x7E, 0x00, 0x07, 0xF0, 0x00, 0x77, 0x80, 0x0E, 0x7C, 0x03, 0xE3, 0xFF, + 0xFC, 0x1F, 0xFF, 0x80, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x80, 0x70, 0x00, 0x0E, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x07, + 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, 0x0E, + 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x1C, + 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, 0x0E, 0x00, 0x01, 0xC0, 0x00, 0x38, + 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, 0xE0, 0x00, 0xFC, 0x00, + 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, + 0x3F, 0x00, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, + 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, + 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7F, 0x00, 0x1E, 0xF0, 0x07, + 0x9F, 0x01, 0xF1, 0xFF, 0xFC, 0x1F, 0xFE, 0x00, 0x7F, 0x00, 0xE0, 0x00, + 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x1C, 0xE0, 0x01, 0xE7, 0x80, 0x0F, 0x3C, + 0x00, 0x70, 0xE0, 0x07, 0x87, 0x80, 0x3C, 0x1C, 0x01, 0xC0, 0xE0, 0x0E, + 0x07, 0x80, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x38, 0x07, 0x83, 0x80, 0x1C, + 0x1C, 0x00, 0xE0, 0xE0, 0x07, 0x8E, 0x00, 0x1C, 0x70, 0x00, 0xE3, 0x80, + 0x07, 0xB8, 0x00, 0x1D, 0xC0, 0x00, 0xEE, 0x00, 0x07, 0xE0, 0x00, 0x1F, + 0x00, 0x00, 0xF8, 0x00, 0x03, 0x80, 0x00, 0x70, 0x03, 0xC0, 0x0F, 0x70, + 0x03, 0xC0, 0x0F, 0x78, 0x03, 0xE0, 0x0F, 0x78, 0x03, 0xE0, 0x0E, 0x38, + 0x07, 0xE0, 0x0E, 0x38, 0x07, 0xF0, 0x1E, 0x3C, 0x07, 0x70, 0x1E, 0x3C, + 0x07, 0x70, 0x1C, 0x1C, 0x0E, 0x70, 0x1C, 0x1C, 0x0E, 0x38, 0x3C, 0x1C, + 0x0E, 0x38, 0x3C, 0x1E, 0x1E, 0x38, 0x38, 0x0E, 0x1C, 0x38, 0x38, 0x0E, + 0x1C, 0x1C, 0x38, 0x0E, 0x1C, 0x1C, 0x78, 0x0F, 0x3C, 0x1C, 0x70, 0x07, + 0x38, 0x0E, 0x70, 0x07, 0x38, 0x0E, 0x70, 0x07, 0x38, 0x0E, 0x70, 0x07, + 0x70, 0x0E, 0xE0, 0x03, 0xF0, 0x07, 0xE0, 0x03, 0xF0, 0x07, 0xE0, 0x03, + 0xF0, 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xC0, 0x01, 0xE0, 0x03, 0xC0, 0x01, + 0xE0, 0x03, 0xC0, 0xF0, 0x00, 0x7B, 0xC0, 0x07, 0x8F, 0x00, 0x38, 0x78, + 0x03, 0xC1, 0xE0, 0x3C, 0x07, 0x81, 0xC0, 0x3C, 0x1E, 0x00, 0xF1, 0xE0, + 0x03, 0x8E, 0x00, 0x1E, 0xF0, 0x00, 0x7F, 0x00, 0x01, 0xF0, 0x00, 0x0F, + 0x80, 0x00, 0x7C, 0x00, 0x07, 0xF0, 0x00, 0x3B, 0x80, 0x03, 0xDE, 0x00, + 0x3C, 0x78, 0x01, 0xC1, 0xC0, 0x1E, 0x0F, 0x01, 0xE0, 0x3C, 0x0E, 0x00, + 0xE0, 0xF0, 0x07, 0x8F, 0x00, 0x1E, 0x70, 0x00, 0xF7, 0x80, 0x03, 0xC0, + 0xF0, 0x00, 0x3C, 0xF0, 0x00, 0x78, 0xF0, 0x01, 0xE1, 0xE0, 0x03, 0x81, + 0xE0, 0x0F, 0x01, 0xC0, 0x1C, 0x03, 0xC0, 0x78, 0x03, 0xC1, 0xE0, 0x07, + 0x83, 0x80, 0x07, 0x8F, 0x00, 0x07, 0x1C, 0x00, 0x0F, 0x78, 0x00, 0x0E, + 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, + 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x70, 0x00, 0x7F, 0xFF, 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x80, 0x00, + 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, + 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, + 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x7C, 0x00, + 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xF8, 0xE3, 0x8E, 0x38, 0xE3, + 0x8E, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x8E, 0x38, 0xE3, + 0x8E, 0x38, 0xE3, 0x8F, 0xFF, 0xFC, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x60, + 0x0C, 0x03, 0x00, 0xC0, 0x18, 0x06, 0x01, 0x80, 0x20, 0x0C, 0x03, 0x00, + 0x40, 0x18, 0x06, 0x01, 0x80, 0x30, 0x0C, 0x03, 0x00, 0x60, 0x18, 0x06, + 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, + 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, + 0x1C, 0x7F, 0xFF, 0xFC, 0x07, 0x00, 0x78, 0x03, 0xC0, 0x3F, 0x01, 0xD8, + 0x0C, 0xE0, 0xE3, 0x06, 0x1C, 0x70, 0xE3, 0x83, 0x18, 0x1D, 0xC0, 0x6C, + 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xF0, 0xF0, 0xE0, 0xE0, + 0xE0, 0x07, 0xF0, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, 0x03, 0xC7, 0x00, 0xE0, + 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0xFE, 0x0F, 0xFF, 0x1F, 0xF3, + 0x9F, 0x01, 0xCF, 0x00, 0xE7, 0x00, 0x73, 0x80, 0x79, 0xE0, 0xFC, 0x7F, + 0xEF, 0x9F, 0xE3, 0xC7, 0xE1, 0xE0, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, + 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE3, 0xE0, 0xEF, 0xF8, + 0xFF, 0xFC, 0xFC, 0x3E, 0xF8, 0x1E, 0xF0, 0x0E, 0xE0, 0x0F, 0xE0, 0x07, + 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0E, + 0xF8, 0x1E, 0xFC, 0x3C, 0xEF, 0xFC, 0xEF, 0xF8, 0xE3, 0xE0, 0x07, 0xF0, + 0x1F, 0xF8, 0x3F, 0xFC, 0x3C, 0x1E, 0x78, 0x0E, 0x70, 0x07, 0xE0, 0x00, + 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x07, + 0x70, 0x07, 0x78, 0x0E, 0x7C, 0x1E, 0x3F, 0xFC, 0x1F, 0xF8, 0x07, 0xE0, + 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, + 0x1C, 0x00, 0x0E, 0x0F, 0xC7, 0x1F, 0xFB, 0x9F, 0xFF, 0xDF, 0x07, 0xEF, + 0x01, 0xF7, 0x00, 0x7F, 0x80, 0x3F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x03, + 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0x77, 0x00, 0x7B, 0xC0, 0x7D, 0xF0, + 0x7E, 0x7F, 0xFB, 0x1F, 0xF9, 0x83, 0xF0, 0xC0, 0x07, 0xE0, 0x1F, 0xF8, + 0x3F, 0xFC, 0x7C, 0x1E, 0x70, 0x0E, 0x60, 0x06, 0xE0, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0x70, 0x07, + 0x78, 0x0E, 0x3C, 0x1E, 0x3F, 0xFC, 0x1F, 0xF8, 0x07, 0xE0, 0x0E, 0x3C, + 0xF9, 0xC3, 0x87, 0x0E, 0x7F, 0xFF, 0xFC, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, + 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0x07, 0xC7, 0x1F, + 0xF7, 0x3F, 0xFF, 0x3C, 0x3F, 0x78, 0x0F, 0x70, 0x0F, 0xE0, 0x07, 0xE0, + 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0x70, + 0x0F, 0x78, 0x0F, 0x7C, 0x3F, 0x3F, 0xF7, 0x1F, 0xE7, 0x07, 0xC7, 0x00, + 0x07, 0x00, 0x07, 0x00, 0x0E, 0x70, 0x0E, 0x78, 0x1E, 0x3F, 0xFC, 0x1F, + 0xF8, 0x07, 0xE0, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, + 0x1C, 0x00, 0x38, 0x00, 0x71, 0xF8, 0xE7, 0xFD, 0xDF, 0xFB, 0xF0, 0xFF, + 0xC0, 0xFF, 0x00, 0xFC, 0x01, 0xF8, 0x03, 0xF0, 0x07, 0xE0, 0x0F, 0xC0, + 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, 0xFC, 0x01, 0xF8, 0x03, 0xF0, 0x07, + 0xE0, 0x0F, 0xC0, 0x1C, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0x1C, 0x71, 0xC7, 0x00, 0x00, 0x07, 0x1C, 0x71, 0xC7, 0x1C, + 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, + 0x73, 0xFF, 0xFB, 0xC0, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, + 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x3C, 0xE0, 0x78, 0xE0, 0xF0, + 0xE1, 0xE0, 0xE3, 0xC0, 0xE7, 0x80, 0xEF, 0x00, 0xEF, 0x80, 0xFF, 0x80, + 0xFB, 0xC0, 0xF1, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xE0, 0x70, 0xE0, 0x78, + 0xE0, 0x3C, 0xE0, 0x1C, 0xE0, 0x1E, 0xE0, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xE3, 0xE0, 0xF8, 0xE7, 0xF1, 0xFE, + 0xEF, 0xFB, 0xFE, 0xF8, 0x7F, 0x0F, 0xF0, 0x3E, 0x07, 0xF0, 0x1C, 0x07, + 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, + 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, + 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, 0xE0, 0x1C, 0x07, + 0xE0, 0x1C, 0x07, 0xE3, 0xF1, 0xCF, 0xFB, 0xBF, 0xF7, 0xE1, 0xFF, 0x81, + 0xFE, 0x01, 0xF8, 0x03, 0xF0, 0x07, 0xE0, 0x0F, 0xC0, 0x1F, 0x80, 0x3F, + 0x00, 0x7E, 0x00, 0xFC, 0x01, 0xF8, 0x03, 0xF0, 0x07, 0xE0, 0x0F, 0xC0, + 0x1F, 0x80, 0x38, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x87, 0x83, 0xC7, + 0x80, 0xF3, 0x80, 0x3B, 0x80, 0x1F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x01, + 0xF8, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0x3B, 0x80, 0x39, 0xE0, 0x3C, 0x78, + 0x3C, 0x3F, 0xFE, 0x0F, 0xFE, 0x01, 0xFC, 0x00, 0xE3, 0xE0, 0xE7, 0xF8, + 0xEF, 0xFC, 0xFC, 0x3E, 0xF8, 0x1E, 0xF0, 0x0E, 0xE0, 0x0F, 0xE0, 0x07, + 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0E, + 0xF8, 0x1E, 0xFC, 0x3E, 0xFF, 0xFC, 0xEF, 0xF8, 0xE3, 0xE0, 0xE0, 0x00, + 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0x07, 0xE1, + 0x8F, 0xFC, 0xCF, 0xFF, 0x67, 0x83, 0xF7, 0x80, 0xFB, 0x80, 0x3F, 0xC0, + 0x1F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0x7E, + 0x00, 0x3B, 0x80, 0x3D, 0xE0, 0x3E, 0xF8, 0x3F, 0x3F, 0xFF, 0x8F, 0xFD, + 0xC1, 0xF8, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, + 0x07, 0x00, 0x03, 0x80, 0xE3, 0xF7, 0xFB, 0xFF, 0x8F, 0x07, 0x83, 0x81, + 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, + 0x38, 0x00, 0x0F, 0xC0, 0xFF, 0x87, 0xFF, 0x3C, 0x1E, 0xE0, 0x3B, 0x80, + 0x0E, 0x00, 0x3C, 0x00, 0x7F, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0x7F, 0x00, + 0x3F, 0x80, 0x7E, 0x01, 0xFC, 0x1F, 0x7F, 0xF8, 0xFF, 0xC1, 0xFC, 0x00, + 0x38, 0x70, 0xE1, 0xCF, 0xFF, 0xFF, 0x9C, 0x38, 0x70, 0xE1, 0xC3, 0x87, + 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0xE7, 0xC7, 0x80, 0xE0, 0x0F, 0xC0, + 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, 0xFC, 0x01, 0xF8, 0x03, 0xF0, 0x07, + 0xE0, 0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, 0xFC, 0x03, 0xFC, + 0x0F, 0xFC, 0x3F, 0x7F, 0xEE, 0xFF, 0x9C, 0x7E, 0x38, 0x70, 0x03, 0xB8, + 0x03, 0x9C, 0x01, 0xC7, 0x00, 0xE3, 0x80, 0xE1, 0xC0, 0x70, 0x70, 0x38, + 0x38, 0x38, 0x1C, 0x1C, 0x07, 0x0E, 0x03, 0x8E, 0x01, 0xC7, 0x00, 0x77, + 0x00, 0x3B, 0x80, 0x1D, 0xC0, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, + 0x70, 0x00, 0xF0, 0x1C, 0x03, 0xB8, 0x1F, 0x03, 0xDC, 0x0F, 0x81, 0xCE, + 0x07, 0xC0, 0xE7, 0x83, 0xE0, 0x71, 0xC3, 0xB8, 0x70, 0xE1, 0xDC, 0x38, + 0x70, 0xEE, 0x1C, 0x1C, 0x63, 0x0E, 0x0E, 0x71, 0xCE, 0x07, 0x38, 0xE7, + 0x03, 0x9C, 0x73, 0x80, 0xEC, 0x19, 0x80, 0x7E, 0x0F, 0xC0, 0x3F, 0x07, + 0xE0, 0x0F, 0x83, 0xF0, 0x07, 0x80, 0xF0, 0x03, 0xC0, 0x78, 0x01, 0xE0, + 0x3C, 0x00, 0x70, 0x07, 0x38, 0x0E, 0x3C, 0x1C, 0x1C, 0x1C, 0x0E, 0x38, + 0x0F, 0x70, 0x07, 0x70, 0x03, 0xE0, 0x03, 0xC0, 0x01, 0xC0, 0x03, 0xE0, + 0x07, 0xE0, 0x07, 0x70, 0x0E, 0x78, 0x1E, 0x38, 0x1C, 0x1C, 0x38, 0x1E, + 0x78, 0x0E, 0x70, 0x07, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xC7, 0x01, + 0xC3, 0x80, 0xE1, 0xC0, 0x70, 0x70, 0x70, 0x38, 0x38, 0x1C, 0x3C, 0x07, + 0x1C, 0x03, 0x8E, 0x01, 0xCE, 0x00, 0x77, 0x00, 0x3B, 0x80, 0x1F, 0x80, + 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x38, + 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x0F, + 0x00, 0x00, 0x7F, 0xFC, 0xFF, 0xF9, 0xFF, 0xF0, 0x00, 0xE0, 0x03, 0x80, + 0x0E, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x1C, 0x00, 0x70, + 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF8, 0x07, 0x0F, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x38, 0xF8, 0xE0, 0xF8, 0x38, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0x0F, 0x07, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xE0, 0xF0, 0xF8, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x1C, 0x1F, + 0x07, 0x1F, 0x1C, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0xF8, 0xF0, 0xE0, 0x38, 0x00, 0xFC, 0x03, 0xFC, 0x1F, 0x3E, + 0x3C, 0x1F, 0xE0, 0x1F, 0x80, 0x1E, 0x00}; + +const GFXglyph FreeSans18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 9, 0, 1}, // 0x20 ' ' + {0, 3, 26, 12, 4, -25}, // 0x21 '!' + {10, 9, 9, 12, 1, -24}, // 0x22 '"' + {21, 19, 24, 19, 0, -23}, // 0x23 '#' + {78, 16, 30, 19, 2, -26}, // 0x24 '$' + {138, 29, 25, 31, 1, -24}, // 0x25 '%' + {229, 20, 25, 23, 2, -24}, // 0x26 '&' + {292, 3, 9, 7, 2, -24}, // 0x27 ''' + {296, 8, 33, 12, 3, -25}, // 0x28 '(' + {329, 8, 33, 12, 1, -25}, // 0x29 ')' + {362, 10, 10, 14, 2, -25}, // 0x2A '*' + {375, 16, 16, 20, 2, -15}, // 0x2B '+' + {407, 3, 9, 10, 3, -3}, // 0x2C ',' + {411, 8, 3, 12, 2, -10}, // 0x2D '-' + {414, 3, 4, 9, 3, -3}, // 0x2E '.' + {416, 10, 26, 10, 0, -25}, // 0x2F '/' + {449, 16, 25, 19, 2, -24}, // 0x30 '0' + {499, 8, 25, 19, 4, -24}, // 0x31 '1' + {524, 16, 25, 19, 2, -24}, // 0x32 '2' + {574, 17, 25, 19, 1, -24}, // 0x33 '3' + {628, 16, 25, 19, 1, -24}, // 0x34 '4' + {678, 17, 25, 19, 1, -24}, // 0x35 '5' + {732, 16, 25, 19, 2, -24}, // 0x36 '6' + {782, 16, 25, 19, 2, -24}, // 0x37 '7' + {832, 17, 25, 19, 1, -24}, // 0x38 '8' + {886, 16, 25, 19, 1, -24}, // 0x39 '9' + {936, 3, 19, 9, 3, -18}, // 0x3A ':' + {944, 3, 24, 9, 3, -18}, // 0x3B ';' + {953, 17, 17, 20, 2, -16}, // 0x3C '<' + {990, 17, 9, 20, 2, -12}, // 0x3D '=' + {1010, 17, 17, 20, 2, -16}, // 0x3E '>' + {1047, 15, 26, 19, 3, -25}, // 0x3F '?' + {1096, 32, 31, 36, 1, -25}, // 0x40 '@' + {1220, 22, 26, 23, 1, -25}, // 0x41 'A' + {1292, 19, 26, 23, 3, -25}, // 0x42 'B' + {1354, 22, 26, 25, 1, -25}, // 0x43 'C' + {1426, 20, 26, 24, 3, -25}, // 0x44 'D' + {1491, 18, 26, 22, 3, -25}, // 0x45 'E' + {1550, 17, 26, 21, 3, -25}, // 0x46 'F' + {1606, 24, 26, 27, 1, -25}, // 0x47 'G' + {1684, 19, 26, 25, 3, -25}, // 0x48 'H' + {1746, 3, 26, 10, 4, -25}, // 0x49 'I' + {1756, 14, 26, 18, 1, -25}, // 0x4A 'J' + {1802, 20, 26, 24, 3, -25}, // 0x4B 'K' + {1867, 15, 26, 20, 3, -25}, // 0x4C 'L' + {1916, 24, 26, 30, 3, -25}, // 0x4D 'M' + {1994, 20, 26, 26, 3, -25}, // 0x4E 'N' + {2059, 25, 26, 27, 1, -25}, // 0x4F 'O' + {2141, 18, 26, 23, 3, -25}, // 0x50 'P' + {2200, 25, 28, 27, 1, -25}, // 0x51 'Q' + {2288, 20, 26, 25, 3, -25}, // 0x52 'R' + {2353, 20, 26, 23, 1, -25}, // 0x53 'S' + {2418, 19, 26, 22, 1, -25}, // 0x54 'T' + {2480, 19, 26, 25, 3, -25}, // 0x55 'U' + {2542, 21, 26, 23, 1, -25}, // 0x56 'V' + {2611, 32, 26, 33, 0, -25}, // 0x57 'W' + {2715, 21, 26, 23, 1, -25}, // 0x58 'X' + {2784, 23, 26, 24, 0, -25}, // 0x59 'Y' + {2859, 19, 26, 22, 1, -25}, // 0x5A 'Z' + {2921, 6, 33, 10, 2, -25}, // 0x5B '[' + {2946, 10, 26, 10, 0, -25}, // 0x5C '\' + {2979, 6, 33, 10, 1, -25}, // 0x5D ']' + {3004, 13, 13, 16, 2, -24}, // 0x5E '^' + {3026, 21, 2, 19, -1, 5}, // 0x5F '_' + {3032, 7, 5, 9, 1, -25}, // 0x60 '`' + {3037, 17, 19, 19, 1, -18}, // 0x61 'a' + {3078, 16, 26, 20, 2, -25}, // 0x62 'b' + {3130, 16, 19, 18, 1, -18}, // 0x63 'c' + {3168, 17, 26, 20, 1, -25}, // 0x64 'd' + {3224, 16, 19, 19, 1, -18}, // 0x65 'e' + {3262, 7, 26, 10, 1, -25}, // 0x66 'f' + {3285, 16, 27, 19, 1, -18}, // 0x67 'g' + {3339, 15, 26, 19, 2, -25}, // 0x68 'h' + {3388, 3, 26, 8, 2, -25}, // 0x69 'i' + {3398, 6, 34, 9, 0, -25}, // 0x6A 'j' + {3424, 16, 26, 18, 2, -25}, // 0x6B 'k' + {3476, 3, 26, 7, 2, -25}, // 0x6C 'l' + {3486, 24, 19, 28, 2, -18}, // 0x6D 'm' + {3543, 15, 19, 19, 2, -18}, // 0x6E 'n' + {3579, 17, 19, 19, 1, -18}, // 0x6F 'o' + {3620, 16, 25, 20, 2, -18}, // 0x70 'p' + {3670, 17, 25, 20, 1, -18}, // 0x71 'q' + {3724, 9, 19, 12, 2, -18}, // 0x72 'r' + {3746, 14, 19, 17, 2, -18}, // 0x73 's' + {3780, 7, 23, 10, 1, -22}, // 0x74 't' + {3801, 15, 19, 19, 2, -18}, // 0x75 'u' + {3837, 17, 19, 17, 0, -18}, // 0x76 'v' + {3878, 25, 19, 25, 0, -18}, // 0x77 'w' + {3938, 16, 19, 17, 0, -18}, // 0x78 'x' + {3976, 17, 27, 17, 0, -18}, // 0x79 'y' + {4034, 15, 19, 17, 1, -18}, // 0x7A 'z' + {4070, 8, 33, 12, 1, -25}, // 0x7B '{' + {4103, 2, 33, 9, 3, -25}, // 0x7C '|' + {4112, 8, 33, 12, 3, -25}, // 0x7D '}' + {4145, 15, 7, 18, 1, -15}}; // 0x7E '~' + +const GFXfont FreeSans18pt7b PROGMEM = {(uint8_t *)FreeSans18pt7bBitmaps, + (GFXglyph *)FreeSans18pt7bGlyphs, 0x20, + 0x7E, 42}; + +// Approx. 4831 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSans24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSans24pt7b.h new file mode 100644 index 0000000..5939c31 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSans24pt7b.h @@ -0,0 +1,726 @@ +const uint8_t FreeSans24pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x76, 0x66, + 0x66, 0x00, 0x0F, 0xFF, 0xFF, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x1F, + 0xE3, 0xFC, 0x7F, 0x8F, 0xF1, 0xEC, 0x19, 0x83, 0x30, 0x60, 0x00, 0x70, + 0x3C, 0x00, 0x70, 0x3C, 0x00, 0xF0, 0x38, 0x00, 0xF0, 0x38, 0x00, 0xF0, + 0x78, 0x00, 0xE0, 0x78, 0x00, 0xE0, 0x78, 0x01, 0xE0, 0x70, 0x01, 0xE0, + 0x70, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x03, 0xC0, + 0xE0, 0x03, 0xC0, 0xE0, 0x03, 0xC0, 0xE0, 0x03, 0x81, 0xE0, 0x03, 0x81, + 0xE0, 0x03, 0x81, 0xE0, 0x07, 0x81, 0xC0, 0x07, 0x81, 0xC0, 0xFF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0x0F, 0x03, 0x80, 0x0F, 0x03, + 0x80, 0x0F, 0x07, 0x80, 0x0E, 0x07, 0x80, 0x0E, 0x07, 0x80, 0x1E, 0x07, + 0x00, 0x1E, 0x07, 0x00, 0x1E, 0x07, 0x00, 0x1C, 0x0F, 0x00, 0x1C, 0x0F, + 0x00, 0x00, 0x38, 0x00, 0x01, 0xFC, 0x00, 0x1F, 0xFE, 0x00, 0x7F, 0xFE, + 0x01, 0xFF, 0xFE, 0x07, 0xE7, 0x3E, 0x0F, 0x8E, 0x3C, 0x3E, 0x1C, 0x3C, + 0x78, 0x38, 0x38, 0xF0, 0x70, 0x71, 0xE0, 0xE0, 0xE3, 0xC1, 0xC0, 0x07, + 0x83, 0x80, 0x0F, 0x87, 0x00, 0x0F, 0x8E, 0x00, 0x1F, 0xDC, 0x00, 0x1F, + 0xF8, 0x00, 0x1F, 0xFF, 0x00, 0x0F, 0xFF, 0x80, 0x07, 0xFF, 0x80, 0x03, + 0xFF, 0x80, 0x07, 0x1F, 0x80, 0x0E, 0x1F, 0x00, 0x1C, 0x1F, 0x00, 0x38, + 0x1F, 0xC0, 0x70, 0x3F, 0x80, 0xE0, 0x7F, 0x81, 0xC0, 0xFF, 0x03, 0x81, + 0xEF, 0x07, 0x07, 0x9F, 0x0E, 0x0F, 0x3E, 0x1C, 0x3E, 0x3F, 0x39, 0xF8, + 0x3F, 0xFF, 0xE0, 0x3F, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x03, 0x80, 0x00, + 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x0F, 0xC0, 0x00, 0x78, 0x00, 0x3F, 0xE0, 0x00, + 0xE0, 0x01, 0xFF, 0xE0, 0x03, 0x80, 0x03, 0xFF, 0xE0, 0x07, 0x00, 0x0F, + 0x87, 0xC0, 0x1C, 0x00, 0x3C, 0x03, 0xC0, 0x38, 0x00, 0x70, 0x03, 0x80, + 0xE0, 0x00, 0xE0, 0x07, 0x03, 0xC0, 0x01, 0xC0, 0x0E, 0x07, 0x00, 0x03, + 0x80, 0x1C, 0x1E, 0x00, 0x07, 0x80, 0x78, 0x38, 0x00, 0x07, 0xC3, 0xE0, + 0xF0, 0x00, 0x07, 0xFF, 0xC1, 0xC0, 0x00, 0x0F, 0xFF, 0x07, 0x80, 0x00, + 0x0F, 0xFC, 0x0E, 0x00, 0x00, 0x07, 0xE0, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x3F, 0x00, 0x00, 0x03, 0x80, 0xFF, + 0x80, 0x00, 0x0E, 0x07, 0xFF, 0x80, 0x00, 0x3C, 0x0F, 0xFF, 0x80, 0x00, + 0x70, 0x3E, 0x1F, 0x00, 0x01, 0xE0, 0xF0, 0x0F, 0x00, 0x03, 0x81, 0xC0, + 0x0E, 0x00, 0x0F, 0x03, 0x80, 0x1C, 0x00, 0x1C, 0x07, 0x00, 0x38, 0x00, + 0x78, 0x0E, 0x00, 0x70, 0x00, 0xE0, 0x1E, 0x01, 0xE0, 0x03, 0x80, 0x1F, + 0x0F, 0x80, 0x07, 0x00, 0x1F, 0xFF, 0x00, 0x1C, 0x00, 0x3F, 0xFC, 0x00, + 0x38, 0x00, 0x1F, 0xF0, 0x00, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x7E, 0x00, + 0x00, 0x1F, 0xF0, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x7F, 0xFC, 0x00, 0x07, + 0xC3, 0xC0, 0x00, 0xF8, 0x1E, 0x00, 0x0F, 0x00, 0xE0, 0x00, 0xF0, 0x0E, + 0x00, 0x0F, 0x00, 0xE0, 0x00, 0xF0, 0x0E, 0x00, 0x07, 0x81, 0xE0, 0x00, + 0x7C, 0x3C, 0x00, 0x03, 0xEF, 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x1F, 0x80, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0xE0, 0x00, + 0x1F, 0x1E, 0x07, 0x83, 0xE0, 0xF0, 0x78, 0x7C, 0x0F, 0x8F, 0x87, 0x80, + 0x7C, 0xF0, 0xF0, 0x03, 0xFF, 0x0F, 0x00, 0x1F, 0xE0, 0xF0, 0x00, 0xFE, + 0x0F, 0x00, 0x0F, 0xC0, 0xF0, 0x00, 0x7E, 0x0F, 0x80, 0x0F, 0xF0, 0x7C, + 0x01, 0xFF, 0x07, 0xF0, 0x7D, 0xF8, 0x3F, 0xFF, 0x8F, 0xC1, 0xFF, 0xF0, + 0x7E, 0x0F, 0xFE, 0x03, 0xE0, 0x3F, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF6, 0x66, 0x01, 0xC0, 0x70, 0x38, 0x1C, 0x07, 0x03, 0xC0, 0xE0, 0x78, + 0x1C, 0x07, 0x03, 0xC0, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x70, 0x3C, + 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, + 0xC0, 0x70, 0x1E, 0x07, 0x81, 0xE0, 0x38, 0x0F, 0x03, 0xC0, 0x70, 0x1E, + 0x03, 0x80, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x18, 0x07, 0xE0, 0x38, 0x07, + 0x01, 0xC0, 0x38, 0x0F, 0x01, 0xC0, 0x78, 0x0E, 0x03, 0x80, 0xF0, 0x1C, + 0x07, 0x01, 0xE0, 0x78, 0x1E, 0x03, 0x80, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, + 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x07, 0x81, 0xE0, 0x78, + 0x1E, 0x07, 0x03, 0xC0, 0xF0, 0x38, 0x1E, 0x07, 0x01, 0xC0, 0xE0, 0x38, + 0x1C, 0x06, 0x03, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x63, + 0x1B, 0xFF, 0xFF, 0xFF, 0xC3, 0xF0, 0x07, 0x80, 0x3F, 0x01, 0xCE, 0x07, + 0x3C, 0x38, 0x70, 0x21, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, + 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, + 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0xFF, 0xFF, 0xF3, + 0x33, 0x36, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF0, + 0x00, 0x38, 0x01, 0xC0, 0x0C, 0x00, 0xE0, 0x07, 0x00, 0x30, 0x03, 0x80, + 0x1C, 0x00, 0xC0, 0x06, 0x00, 0x70, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x0E, + 0x00, 0x60, 0x03, 0x00, 0x38, 0x01, 0x80, 0x0C, 0x00, 0xE0, 0x07, 0x00, + 0x30, 0x03, 0x80, 0x1C, 0x00, 0xC0, 0x06, 0x00, 0x70, 0x03, 0x80, 0x18, + 0x01, 0xC0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, + 0x0F, 0xFC, 0x00, 0xFF, 0xFC, 0x07, 0xFF, 0xF8, 0x1F, 0x87, 0xE0, 0xF8, + 0x07, 0xC3, 0xC0, 0x0F, 0x1F, 0x00, 0x3E, 0x78, 0x00, 0x79, 0xE0, 0x01, + 0xE7, 0x80, 0x07, 0xBC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, + 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x00, + 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x00, 0x03, + 0xDE, 0x00, 0x1E, 0x78, 0x00, 0x79, 0xE0, 0x01, 0xE7, 0xC0, 0x0F, 0x8F, + 0x00, 0x3C, 0x3E, 0x01, 0xF0, 0x7C, 0x1F, 0x81, 0xFF, 0xFE, 0x03, 0xFF, + 0xF0, 0x03, 0xFF, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x60, 0x1C, 0x03, 0x80, + 0xF0, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3C, 0x07, 0x80, 0xF0, + 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, + 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, + 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x01, 0xFE, 0x00, 0x1F, 0xFE, 0x01, 0xFF, + 0xFE, 0x0F, 0xFF, 0xFC, 0x3F, 0x03, 0xF9, 0xF0, 0x03, 0xE7, 0x80, 0x07, + 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x00, 0x03, 0xC0, + 0x00, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, + 0x7C, 0x00, 0x07, 0xF0, 0x00, 0x7F, 0x80, 0x07, 0xF8, 0x00, 0x3F, 0xC0, + 0x03, 0xFC, 0x00, 0x1F, 0xC0, 0x00, 0xFC, 0x00, 0x07, 0xC0, 0x00, 0x3E, + 0x00, 0x00, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1C, 0x00, 0x00, 0x70, 0x00, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0xFE, 0x00, 0x0F, 0xFF, 0x80, 0x3F, 0xFF, 0x80, 0xFF, 0xFF, 0x83, + 0xF0, 0x1F, 0x87, 0xC0, 0x1F, 0x1F, 0x00, 0x1F, 0x3C, 0x00, 0x1E, 0x78, + 0x00, 0x3C, 0xF0, 0x00, 0x78, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, + 0x07, 0x80, 0x00, 0x7F, 0x00, 0x1F, 0xFC, 0x00, 0x3F, 0xE0, 0x00, 0x7F, + 0xE0, 0x00, 0xFF, 0xF0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x03, + 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x0F, 0xF0, 0x00, 0x1F, + 0xE0, 0x00, 0x3F, 0xE0, 0x00, 0xFB, 0xC0, 0x01, 0xE7, 0xC0, 0x07, 0xC7, + 0xE0, 0x3F, 0x0F, 0xFF, 0xFE, 0x0F, 0xFF, 0xF8, 0x07, 0xFF, 0xC0, 0x03, + 0xFC, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x1F, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF8, 0x00, 0x0F, 0xF0, 0x00, 0x3F, + 0xE0, 0x00, 0x7B, 0xC0, 0x01, 0xE7, 0x80, 0x07, 0x8F, 0x00, 0x0F, 0x1E, + 0x00, 0x3C, 0x3C, 0x00, 0xF0, 0x78, 0x03, 0xC0, 0xF0, 0x07, 0x81, 0xE0, + 0x1E, 0x03, 0xC0, 0x78, 0x07, 0x81, 0xE0, 0x0F, 0x03, 0xC0, 0x1E, 0x0F, + 0x00, 0x3C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x78, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x03, + 0xC0, 0x1F, 0xFF, 0xF0, 0x7F, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFC, + 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x1F, 0x3F, 0x80, 0x7B, 0xFF, + 0x81, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x1F, 0x80, 0xFC, 0x78, 0x01, 0xF8, + 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0xFF, 0x80, 0x07, 0x9E, 0x00, 0x1E, 0x7C, 0x00, 0xF1, 0xFC, 0x0F, 0xC3, + 0xFF, 0xFE, 0x07, 0xFF, 0xF0, 0x0F, 0xFF, 0x80, 0x07, 0xF0, 0x00, 0x00, + 0xFE, 0x00, 0x0F, 0xFE, 0x00, 0x7F, 0xFC, 0x03, 0xFF, 0xF8, 0x1F, 0x83, + 0xF0, 0xF8, 0x07, 0xC3, 0xC0, 0x0F, 0x8F, 0x00, 0x1E, 0x78, 0x00, 0x79, + 0xE0, 0x00, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF0, 0xFE, 0x03, 0xCF, + 0xFE, 0x0F, 0x7F, 0xFE, 0x3F, 0xFF, 0xFC, 0xFF, 0x03, 0xF3, 0xF0, 0x03, + 0xEF, 0x80, 0x07, 0xBE, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, + 0x00, 0x03, 0xFC, 0x00, 0x0F, 0x70, 0x00, 0x3D, 0xC0, 0x00, 0xF7, 0x80, + 0x07, 0x9F, 0x00, 0x3E, 0x3E, 0x00, 0xF8, 0xFC, 0x0F, 0xC1, 0xFF, 0xFE, + 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0x80, 0x07, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0x00, 0x00, + 0x78, 0x00, 0x07, 0x80, 0x00, 0x38, 0x00, 0x03, 0xC0, 0x00, 0x3C, 0x00, + 0x01, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x70, + 0x00, 0x07, 0x80, 0x00, 0x38, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x01, + 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x38, 0x00, + 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, + 0x01, 0xFE, 0x00, 0x1F, 0xFE, 0x00, 0xFF, 0xFC, 0x07, 0xFF, 0xF8, 0x3F, + 0x03, 0xF1, 0xF0, 0x03, 0xC7, 0xC0, 0x0F, 0x9E, 0x00, 0x1E, 0x78, 0x00, + 0x79, 0xE0, 0x01, 0xE7, 0x80, 0x0F, 0x8F, 0x00, 0x3C, 0x3F, 0x03, 0xF0, + 0x7F, 0xFF, 0x80, 0x7F, 0xF8, 0x03, 0xFF, 0xF0, 0x1F, 0xFF, 0xE0, 0xFC, + 0x0F, 0xC7, 0xC0, 0x0F, 0x9E, 0x00, 0x1E, 0xF8, 0x00, 0x7F, 0xC0, 0x00, + 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, + 0x80, 0x07, 0xDE, 0x00, 0x1E, 0x7C, 0x00, 0xF8, 0xFC, 0x0F, 0xC3, 0xFF, + 0xFF, 0x07, 0xFF, 0xF8, 0x07, 0xFF, 0x80, 0x07, 0xF8, 0x00, 0x01, 0xFC, + 0x00, 0x3F, 0xF8, 0x03, 0xFF, 0xE0, 0x3F, 0xFF, 0x83, 0xF0, 0x7E, 0x3E, + 0x00, 0xF1, 0xE0, 0x07, 0xCF, 0x00, 0x1E, 0xF0, 0x00, 0x77, 0x80, 0x03, + 0xBC, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xE0, + 0x03, 0xEF, 0x00, 0x1F, 0x7C, 0x01, 0xF9, 0xF8, 0x3F, 0xCF, 0xFF, 0xFE, + 0x3F, 0xFE, 0xF0, 0xFF, 0xE7, 0x80, 0xFC, 0x3C, 0x00, 0x01, 0xE0, 0x00, + 0x0E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x9E, 0x00, 0x3C, 0xF0, 0x03, 0xC7, + 0xC0, 0x3E, 0x1F, 0x03, 0xE0, 0xFF, 0xFE, 0x03, 0xFF, 0xE0, 0x0F, 0xFE, + 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF3, 0x33, 0x36, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x7F, 0xC0, + 0x03, 0xFC, 0x00, 0x3F, 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, + 0x80, 0x03, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x0F, 0xF0, + 0x00, 0x07, 0xFC, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x3F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xE0, 0x00, 0x01, + 0xC0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x80, 0x00, 0x01, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x07, + 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x3F, 0xC0, 0x00, 0x1F, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xF8, 0x00, + 0x01, 0xF0, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0xC0, 0x03, 0xFC, 0x00, 0x1F, + 0xE0, 0x01, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0xFC, 0x00, + 0x0F, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0xF8, + 0x00, 0xFF, 0xF0, 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x7E, 0x0F, 0xE7, 0x80, + 0x3E, 0x78, 0x01, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, + 0x00, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, + 0x01, 0xF0, 0x00, 0x1E, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xE0, + 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xC0, + 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, + 0x07, 0xFC, 0x03, 0xFF, 0x00, 0x01, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x7E, + 0x00, 0x00, 0x7F, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xF0, 0x07, 0xC0, 0x00, + 0x00, 0x3F, 0x01, 0xF0, 0x00, 0x00, 0x03, 0xF0, 0x3C, 0x00, 0x7E, 0x00, + 0x3E, 0x0F, 0x00, 0x3F, 0xE3, 0xC3, 0xE3, 0xE0, 0x1F, 0xFE, 0x78, 0x3C, + 0x78, 0x07, 0xE1, 0xFF, 0x07, 0xDF, 0x01, 0xF0, 0x1F, 0xC0, 0xFB, 0xC0, + 0x7C, 0x01, 0xF8, 0x0F, 0x78, 0x0F, 0x00, 0x3F, 0x01, 0xEF, 0x03, 0xC0, + 0x07, 0xC0, 0x3F, 0xC0, 0x78, 0x00, 0xF8, 0x07, 0xF8, 0x0F, 0x00, 0x1F, + 0x00, 0xFF, 0x03, 0xC0, 0x03, 0xC0, 0x1F, 0xE0, 0x78, 0x00, 0x78, 0x07, + 0xFC, 0x0F, 0x00, 0x1F, 0x00, 0xF7, 0x81, 0xE0, 0x03, 0xC0, 0x1E, 0xF0, + 0x3C, 0x00, 0x78, 0x07, 0x9E, 0x07, 0x80, 0x1F, 0x01, 0xF3, 0xE0, 0xF8, + 0x07, 0xC0, 0x3C, 0x3C, 0x0F, 0x81, 0xF8, 0x0F, 0x87, 0x81, 0xF8, 0x7F, + 0x87, 0xE0, 0xF8, 0x1F, 0xFE, 0xFF, 0xF8, 0x0F, 0x01, 0xFF, 0x1F, 0xFC, + 0x01, 0xF0, 0x0F, 0x80, 0xFE, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, + 0x7C, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xF8, + 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xF7, 0xC0, 0x00, 0x03, + 0xDF, 0x00, 0x00, 0x1F, 0x3C, 0x00, 0x00, 0x78, 0xF8, 0x00, 0x01, 0xE3, + 0xE0, 0x00, 0x0F, 0x87, 0x80, 0x00, 0x3C, 0x1F, 0x00, 0x01, 0xF0, 0x7C, + 0x00, 0x07, 0x80, 0xF0, 0x00, 0x1E, 0x03, 0xE0, 0x00, 0xF8, 0x0F, 0x80, + 0x03, 0xC0, 0x1E, 0x00, 0x0F, 0x00, 0x7C, 0x00, 0x7C, 0x01, 0xF0, 0x01, + 0xE0, 0x03, 0xC0, 0x07, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFE, 0x00, 0xFF, + 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF0, 0x1F, 0x00, 0x07, 0xC0, 0x78, 0x00, + 0x0F, 0x83, 0xE0, 0x00, 0x3E, 0x0F, 0x80, 0x00, 0xF8, 0x3C, 0x00, 0x01, + 0xF1, 0xF0, 0x00, 0x07, 0xC7, 0xC0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x3E, + 0xF8, 0x00, 0x00, 0xFB, 0xE0, 0x00, 0x01, 0xE0, 0xFF, 0xFF, 0x80, 0x7F, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x0F, 0xC7, + 0x80, 0x01, 0xE3, 0xC0, 0x00, 0xF9, 0xE0, 0x00, 0x3C, 0xF0, 0x00, 0x1E, + 0x78, 0x00, 0x0F, 0x3C, 0x00, 0x07, 0x9E, 0x00, 0x07, 0x8F, 0x00, 0x03, + 0xC7, 0x80, 0x07, 0xC3, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, + 0xF8, 0x7F, 0xFF, 0xFE, 0x3C, 0x00, 0x0F, 0x9E, 0x00, 0x03, 0xEF, 0x00, + 0x00, 0xF7, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x0F, 0xF0, + 0x00, 0x07, 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x01, 0xFF, + 0x00, 0x01, 0xF7, 0x80, 0x01, 0xFB, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x07, + 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x00, 0xFE, 0x01, + 0xF8, 0x07, 0xC0, 0x03, 0xE0, 0x7C, 0x00, 0x0F, 0x87, 0xC0, 0x00, 0x3C, + 0x3C, 0x00, 0x01, 0xE3, 0xE0, 0x00, 0x07, 0x9E, 0x00, 0x00, 0x3C, 0xF0, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x78, 0x00, 0x00, 0x03, 0xC0, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x78, 0x00, + 0x00, 0x7B, 0xC0, 0x00, 0x07, 0xDF, 0x00, 0x00, 0x3C, 0x78, 0x00, 0x01, + 0xE3, 0xE0, 0x00, 0x1F, 0x0F, 0x80, 0x01, 0xF0, 0x3E, 0x00, 0x1F, 0x81, + 0xFE, 0x03, 0xF8, 0x07, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xF8, 0x00, 0x3F, + 0xFF, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFE, + 0x03, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFE, 0x0F, 0x00, 0x0F, 0xE1, 0xE0, + 0x00, 0x7E, 0x3C, 0x00, 0x07, 0xE7, 0x80, 0x00, 0x7C, 0xF0, 0x00, 0x07, + 0xDE, 0x00, 0x00, 0x7B, 0xC0, 0x00, 0x0F, 0x78, 0x00, 0x01, 0xEF, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x0F, + 0xF0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x07, 0xF8, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xF7, + 0x80, 0x00, 0x1E, 0xF0, 0x00, 0x03, 0xDE, 0x00, 0x00, 0xFB, 0xC0, 0x00, + 0x3E, 0x78, 0x00, 0x0F, 0xCF, 0x00, 0x03, 0xF1, 0xE0, 0x01, 0xFC, 0x3F, + 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xF0, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, + 0xFE, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, + 0x3F, 0xFF, 0xFC, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0x3C, + 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF0, + 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0xFF, 0xFE, + 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x7F, 0x80, 0x7F, + 0x01, 0xF8, 0x00, 0x3F, 0x07, 0xE0, 0x00, 0x1F, 0x0F, 0x80, 0x00, 0x1E, + 0x3E, 0x00, 0x00, 0x3E, 0x78, 0x00, 0x00, 0x3D, 0xF0, 0x00, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x01, 0xE0, + 0x00, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x00, + 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xBC, 0x00, + 0x00, 0x0F, 0x78, 0x00, 0x00, 0x1E, 0xF8, 0x00, 0x00, 0x7D, 0xF0, 0x00, + 0x00, 0xF9, 0xF0, 0x00, 0x03, 0xF3, 0xF0, 0x00, 0x07, 0xE3, 0xF0, 0x00, + 0x1F, 0xC3, 0xF0, 0x00, 0xFF, 0x83, 0xFC, 0x07, 0xEF, 0x03, 0xFF, 0xFF, + 0x9E, 0x03, 0xFF, 0xFE, 0x1C, 0x01, 0xFF, 0xF0, 0x38, 0x00, 0x7F, 0x80, + 0x00, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0xC0, + 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, + 0xFC, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xE0, + 0x00, 0x3C, 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, + 0x00, 0x78, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x07, 0x80, + 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x0F, 0x00, + 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x1E, 0x00, + 0x03, 0xC0, 0x00, 0x7F, 0x80, 0x0F, 0xF0, 0x01, 0xFE, 0x00, 0x3F, 0xC0, + 0x07, 0xF8, 0x01, 0xFF, 0x80, 0x3E, 0xF0, 0x0F, 0x9F, 0x83, 0xF1, 0xFF, + 0xFC, 0x3F, 0xFF, 0x01, 0xFF, 0xC0, 0x0F, 0xE0, 0x00, 0xF0, 0x00, 0x07, + 0xDE, 0x00, 0x01, 0xF3, 0xC0, 0x00, 0x7C, 0x78, 0x00, 0x1F, 0x0F, 0x00, + 0x07, 0xC1, 0xE0, 0x01, 0xF0, 0x3C, 0x00, 0x7C, 0x07, 0x80, 0x1F, 0x00, + 0xF0, 0x07, 0xC0, 0x1E, 0x01, 0xF0, 0x03, 0xC0, 0x7C, 0x00, 0x78, 0x1F, + 0x00, 0x0F, 0x07, 0xC0, 0x01, 0xE1, 0xF0, 0x00, 0x3C, 0x7E, 0x00, 0x07, + 0x9F, 0xE0, 0x00, 0xF7, 0xFE, 0x00, 0x1F, 0xF7, 0xC0, 0x03, 0xFC, 0x7C, + 0x00, 0x7F, 0x07, 0xC0, 0x0F, 0xC0, 0xF8, 0x01, 0xF0, 0x0F, 0x80, 0x3C, + 0x00, 0xF8, 0x07, 0x80, 0x1F, 0x80, 0xF0, 0x01, 0xF0, 0x1E, 0x00, 0x1F, + 0x03, 0xC0, 0x03, 0xF0, 0x78, 0x00, 0x3E, 0x0F, 0x00, 0x03, 0xE1, 0xE0, + 0x00, 0x3E, 0x3C, 0x00, 0x07, 0xC7, 0x80, 0x00, 0x7C, 0xF0, 0x00, 0x07, + 0xDE, 0x00, 0x00, 0xFC, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, + 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, + 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, + 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, + 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, + 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFC, 0x00, + 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, + 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xF7, 0x00, 0x00, 0xEF, 0xF7, 0x80, 0x01, 0xEF, 0xF7, 0x80, + 0x01, 0xEF, 0xF3, 0xC0, 0x01, 0xCF, 0xF3, 0xC0, 0x03, 0xCF, 0xF3, 0xC0, + 0x03, 0xCF, 0xF1, 0xE0, 0x03, 0x8F, 0xF1, 0xE0, 0x07, 0x8F, 0xF1, 0xE0, + 0x07, 0x8F, 0xF0, 0xF0, 0x0F, 0x0F, 0xF0, 0xF0, 0x0F, 0x0F, 0xF0, 0xF0, + 0x0F, 0x0F, 0xF0, 0x78, 0x1E, 0x0F, 0xF0, 0x78, 0x1E, 0x0F, 0xF0, 0x78, + 0x1E, 0x0F, 0xF0, 0x3C, 0x3C, 0x0F, 0xF0, 0x3C, 0x3C, 0x0F, 0xF0, 0x3C, + 0x3C, 0x0F, 0xF0, 0x1E, 0x78, 0x0F, 0xF0, 0x1E, 0x78, 0x0F, 0xF0, 0x0E, + 0x78, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x07, + 0xF0, 0x0F, 0xF0, 0x07, 0xE0, 0x0F, 0xF0, 0x07, 0xE0, 0x0F, 0xF0, 0x03, + 0xE0, 0x0F, 0xF8, 0x00, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x3F, + 0xF8, 0x00, 0x0F, 0xFE, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0xFF, 0xF8, 0x00, + 0x3F, 0xDE, 0x00, 0x0F, 0xF7, 0xC0, 0x03, 0xFC, 0xF8, 0x00, 0xFF, 0x1E, + 0x00, 0x3F, 0xC7, 0xC0, 0x0F, 0xF0, 0xF0, 0x03, 0xFC, 0x3E, 0x00, 0xFF, + 0x07, 0xC0, 0x3F, 0xC0, 0xF0, 0x0F, 0xF0, 0x3E, 0x03, 0xFC, 0x07, 0xC0, + 0xFF, 0x00, 0xF0, 0x3F, 0xC0, 0x3E, 0x0F, 0xF0, 0x07, 0x83, 0xFC, 0x01, + 0xF0, 0xFF, 0x00, 0x3E, 0x3F, 0xC0, 0x07, 0x8F, 0xF0, 0x01, 0xF3, 0xFC, + 0x00, 0x3E, 0xFF, 0x00, 0x07, 0xBF, 0xC0, 0x01, 0xFF, 0xF0, 0x00, 0x3F, + 0xFC, 0x00, 0x0F, 0xFF, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0x00, + 0x0F, 0xFC, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xFF, + 0x80, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0x80, + 0xFF, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x7E, 0x00, 0x03, 0xF0, 0x3E, 0x00, + 0x00, 0xF8, 0x3E, 0x00, 0x00, 0x3E, 0x1E, 0x00, 0x00, 0x0F, 0x1F, 0x00, + 0x00, 0x07, 0xCF, 0x00, 0x00, 0x01, 0xE7, 0x80, 0x00, 0x00, 0xF7, 0xC0, + 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x00, 0x3E, 0xF0, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x0F, + 0x3E, 0x00, 0x00, 0x0F, 0x8F, 0x00, 0x00, 0x07, 0x87, 0xC0, 0x00, 0x07, + 0xC1, 0xF0, 0x00, 0x07, 0xC0, 0xFC, 0x00, 0x07, 0xE0, 0x3F, 0x00, 0x07, + 0xE0, 0x0F, 0xF0, 0x1F, 0xE0, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, + 0xE0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0x80, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC, 0xF0, 0x00, + 0xFE, 0xF0, 0x00, 0x3E, 0xF0, 0x00, 0x1F, 0xF0, 0x00, 0x0F, 0xF0, 0x00, + 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x00, + 0x0F, 0xF0, 0x00, 0x1F, 0xF0, 0x00, 0x3E, 0xF0, 0x00, 0xFE, 0xFF, 0xFF, + 0xFC, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xC0, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0x80, + 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0x80, 0xFF, + 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x7E, 0x00, 0x03, 0xF0, 0x3E, 0x00, 0x00, + 0xF8, 0x3E, 0x00, 0x00, 0x3E, 0x1E, 0x00, 0x00, 0x0F, 0x1F, 0x00, 0x00, + 0x07, 0xCF, 0x00, 0x00, 0x01, 0xE7, 0x80, 0x00, 0x00, 0xF7, 0xC0, 0x00, + 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x00, + 0x00, 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x01, 0xFE, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x3F, 0xC0, + 0x00, 0x00, 0x3E, 0xF0, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x0F, 0x3E, + 0x00, 0x00, 0x0F, 0x8F, 0x00, 0x03, 0x87, 0x87, 0xC0, 0x03, 0xE7, 0xC1, + 0xF0, 0x00, 0xFF, 0xC0, 0xFC, 0x00, 0x3F, 0xE0, 0x3F, 0x00, 0x0F, 0xE0, + 0x0F, 0xF0, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x1F, 0xFF, 0xC7, 0xC0, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFE, 0x0F, + 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xF8, 0xF0, 0x00, 0x3F, 0x3C, 0x00, 0x07, + 0xCF, 0x00, 0x00, 0xFB, 0xC0, 0x00, 0x1E, 0xF0, 0x00, 0x07, 0xBC, 0x00, + 0x01, 0xEF, 0x00, 0x00, 0x7B, 0xC0, 0x00, 0x1E, 0xF0, 0x00, 0x07, 0xBC, + 0x00, 0x03, 0xCF, 0x00, 0x01, 0xF3, 0xC0, 0x00, 0xF8, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xF8, 0xF0, 0x00, + 0x3F, 0x3C, 0x00, 0x03, 0xCF, 0x00, 0x00, 0xFB, 0xC0, 0x00, 0x1E, 0xF0, + 0x00, 0x07, 0xBC, 0x00, 0x01, 0xEF, 0x00, 0x00, 0x7B, 0xC0, 0x00, 0x1E, + 0xF0, 0x00, 0x07, 0xBC, 0x00, 0x01, 0xEF, 0x00, 0x00, 0x7B, 0xC0, 0x00, + 0x1E, 0xF0, 0x00, 0x07, 0xFC, 0x00, 0x01, 0xF0, 0x00, 0x7F, 0xC0, 0x00, + 0x7F, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x81, 0xF8, 0x07, + 0xF0, 0x7C, 0x00, 0x1F, 0x0F, 0x00, 0x01, 0xE3, 0xE0, 0x00, 0x3E, 0x78, + 0x00, 0x03, 0xCF, 0x00, 0x00, 0x79, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x07, 0xC0, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0x00, 0x01, 0xFF, 0xF8, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x07, 0xF8, 0x00, 0x00, 0xF7, 0x80, 0x00, 0x3E, 0xF8, 0x00, + 0x07, 0x9F, 0x80, 0x01, 0xF1, 0xFE, 0x01, 0xFC, 0x1F, 0xFF, 0xFF, 0x01, + 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0x00, 0x3F, 0xE0, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, + 0x00, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0xC0, + 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x7D, 0xE0, 0x00, 0x1E, 0x7C, 0x00, 0x0F, 0x9F, + 0x80, 0x07, 0xE3, 0xF8, 0x07, 0xF0, 0x7F, 0xFF, 0xF8, 0x0F, 0xFF, 0xFC, + 0x00, 0xFF, 0xFC, 0x00, 0x0F, 0xF8, 0x00, 0xF8, 0x00, 0x00, 0xF7, 0xC0, + 0x00, 0x0F, 0x9E, 0x00, 0x00, 0x7C, 0xF8, 0x00, 0x03, 0xC7, 0xC0, 0x00, + 0x3E, 0x1E, 0x00, 0x01, 0xF0, 0xF8, 0x00, 0x0F, 0x07, 0xC0, 0x00, 0xF8, + 0x1E, 0x00, 0x07, 0xC0, 0xF8, 0x00, 0x3C, 0x07, 0xC0, 0x03, 0xE0, 0x1E, + 0x00, 0x1F, 0x00, 0xF8, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x80, 0x1E, 0x00, + 0x7C, 0x00, 0xF8, 0x03, 0xC0, 0x03, 0xC0, 0x1E, 0x00, 0x1F, 0x01, 0xF0, + 0x00, 0xF8, 0x0F, 0x00, 0x03, 0xC0, 0x78, 0x00, 0x1F, 0x07, 0x80, 0x00, + 0xF8, 0x3C, 0x00, 0x03, 0xC1, 0xE0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x78, + 0xF0, 0x00, 0x03, 0xC7, 0x80, 0x00, 0x1F, 0x78, 0x00, 0x00, 0x7B, 0xC0, + 0x00, 0x03, 0xDE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, + 0x00, 0x3F, 0x00, 0x07, 0xFE, 0x00, 0x0F, 0xC0, 0x01, 0xFF, 0x80, 0x03, + 0xF0, 0x00, 0x7D, 0xE0, 0x00, 0xFC, 0x00, 0x1E, 0x7C, 0x00, 0x7F, 0x80, + 0x0F, 0x9F, 0x00, 0x1F, 0xE0, 0x03, 0xE7, 0xC0, 0x07, 0xF8, 0x00, 0xF8, + 0xF0, 0x01, 0xFF, 0x00, 0x3C, 0x3E, 0x00, 0xF3, 0xC0, 0x1F, 0x0F, 0x80, + 0x3C, 0xF0, 0x07, 0xC3, 0xE0, 0x0F, 0x3C, 0x01, 0xF0, 0x78, 0x07, 0xC7, + 0x80, 0x78, 0x1F, 0x01, 0xE1, 0xE0, 0x1E, 0x07, 0xC0, 0x78, 0x78, 0x0F, + 0x80, 0xF0, 0x1E, 0x1E, 0x03, 0xE0, 0x3C, 0x0F, 0x83, 0xC0, 0xF0, 0x0F, + 0x83, 0xC0, 0xF0, 0x3C, 0x03, 0xE0, 0xF0, 0x3C, 0x1F, 0x00, 0x78, 0x3C, + 0x0F, 0x87, 0xC0, 0x1E, 0x1E, 0x01, 0xE1, 0xE0, 0x07, 0x87, 0x80, 0x78, + 0x78, 0x01, 0xF1, 0xE0, 0x1E, 0x1E, 0x00, 0x3C, 0xF8, 0x03, 0xCF, 0x80, + 0x0F, 0x3C, 0x00, 0xF3, 0xC0, 0x03, 0xCF, 0x00, 0x3C, 0xF0, 0x00, 0xFB, + 0xC0, 0x0F, 0xBC, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x07, 0xF8, 0x00, + 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x03, 0xF8, + 0x00, 0x0F, 0xC0, 0x00, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x00, + 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x01, 0xF3, 0xF0, 0x00, 0x1F, 0x8F, 0x80, 0x00, 0xF8, 0x3E, 0x00, 0x0F, + 0x80, 0xF8, 0x00, 0xF8, 0x07, 0xC0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x00, + 0x7C, 0x07, 0xC0, 0x03, 0xE0, 0x3E, 0x00, 0x0F, 0x83, 0xE0, 0x00, 0x3E, + 0x3E, 0x00, 0x01, 0xF1, 0xF0, 0x00, 0x07, 0xDF, 0x00, 0x00, 0x1F, 0xF0, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x07, + 0xDF, 0x00, 0x00, 0x7C, 0x78, 0x00, 0x03, 0xE3, 0xE0, 0x00, 0x3E, 0x0F, + 0x80, 0x03, 0xE0, 0x3E, 0x00, 0x1F, 0x01, 0xF0, 0x01, 0xF0, 0x07, 0xC0, + 0x1F, 0x00, 0x3F, 0x00, 0xF8, 0x00, 0xF8, 0x0F, 0x80, 0x03, 0xE0, 0xF8, + 0x00, 0x1F, 0x8F, 0xC0, 0x00, 0x7C, 0x7C, 0x00, 0x01, 0xF7, 0xC0, 0x00, + 0x0F, 0xC0, 0xFC, 0x00, 0x00, 0xFD, 0xF0, 0x00, 0x03, 0xE7, 0xE0, 0x00, + 0x1F, 0x0F, 0x80, 0x00, 0x7C, 0x1F, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x1F, + 0x00, 0xF8, 0x00, 0x7C, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, + 0x0F, 0x80, 0x7C, 0x00, 0x1E, 0x01, 0xE0, 0x00, 0x7C, 0x0F, 0x80, 0x00, + 0xF8, 0x7C, 0x00, 0x03, 0xE1, 0xE0, 0x00, 0x07, 0xCF, 0x80, 0x00, 0x0F, + 0x3C, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, + 0x07, 0x80, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3F, 0xFF, + 0xFF, 0xC7, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xE0, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x01, + 0xF8, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x07, 0xE0, 0x00, 0x01, + 0xF8, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xF0, 0x00, + 0x00, 0xFC, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0, 0x07, 0x00, 0x18, 0x00, 0xE0, 0x07, 0x00, 0x18, 0x00, 0xE0, + 0x07, 0x00, 0x18, 0x00, 0xC0, 0x07, 0x00, 0x38, 0x00, 0xC0, 0x07, 0x00, + 0x38, 0x00, 0xC0, 0x06, 0x00, 0x38, 0x00, 0xC0, 0x06, 0x00, 0x38, 0x01, + 0xC0, 0x06, 0x00, 0x38, 0x01, 0xC0, 0x06, 0x00, 0x30, 0x01, 0xC0, 0x0E, + 0x00, 0x30, 0x01, 0xC0, 0x0E, 0x00, 0x30, 0x01, 0xC0, 0x0E, 0xFF, 0xFF, + 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x3F, + 0x00, 0x0F, 0xC0, 0x07, 0xF8, 0x01, 0xCE, 0x00, 0x73, 0x80, 0x3C, 0x70, + 0x0E, 0x1C, 0x07, 0x87, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x38, 0x07, 0x0E, + 0x01, 0xC7, 0x80, 0x79, 0xC0, 0x0E, 0x70, 0x03, 0xB8, 0x00, 0x70, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0x01, 0xE0, 0x3C, 0x07, + 0x00, 0xE0, 0x1C, 0x01, 0xFF, 0x00, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0xE0, + 0x3F, 0xFF, 0xF0, 0x7E, 0x03, 0xF8, 0x7C, 0x00, 0xF8, 0x78, 0x00, 0x78, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0xF8, + 0x00, 0x03, 0xF8, 0x00, 0xFF, 0xF8, 0x0F, 0xFF, 0xF8, 0x3F, 0xFE, 0x78, + 0x7F, 0x80, 0x78, 0xFC, 0x00, 0x78, 0xF8, 0x00, 0x78, 0xF0, 0x00, 0x78, + 0xF0, 0x00, 0xF8, 0xF0, 0x00, 0xF8, 0xF8, 0x03, 0xF8, 0x7E, 0x0F, 0xF8, + 0x7F, 0xFF, 0x7F, 0x3F, 0xFE, 0x3F, 0x1F, 0xFC, 0x3F, 0x07, 0xE0, 0x1F, + 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF0, + 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF0, 0x7E, + 0x03, 0xC7, 0xFE, 0x0F, 0x7F, 0xFC, 0x3D, 0xFF, 0xF8, 0xFF, 0x07, 0xF3, + 0xF8, 0x07, 0xCF, 0xC0, 0x0F, 0xBE, 0x00, 0x1E, 0xF8, 0x00, 0x7B, 0xE0, + 0x01, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, + 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x01, 0xFF, + 0x80, 0x07, 0xBE, 0x00, 0x1E, 0xFC, 0x00, 0xFB, 0xF8, 0x07, 0xCF, 0xF0, + 0x7F, 0x3B, 0xFF, 0xF8, 0xE7, 0xFF, 0xC3, 0x8F, 0xFE, 0x00, 0x0F, 0xE0, + 0x00, 0x00, 0xFE, 0x00, 0x3F, 0xFC, 0x03, 0xFF, 0xF0, 0x3F, 0xFF, 0xC3, + 0xF0, 0x3F, 0x1F, 0x00, 0xF9, 0xF0, 0x03, 0xCF, 0x00, 0x0F, 0x78, 0x00, + 0x07, 0xC0, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, + 0x1E, 0x00, 0x1E, 0xF0, 0x00, 0xF7, 0xC0, 0x0F, 0x9F, 0x00, 0xF8, 0xFC, + 0x0F, 0xC3, 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, 0x3F, 0xFC, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x78, 0x00, 0x00, 0xF0, + 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x01, + 0xFC, 0x1E, 0x0F, 0xFE, 0x3C, 0x3F, 0xFF, 0x78, 0xFF, 0xFF, 0xF3, 0xF8, + 0x3F, 0xE7, 0xC0, 0x1F, 0xDF, 0x00, 0x1F, 0xBE, 0x00, 0x1F, 0x78, 0x00, + 0x3F, 0xF0, 0x00, 0x7F, 0xC0, 0x00, 0x7F, 0x80, 0x00, 0xFF, 0x00, 0x01, + 0xFE, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xF8, 0x00, 0x0F, 0xF0, 0x00, 0x1F, + 0xF0, 0x00, 0x7D, 0xE0, 0x00, 0xFB, 0xC0, 0x01, 0xF7, 0xC0, 0x07, 0xE7, + 0xC0, 0x1F, 0xCF, 0xE0, 0xFF, 0x8F, 0xFF, 0xF7, 0x0F, 0xFF, 0xCE, 0x0F, + 0xFF, 0x1C, 0x07, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x0F, 0xFE, 0x00, 0xFF, + 0xFC, 0x07, 0xFF, 0xF8, 0x1F, 0x83, 0xF0, 0xF8, 0x07, 0xC7, 0xC0, 0x0F, + 0x9E, 0x00, 0x1E, 0x78, 0x00, 0x7B, 0xC0, 0x00, 0xFF, 0x00, 0x03, 0xFC, + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, + 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x1E, + 0x7C, 0x00, 0x78, 0xF8, 0x03, 0xE3, 0xF0, 0x3F, 0x07, 0xFF, 0xF8, 0x0F, + 0xFF, 0xE0, 0x1F, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0x03, 0xC3, 0xF0, 0xFC, + 0x7F, 0x1F, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x3F, 0xFF, 0xFF, 0xFF, 0x1E, + 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, + 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, + 0x1E, 0x07, 0x80, 0x00, 0xFC, 0x00, 0x1F, 0xF8, 0xF0, 0xFF, 0xFB, 0xC7, + 0xFF, 0xFF, 0x3F, 0x83, 0xFC, 0xF8, 0x07, 0xF7, 0xC0, 0x0F, 0xDE, 0x00, + 0x1F, 0x78, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, + 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, + 0x00, 0x3F, 0xC0, 0x00, 0xF7, 0x80, 0x07, 0xDE, 0x00, 0x1F, 0x7C, 0x00, + 0xFC, 0xF8, 0x07, 0xF3, 0xF8, 0x3F, 0xC7, 0xFF, 0xEF, 0x0F, 0xFF, 0x3C, + 0x1F, 0xF8, 0xF0, 0x1F, 0x83, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x79, 0xE0, + 0x01, 0xE7, 0xC0, 0x0F, 0x8F, 0x80, 0xFC, 0x3F, 0xFF, 0xF0, 0x7F, 0xFF, + 0x80, 0xFF, 0xFC, 0x00, 0x7F, 0x80, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, + 0x00, 0x78, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x07, 0x80, + 0x00, 0xF0, 0xFE, 0x1E, 0x3F, 0xE3, 0xCF, 0xFF, 0x7B, 0xFF, 0xEF, 0xF0, + 0xFF, 0xF8, 0x07, 0xFF, 0x00, 0x7F, 0xC0, 0x0F, 0xF8, 0x01, 0xFE, 0x00, + 0x3F, 0xC0, 0x07, 0xF8, 0x00, 0xFF, 0x00, 0x1F, 0xE0, 0x03, 0xFC, 0x00, + 0x7F, 0x80, 0x0F, 0xF0, 0x01, 0xFE, 0x00, 0x3F, 0xC0, 0x07, 0xF8, 0x00, + 0xFF, 0x00, 0x1F, 0xE0, 0x03, 0xFC, 0x00, 0x7F, 0x80, 0x0F, 0xF0, 0x01, + 0xFE, 0x00, 0x3C, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x1F, + 0xFF, 0xFE, 0xFE, 0xF8, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, + 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, + 0x00, 0xF0, 0x00, 0x07, 0x80, 0x1F, 0x3C, 0x01, 0xF1, 0xE0, 0x1F, 0x0F, + 0x01, 0xF0, 0x78, 0x1F, 0x03, 0xC1, 0xF0, 0x1E, 0x1F, 0x00, 0xF1, 0xF0, + 0x07, 0x9F, 0x00, 0x3D, 0xF8, 0x01, 0xFF, 0xE0, 0x0F, 0xFF, 0x80, 0x7F, + 0x7C, 0x03, 0xF1, 0xF0, 0x1F, 0x07, 0xC0, 0xF0, 0x3E, 0x07, 0x80, 0xF8, + 0x3C, 0x03, 0xC1, 0xE0, 0x1F, 0x0F, 0x00, 0x7C, 0x78, 0x03, 0xE3, 0xC0, + 0x0F, 0x9E, 0x00, 0x3C, 0xF0, 0x01, 0xF7, 0x80, 0x07, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xFC, 0x03, 0xF0, 0xE3, 0xFE, 0x0F, 0xFC, 0xE7, + 0xFF, 0x1F, 0xFE, 0xEF, 0xFF, 0xBF, 0xFE, 0xFE, 0x0F, 0xF8, 0x3F, 0xFC, + 0x07, 0xF0, 0x1F, 0xF8, 0x03, 0xE0, 0x0F, 0xF8, 0x03, 0xE0, 0x0F, 0xF0, + 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, + 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, + 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, + 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, + 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, + 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0xF0, 0x03, 0xC0, 0x0F, 0x00, + 0x7E, 0x0E, 0x1F, 0xF8, 0xE7, 0xFF, 0xCE, 0xFF, 0xFE, 0xEF, 0x07, 0xFF, + 0xE0, 0x1F, 0xFC, 0x01, 0xFF, 0x80, 0x0F, 0xF8, 0x00, 0xFF, 0x00, 0x0F, + 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, + 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, + 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, + 0x00, 0xFF, 0x00, 0x0F, 0x00, 0xFE, 0x00, 0x07, 0xFF, 0x00, 0x3F, 0xFF, + 0x80, 0xFF, 0xFF, 0x83, 0xF8, 0x3F, 0x87, 0xC0, 0x1F, 0x1F, 0x00, 0x1F, + 0x3C, 0x00, 0x1E, 0x78, 0x00, 0x3D, 0xF0, 0x00, 0x7F, 0xC0, 0x00, 0x7F, + 0x80, 0x00, 0xFF, 0x00, 0x01, 0xFE, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xF8, + 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0xF0, 0x00, 0x7D, 0xE0, 0x00, 0xF3, 0xC0, + 0x01, 0xE7, 0xC0, 0x07, 0xC7, 0xC0, 0x1F, 0x0F, 0xE0, 0xFE, 0x0F, 0xFF, + 0xF8, 0x0F, 0xFF, 0xE0, 0x0F, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x00, 0xFE, + 0x03, 0x8F, 0xFE, 0x0E, 0x7F, 0xFC, 0x3B, 0xFF, 0xF8, 0xFF, 0x87, 0xF3, + 0xF8, 0x07, 0xCF, 0xC0, 0x0F, 0xBE, 0x00, 0x1E, 0xF8, 0x00, 0x7B, 0xE0, + 0x01, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, + 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x01, 0xFF, + 0x80, 0x07, 0xBE, 0x00, 0x1E, 0xFC, 0x00, 0xFB, 0xF8, 0x07, 0xCF, 0xF0, + 0x7F, 0x3F, 0xFF, 0xF8, 0xF7, 0xFF, 0xC3, 0xC7, 0xFE, 0x0F, 0x07, 0xE0, + 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0x00, 0x07, 0xFF, 0x1C, 0x3F, 0xFF, 0x38, 0xFF, 0xFF, 0x73, 0xF8, + 0x3F, 0xE7, 0xC0, 0x1F, 0xDF, 0x00, 0x1F, 0xBE, 0x00, 0x1F, 0x78, 0x00, + 0x3F, 0xF0, 0x00, 0x7F, 0xC0, 0x00, 0x7F, 0x80, 0x00, 0xFF, 0x00, 0x01, + 0xFE, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xF8, 0x00, 0x0F, 0xF0, 0x00, 0x1F, + 0xF0, 0x00, 0x7D, 0xE0, 0x00, 0xFB, 0xC0, 0x01, 0xF7, 0xC0, 0x07, 0xE7, + 0xC0, 0x1F, 0xCF, 0xE0, 0xFF, 0x8F, 0xFF, 0xEF, 0x0F, 0xFF, 0xDE, 0x0F, + 0xFE, 0x3C, 0x07, 0xF0, 0x78, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, + 0x03, 0xC0, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x78, 0x00, 0xFE, 0x1F, 0xE7, 0xFE, 0xFF, 0xFF, 0x8F, + 0xC0, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0x01, 0xFC, 0x00, 0xFF, 0xF0, + 0x1F, 0xFF, 0x83, 0xFF, 0xFC, 0x3E, 0x07, 0xE7, 0xC0, 0x3E, 0x78, 0x01, + 0xE7, 0x80, 0x00, 0x78, 0x00, 0x07, 0xC0, 0x00, 0x7E, 0x00, 0x03, 0xFC, + 0x00, 0x1F, 0xFC, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0xC0, 0x03, 0xFE, 0x00, + 0x03, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, + 0x80, 0x1F, 0x7E, 0x07, 0xE7, 0xFF, 0xFE, 0x3F, 0xFF, 0xC1, 0xFF, 0xF0, + 0x03, 0xFC, 0x00, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x8F, 0xFF, + 0xFF, 0xFF, 0xC7, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, + 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, + 0xE0, 0x78, 0x1F, 0xC7, 0xF0, 0xFC, 0x1F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, + 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, + 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, + 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xF0, + 0x00, 0xFF, 0x00, 0x1F, 0xF0, 0x01, 0xFF, 0x00, 0x3F, 0xF8, 0x07, 0xFF, + 0xE0, 0xFF, 0x7F, 0xFF, 0x77, 0xFF, 0xE7, 0x1F, 0xFC, 0x70, 0x7E, 0x00, + 0x78, 0x00, 0x3E, 0xF0, 0x00, 0x79, 0xF0, 0x00, 0xF1, 0xE0, 0x03, 0xE3, + 0xC0, 0x07, 0x87, 0xC0, 0x0F, 0x07, 0x80, 0x3C, 0x0F, 0x00, 0x78, 0x1F, + 0x01, 0xF0, 0x1E, 0x03, 0xC0, 0x3C, 0x07, 0x80, 0x7C, 0x1F, 0x00, 0x78, + 0x3C, 0x00, 0xF0, 0x78, 0x01, 0xF1, 0xE0, 0x01, 0xE3, 0xC0, 0x03, 0xC7, + 0x80, 0x03, 0xDE, 0x00, 0x07, 0xBC, 0x00, 0x0F, 0x70, 0x00, 0x0F, 0xE0, + 0x00, 0x1F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, + 0xF8, 0x03, 0xE0, 0x07, 0x9E, 0x00, 0xFC, 0x01, 0xE7, 0x80, 0x3F, 0x00, + 0x79, 0xF0, 0x0F, 0xC0, 0x3E, 0x3C, 0x07, 0xF0, 0x0F, 0x0F, 0x01, 0xFE, + 0x03, 0xC3, 0xC0, 0x7F, 0x80, 0xF0, 0x78, 0x1D, 0xE0, 0x78, 0x1E, 0x0F, + 0x38, 0x1E, 0x07, 0x83, 0xCF, 0x07, 0x81, 0xE0, 0xF3, 0xC1, 0xE0, 0x3C, + 0x38, 0xF0, 0xF0, 0x0F, 0x1E, 0x1C, 0x3C, 0x03, 0xC7, 0x87, 0x8F, 0x00, + 0x71, 0xE1, 0xE3, 0x80, 0x1E, 0x70, 0x79, 0xE0, 0x07, 0xBC, 0x0E, 0x78, + 0x01, 0xEF, 0x03, 0xDE, 0x00, 0x3B, 0xC0, 0xF7, 0x00, 0x0F, 0xE0, 0x3F, + 0xC0, 0x03, 0xF8, 0x07, 0xF0, 0x00, 0x7E, 0x01, 0xF8, 0x00, 0x1F, 0x80, + 0x7E, 0x00, 0x07, 0xC0, 0x1F, 0x80, 0x01, 0xF0, 0x03, 0xC0, 0x00, 0x7C, + 0x00, 0x78, 0xF0, 0x03, 0xE1, 0xE0, 0x0F, 0x07, 0xC0, 0x78, 0x0F, 0x03, + 0xE0, 0x1E, 0x0F, 0x00, 0x7C, 0x78, 0x00, 0xF3, 0xE0, 0x01, 0xEF, 0x00, + 0x07, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, 0x03, + 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x7F, 0xC0, 0x03, 0xCF, 0x00, 0x0F, 0x1E, + 0x00, 0x78, 0x7C, 0x03, 0xE0, 0xF0, 0x0F, 0x03, 0xE0, 0x78, 0x07, 0xC3, + 0xE0, 0x0F, 0x1F, 0x00, 0x3E, 0x78, 0x00, 0x7C, 0x78, 0x00, 0x3D, 0xE0, + 0x01, 0xF7, 0x80, 0x07, 0x8F, 0x00, 0x1E, 0x3C, 0x00, 0xF0, 0xF0, 0x03, + 0xC1, 0xE0, 0x0F, 0x07, 0x80, 0x78, 0x1E, 0x01, 0xE0, 0x3C, 0x07, 0x80, + 0xF0, 0x3C, 0x03, 0xC0, 0xF0, 0x07, 0x87, 0xC0, 0x1E, 0x1E, 0x00, 0x78, + 0x78, 0x00, 0xF3, 0xC0, 0x03, 0xCF, 0x00, 0x0F, 0x3C, 0x00, 0x1F, 0xE0, + 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, + 0x3E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x3C, + 0x00, 0x01, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3E, 0x00, 0x0F, 0xF0, 0x00, + 0x3F, 0xC0, 0x00, 0xFE, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x7F, 0xFF, 0xF7, + 0xFF, 0xFF, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0x00, 0x01, 0xE0, 0x00, 0x3E, + 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, + 0x00, 0x07, 0x80, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, + 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x03, 0xC0, 0x00, 0x7C, + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x01, 0xE0, 0xFC, 0x1F, 0x87, 0x80, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, + 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, + 0xC0, 0x78, 0x1E, 0x0F, 0x81, 0xE0, 0x3C, 0x07, 0xC0, 0x3C, 0x03, 0x80, + 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, + 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0F, 0x00, 0xFC, 0x1F, 0x80, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x1F, 0x83, 0xF0, 0x0F, 0x00, + 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, + 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x1C, 0x03, 0xC0, 0x3E, 0x03, + 0xC0, 0x78, 0x1F, 0x07, 0x80, 0xE0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, + 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, + 0x70, 0x1E, 0x1F, 0x83, 0xF0, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0xF0, 0x0D, + 0xFF, 0x01, 0xF0, 0xF8, 0x7C, 0x0F, 0xFD, 0x80, 0x7F, 0x80, 0x03, 0xE0}; + +const GFXglyph FreeSans24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 12, 0, 1}, // 0x20 ' ' + {0, 4, 34, 16, 6, -33}, // 0x21 '!' + {17, 11, 12, 16, 2, -32}, // 0x22 '"' + {34, 24, 33, 26, 1, -31}, // 0x23 '#' + {133, 23, 41, 26, 1, -34}, // 0x24 '$' + {251, 39, 34, 42, 1, -32}, // 0x25 '%' + {417, 28, 34, 31, 2, -32}, // 0x26 '&' + {536, 4, 12, 9, 2, -32}, // 0x27 ''' + {542, 10, 44, 16, 3, -33}, // 0x28 '(' + {597, 10, 44, 16, 2, -33}, // 0x29 ')' + {652, 14, 14, 18, 2, -33}, // 0x2A '*' + {677, 23, 22, 27, 2, -21}, // 0x2B '+' + {741, 4, 12, 13, 4, -4}, // 0x2C ',' + {747, 11, 4, 16, 2, -14}, // 0x2D '-' + {753, 4, 5, 12, 4, -4}, // 0x2E '.' + {756, 13, 35, 13, 0, -33}, // 0x2F '/' + {813, 22, 34, 26, 2, -32}, // 0x30 '0' + {907, 11, 33, 26, 5, -32}, // 0x31 '1' + {953, 22, 33, 26, 2, -32}, // 0x32 '2' + {1044, 23, 34, 26, 1, -32}, // 0x33 '3' + {1142, 23, 33, 26, 1, -32}, // 0x34 '4' + {1237, 22, 34, 26, 2, -32}, // 0x35 '5' + {1331, 22, 34, 26, 2, -32}, // 0x36 '6' + {1425, 21, 33, 26, 2, -32}, // 0x37 '7' + {1512, 22, 34, 26, 2, -32}, // 0x38 '8' + {1606, 21, 34, 26, 2, -32}, // 0x39 '9' + {1696, 4, 25, 12, 4, -24}, // 0x3A ':' + {1709, 4, 32, 12, 4, -24}, // 0x3B ';' + {1725, 23, 23, 27, 2, -22}, // 0x3C '<' + {1792, 23, 12, 27, 2, -16}, // 0x3D '=' + {1827, 23, 23, 27, 2, -22}, // 0x3E '>' + {1894, 20, 35, 26, 4, -34}, // 0x3F '?' + {1982, 43, 42, 48, 2, -34}, // 0x40 '@' + {2208, 30, 34, 31, 1, -33}, // 0x41 'A' + {2336, 25, 34, 31, 4, -33}, // 0x42 'B' + {2443, 29, 36, 33, 2, -34}, // 0x43 'C' + {2574, 27, 34, 33, 4, -33}, // 0x44 'D' + {2689, 24, 34, 30, 4, -33}, // 0x45 'E' + {2791, 22, 34, 28, 4, -33}, // 0x46 'F' + {2885, 31, 36, 36, 2, -34}, // 0x47 'G' + {3025, 26, 34, 34, 4, -33}, // 0x48 'H' + {3136, 4, 34, 13, 5, -33}, // 0x49 'I' + {3153, 19, 35, 25, 2, -33}, // 0x4A 'J' + {3237, 27, 34, 32, 4, -33}, // 0x4B 'K' + {3352, 21, 34, 26, 4, -33}, // 0x4C 'L' + {3442, 32, 34, 40, 4, -33}, // 0x4D 'M' + {3578, 26, 34, 34, 4, -33}, // 0x4E 'N' + {3689, 33, 36, 37, 2, -34}, // 0x4F 'O' + {3838, 24, 34, 31, 4, -33}, // 0x50 'P' + {3940, 33, 38, 37, 2, -34}, // 0x51 'Q' + {4097, 26, 34, 33, 4, -33}, // 0x52 'R' + {4208, 27, 36, 31, 2, -34}, // 0x53 'S' + {4330, 26, 34, 30, 2, -33}, // 0x54 'T' + {4441, 26, 35, 34, 4, -33}, // 0x55 'U' + {4555, 29, 34, 30, 1, -33}, // 0x56 'V' + {4679, 42, 34, 44, 1, -33}, // 0x57 'W' + {4858, 29, 34, 31, 1, -33}, // 0x58 'X' + {4982, 30, 34, 32, 1, -33}, // 0x59 'Y' + {5110, 27, 34, 29, 1, -33}, // 0x5A 'Z' + {5225, 8, 44, 13, 3, -33}, // 0x5B '[' + {5269, 13, 35, 13, 0, -33}, // 0x5C '\' + {5326, 8, 44, 13, 1, -33}, // 0x5D ']' + {5370, 18, 18, 22, 2, -32}, // 0x5E '^' + {5411, 28, 2, 26, -1, 7}, // 0x5F '_' + {5418, 10, 7, 12, 1, -34}, // 0x60 '`' + {5427, 24, 27, 26, 1, -25}, // 0x61 'a' + {5508, 22, 35, 26, 3, -33}, // 0x62 'b' + {5605, 21, 27, 24, 1, -25}, // 0x63 'c' + {5676, 23, 35, 26, 1, -33}, // 0x64 'd' + {5777, 22, 27, 25, 1, -25}, // 0x65 'e' + {5852, 10, 34, 13, 1, -33}, // 0x66 'f' + {5895, 22, 36, 26, 1, -25}, // 0x67 'g' + {5994, 19, 34, 25, 3, -33}, // 0x68 'h' + {6075, 4, 34, 10, 3, -33}, // 0x69 'i' + {6092, 8, 44, 11, 0, -33}, // 0x6A 'j' + {6136, 21, 34, 24, 3, -33}, // 0x6B 'k' + {6226, 4, 34, 10, 3, -33}, // 0x6C 'l' + {6243, 32, 26, 38, 3, -25}, // 0x6D 'm' + {6347, 20, 26, 25, 3, -25}, // 0x6E 'n' + {6412, 23, 27, 25, 1, -25}, // 0x6F 'o' + {6490, 22, 35, 26, 3, -25}, // 0x70 'p' + {6587, 23, 35, 26, 1, -25}, // 0x71 'q' + {6688, 12, 26, 16, 3, -25}, // 0x72 'r' + {6727, 20, 27, 23, 1, -25}, // 0x73 's' + {6795, 10, 32, 13, 1, -30}, // 0x74 't' + {6835, 20, 26, 25, 3, -24}, // 0x75 'u' + {6900, 23, 25, 23, 0, -24}, // 0x76 'v' + {6972, 34, 25, 34, 0, -24}, // 0x77 'w' + {7079, 22, 25, 22, 0, -24}, // 0x78 'x' + {7148, 22, 35, 22, 0, -24}, // 0x79 'y' + {7245, 20, 25, 23, 1, -24}, // 0x7A 'z' + {7308, 11, 44, 16, 2, -33}, // 0x7B '{' + {7369, 3, 44, 12, 4, -33}, // 0x7C '|' + {7386, 11, 44, 16, 2, -33}, // 0x7D '}' + {7447, 19, 7, 24, 2, -19}}; // 0x7E '~' + +const GFXfont FreeSans24pt7b PROGMEM = {(uint8_t *)FreeSans24pt7bBitmaps, + (GFXglyph *)FreeSans24pt7bGlyphs, 0x20, + 0x7E, 56}; + +// Approx. 8136 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSans9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSans9pt7b.h new file mode 100644 index 0000000..f151f32 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSans9pt7b.h @@ -0,0 +1,200 @@ +const uint8_t FreeSans9pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xF8, 0xC0, 0xDE, 0xF7, 0x20, 0x09, 0x86, 0x41, 0x91, 0xFF, + 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90, 0x10, 0x1F, + 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, + 0x33, 0xF0, 0x40, 0x20, 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, + 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, + 0x04, 0x63, 0x04, 0x77, 0x08, 0x3C, 0x0E, 0x06, 0x60, 0xCC, 0x19, 0x81, + 0xE0, 0x18, 0x0F, 0x03, 0x36, 0xC2, 0xD8, 0x73, 0x06, 0x31, 0xE3, 0xC4, + 0xFE, 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10, 0x8C, 0x46, + 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80, 0x25, 0x7E, 0xA5, 0x00, 0x30, + 0xC3, 0x3F, 0x30, 0xC3, 0x0C, 0xD6, 0xF0, 0xC0, 0x08, 0x44, 0x21, 0x10, + 0x84, 0x42, 0x11, 0x08, 0x00, 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, + 0x30, 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, + 0x10, 0x08, 0x07, 0xF8, 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, + 0x03, 0xC3, 0xC3, 0x66, 0x3C, 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, + 0xFE, 0x18, 0x30, 0x60, 0xC0, 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, + 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0, 0x1E, 0x31, 0x98, 0x78, + 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0, 0xFF, + 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30, + 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, + 0x6C, 0x63, 0xE0, 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, + 0x03, 0xC2, 0x66, 0x3C, 0xC0, 0x00, 0x30, 0xC0, 0x00, 0x00, 0x64, 0xA0, + 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80, 0xFF, + 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x70, 0x0E, 0x01, 0xC0, 0x18, 0x38, 0x71, + 0xC0, 0x80, 0x00, 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, + 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80, 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, + 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, + 0x31, 0xE6, 0x11, 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, + 0x70, 0x40, 0x0F, 0xE0, 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, + 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30, + 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, + 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, + 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, + 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, + 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0, 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, + 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, 0xFF, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x0F, 0x83, + 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, + 0x03, 0x60, 0x73, 0x0F, 0x0F, 0x10, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, + 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, + 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, + 0x8F, 0x1E, 0x27, 0x80, 0xC0, 0xD8, 0x33, 0x0C, 0x63, 0x0C, 0xC1, 0xB8, + 0x3F, 0x07, 0x30, 0xC3, 0x18, 0x63, 0x06, 0x60, 0x6C, 0x0C, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xE0, + 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, + 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80, 0xE0, 0x7C, 0x0F, + 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, + 0xE0, 0x7C, 0x0E, 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, + 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8, + 0x00, 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, + 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, + 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, + 0x60, 0xC0, 0xFB, 0x00, 0x08, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, + 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, + 0x70, 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, + 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, + 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0xC0, 0x78, 0x0F, + 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, + 0xB0, 0x61, 0xF0, 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, + 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60, 0xC1, 0x81, 0x30, + 0xE1, 0x98, 0x70, 0xCC, 0x28, 0x66, 0x26, 0x21, 0x13, 0x30, 0xC8, 0x98, + 0x6C, 0x4C, 0x14, 0x34, 0x0A, 0x1A, 0x07, 0x07, 0x03, 0x03, 0x80, 0x81, + 0x80, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x0F, 0x00, 0xE0, 0x06, 0x00, + 0xF0, 0x19, 0x01, 0x98, 0x30, 0xC6, 0x0E, 0x60, 0x60, 0xC0, 0x36, 0x06, + 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x00, 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, + 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0, 0xFB, 0x6D, + 0xB6, 0xDB, 0x6D, 0xB6, 0xE0, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, + 0x10, 0x80, 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0, 0x30, 0x60, 0xA2, + 0x44, 0xD8, 0xA1, 0x80, 0xFF, 0xC0, 0xC6, 0x30, 0x7E, 0x71, 0xB0, 0xC0, + 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, 0xC0, 0x60, 0x30, 0x1B, + 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0, 0x3C, + 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x03, 0x03, 0x03, + 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x3C, 0x66, + 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x36, 0x6F, 0x66, 0x66, + 0x66, 0x66, 0x60, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, + 0x3B, 0x03, 0x03, 0xC6, 0x7C, 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xC0, 0x30, 0x03, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x4C, + 0x46, 0x63, 0x61, 0xF0, 0xEC, 0x62, 0x31, 0x98, 0x6C, 0x30, 0xFF, 0xFF, + 0xFF, 0xC0, 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, + 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0x66, 0x3C, 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, + 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03, 0xDF, 0x31, 0x8C, 0x63, 0x18, + 0xC6, 0x00, 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, + 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC7, 0x7B, 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, + 0x28, 0x1C, 0x0C, 0x00, 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, + 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00, 0x43, 0x62, 0x36, + 0x1C, 0x18, 0x1C, 0x3C, 0x26, 0x62, 0x43, 0xC1, 0x21, 0x98, 0xCC, 0x42, + 0x61, 0xB0, 0xD0, 0x38, 0x1C, 0x0C, 0x06, 0x03, 0x01, 0x03, 0x00, 0xFE, + 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC, 0x36, 0x66, 0x66, 0x6E, + 0xCE, 0x66, 0x66, 0x66, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC6, 0x66, + 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0, 0x61, 0x24, 0x38}; + +const GFXglyph FreeSans9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 2, 13, 6, 2, -12}, // 0x21 '!' + {4, 5, 4, 6, 1, -12}, // 0x22 '"' + {7, 10, 12, 10, 0, -11}, // 0x23 '#' + {22, 9, 16, 10, 1, -13}, // 0x24 '$' + {40, 16, 13, 16, 1, -12}, // 0x25 '%' + {66, 11, 13, 12, 1, -12}, // 0x26 '&' + {84, 2, 4, 4, 1, -12}, // 0x27 ''' + {85, 4, 17, 6, 1, -12}, // 0x28 '(' + {94, 4, 17, 6, 1, -12}, // 0x29 ')' + {103, 5, 5, 7, 1, -12}, // 0x2A '*' + {107, 6, 8, 11, 3, -7}, // 0x2B '+' + {113, 2, 4, 5, 2, 0}, // 0x2C ',' + {114, 4, 1, 6, 1, -4}, // 0x2D '-' + {115, 2, 1, 5, 1, 0}, // 0x2E '.' + {116, 5, 13, 5, 0, -12}, // 0x2F '/' + {125, 8, 13, 10, 1, -12}, // 0x30 '0' + {138, 4, 13, 10, 3, -12}, // 0x31 '1' + {145, 9, 13, 10, 1, -12}, // 0x32 '2' + {160, 8, 13, 10, 1, -12}, // 0x33 '3' + {173, 7, 13, 10, 2, -12}, // 0x34 '4' + {185, 9, 13, 10, 1, -12}, // 0x35 '5' + {200, 9, 13, 10, 1, -12}, // 0x36 '6' + {215, 8, 13, 10, 0, -12}, // 0x37 '7' + {228, 9, 13, 10, 1, -12}, // 0x38 '8' + {243, 8, 13, 10, 1, -12}, // 0x39 '9' + {256, 2, 10, 5, 1, -9}, // 0x3A ':' + {259, 3, 12, 5, 1, -8}, // 0x3B ';' + {264, 9, 9, 11, 1, -8}, // 0x3C '<' + {275, 9, 4, 11, 1, -5}, // 0x3D '=' + {280, 9, 9, 11, 1, -8}, // 0x3E '>' + {291, 9, 13, 10, 1, -12}, // 0x3F '?' + {306, 17, 16, 18, 1, -12}, // 0x40 '@' + {340, 12, 13, 12, 0, -12}, // 0x41 'A' + {360, 11, 13, 12, 1, -12}, // 0x42 'B' + {378, 11, 13, 13, 1, -12}, // 0x43 'C' + {396, 11, 13, 13, 1, -12}, // 0x44 'D' + {414, 9, 13, 11, 1, -12}, // 0x45 'E' + {429, 8, 13, 11, 1, -12}, // 0x46 'F' + {442, 12, 13, 14, 1, -12}, // 0x47 'G' + {462, 11, 13, 13, 1, -12}, // 0x48 'H' + {480, 2, 13, 5, 2, -12}, // 0x49 'I' + {484, 7, 13, 10, 1, -12}, // 0x4A 'J' + {496, 11, 13, 12, 1, -12}, // 0x4B 'K' + {514, 8, 13, 10, 1, -12}, // 0x4C 'L' + {527, 13, 13, 15, 1, -12}, // 0x4D 'M' + {549, 11, 13, 13, 1, -12}, // 0x4E 'N' + {567, 13, 13, 14, 1, -12}, // 0x4F 'O' + {589, 10, 13, 12, 1, -12}, // 0x50 'P' + {606, 13, 14, 14, 1, -12}, // 0x51 'Q' + {629, 12, 13, 13, 1, -12}, // 0x52 'R' + {649, 10, 13, 12, 1, -12}, // 0x53 'S' + {666, 9, 13, 11, 1, -12}, // 0x54 'T' + {681, 11, 13, 13, 1, -12}, // 0x55 'U' + {699, 11, 13, 12, 0, -12}, // 0x56 'V' + {717, 17, 13, 17, 0, -12}, // 0x57 'W' + {745, 12, 13, 12, 0, -12}, // 0x58 'X' + {765, 12, 13, 12, 0, -12}, // 0x59 'Y' + {785, 10, 13, 11, 1, -12}, // 0x5A 'Z' + {802, 3, 17, 5, 1, -12}, // 0x5B '[' + {809, 5, 13, 5, 0, -12}, // 0x5C '\' + {818, 3, 17, 5, 0, -12}, // 0x5D ']' + {825, 7, 7, 8, 1, -12}, // 0x5E '^' + {832, 10, 1, 10, 0, 3}, // 0x5F '_' + {834, 4, 3, 5, 0, -12}, // 0x60 '`' + {836, 9, 10, 10, 1, -9}, // 0x61 'a' + {848, 9, 13, 10, 1, -12}, // 0x62 'b' + {863, 8, 10, 9, 1, -9}, // 0x63 'c' + {873, 8, 13, 10, 1, -12}, // 0x64 'd' + {886, 8, 10, 10, 1, -9}, // 0x65 'e' + {896, 4, 13, 5, 1, -12}, // 0x66 'f' + {903, 8, 14, 10, 1, -9}, // 0x67 'g' + {917, 8, 13, 10, 1, -12}, // 0x68 'h' + {930, 2, 13, 4, 1, -12}, // 0x69 'i' + {934, 4, 17, 4, 0, -12}, // 0x6A 'j' + {943, 9, 13, 9, 1, -12}, // 0x6B 'k' + {958, 2, 13, 4, 1, -12}, // 0x6C 'l' + {962, 13, 10, 15, 1, -9}, // 0x6D 'm' + {979, 8, 10, 10, 1, -9}, // 0x6E 'n' + {989, 8, 10, 10, 1, -9}, // 0x6F 'o' + {999, 9, 13, 10, 1, -9}, // 0x70 'p' + {1014, 8, 13, 10, 1, -9}, // 0x71 'q' + {1027, 5, 10, 6, 1, -9}, // 0x72 'r' + {1034, 8, 10, 9, 1, -9}, // 0x73 's' + {1044, 4, 12, 5, 1, -11}, // 0x74 't' + {1050, 8, 10, 10, 1, -9}, // 0x75 'u' + {1060, 9, 10, 9, 0, -9}, // 0x76 'v' + {1072, 13, 10, 13, 0, -9}, // 0x77 'w' + {1089, 8, 10, 9, 0, -9}, // 0x78 'x' + {1099, 9, 14, 9, 0, -9}, // 0x79 'y' + {1115, 7, 10, 9, 1, -9}, // 0x7A 'z' + {1124, 4, 17, 6, 1, -12}, // 0x7B '{' + {1133, 2, 17, 4, 2, -12}, // 0x7C '|' + {1138, 4, 17, 6, 1, -12}, // 0x7D '}' + {1147, 7, 3, 9, 1, -7}}; // 0x7E '~' + +const GFXfont FreeSans9pt7b PROGMEM = {(uint8_t *)FreeSans9pt7bBitmaps, + (GFXglyph *)FreeSans9pt7bGlyphs, 0x20, + 0x7E, 22}; + +// Approx. 1822 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold12pt7b.h new file mode 100644 index 0000000..d75ef62 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold12pt7b.h @@ -0,0 +1,287 @@ +const uint8_t FreeSansBold12pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x60, 0xFF, 0xF0, 0xF3, 0xFC, 0xFF, + 0x3F, 0xCF, 0x61, 0x98, 0x60, 0x0E, 0x70, 0x73, 0x83, 0x18, 0xFF, 0xF7, + 0xFF, 0xBF, 0xFC, 0x73, 0x83, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFD, 0xFF, + 0xE3, 0x18, 0x39, 0xC1, 0xCE, 0x0E, 0x70, 0x02, 0x00, 0x7E, 0x0F, 0xF8, + 0x7F, 0xE7, 0xAF, 0xB9, 0x3D, 0xC8, 0x0F, 0x40, 0x3F, 0x00, 0xFF, 0x00, + 0xFC, 0x05, 0xFF, 0x27, 0xF9, 0x3F, 0xEB, 0xEF, 0xFE, 0x3F, 0xE0, 0x7C, + 0x00, 0x80, 0x04, 0x00, 0x3C, 0x06, 0x0F, 0xC1, 0x81, 0xFC, 0x30, 0x73, + 0x8C, 0x0C, 0x31, 0x81, 0xCE, 0x60, 0x1F, 0xCC, 0x03, 0xF3, 0x00, 0x3C, + 0x67, 0x80, 0x19, 0xF8, 0x02, 0x7F, 0x80, 0xCE, 0x70, 0x11, 0x86, 0x06, + 0x39, 0xC1, 0x87, 0xF8, 0x30, 0x7E, 0x0C, 0x07, 0x80, 0x07, 0x80, 0x1F, + 0xC0, 0x3F, 0xE0, 0x3C, 0xE0, 0x3C, 0xE0, 0x3E, 0xE0, 0x0F, 0xC0, 0x07, + 0x00, 0x3F, 0x8C, 0x7F, 0xCC, 0xF1, 0xFC, 0xF0, 0xF8, 0xF0, 0x78, 0xF8, + 0xF8, 0x7F, 0xFC, 0x3F, 0xDE, 0x1F, 0x8E, 0xFF, 0xFF, 0x66, 0x0C, 0x73, + 0x8E, 0x71, 0xC7, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x8E, 0x1C, 0x71, 0xC3, + 0x8E, 0x18, 0x70, 0xC3, 0x87, 0x1C, 0x38, 0xE3, 0x87, 0x1C, 0x71, 0xC7, + 0x1C, 0x71, 0xCE, 0x38, 0xE7, 0x1C, 0x63, 0x80, 0x10, 0x23, 0x5F, 0xF3, + 0x87, 0x1B, 0x14, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x0F, 0xFF, 0xFF, 0xFF, + 0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x00, 0xFF, 0xF3, 0x36, 0xC0, 0xFF, + 0xFF, 0xC0, 0xFF, 0xF0, 0x0C, 0x30, 0x86, 0x18, 0x61, 0x0C, 0x30, 0xC2, + 0x18, 0x61, 0x84, 0x30, 0xC0, 0x1F, 0x83, 0xFC, 0x7F, 0xE7, 0x9E, 0xF0, + 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, + 0xF7, 0x9E, 0x7F, 0xE3, 0xFC, 0x0F, 0x00, 0x06, 0x1C, 0x7F, 0xFF, 0xE3, + 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x1F, 0x83, + 0xFC, 0x7F, 0xEF, 0x9F, 0xF0, 0xFF, 0x0F, 0x00, 0xF0, 0x0F, 0x01, 0xE0, + 0x3C, 0x0F, 0x81, 0xE0, 0x3C, 0x03, 0x80, 0x7F, 0xF7, 0xFF, 0x7F, 0xF0, + 0x1F, 0x07, 0xFC, 0xFF, 0xEF, 0x1E, 0xF1, 0xE0, 0x1E, 0x03, 0xC0, 0x78, + 0x07, 0xC0, 0x1E, 0x00, 0xF0, 0x0F, 0xF0, 0xFF, 0x1F, 0x7F, 0xE7, 0xFC, + 0x1F, 0x80, 0x03, 0xC0, 0xF8, 0x1F, 0x07, 0xE1, 0xBC, 0x27, 0x8C, 0xF3, + 0x1E, 0x63, 0xD8, 0x7B, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0x80, 0xF0, 0x1E, + 0x03, 0xC0, 0x3F, 0xE7, 0xFE, 0x7F, 0xE7, 0x00, 0x60, 0x06, 0xF8, 0x7F, + 0xCF, 0xFE, 0xF1, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xFE, 0x1E, 0xFF, + 0xE7, 0xFC, 0x3F, 0x00, 0x0F, 0x83, 0xFC, 0x7F, 0xE7, 0x9F, 0xF0, 0x0F, + 0x78, 0xFF, 0xCF, 0xFE, 0xF9, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xF7, + 0x9F, 0x7F, 0xE3, 0xFC, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, + 0x1C, 0x07, 0x01, 0xE0, 0x38, 0x0F, 0x01, 0xC0, 0x78, 0x0F, 0x01, 0xE0, + 0x38, 0x0F, 0x01, 0xE0, 0x3C, 0x00, 0x0F, 0x03, 0xFC, 0x7F, 0xC7, 0x9E, + 0x70, 0xE7, 0x0E, 0x39, 0xC1, 0xF8, 0x3F, 0xC7, 0x9E, 0xF0, 0xFF, 0x0F, + 0xF0, 0xFF, 0x9F, 0x7F, 0xE3, 0xFC, 0x1F, 0x80, 0x1F, 0x03, 0xFC, 0x7F, + 0xEF, 0x9E, 0xF0, 0xEF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF9, 0xF7, 0xFF, 0x3F, + 0xF1, 0xEF, 0x00, 0xEF, 0x1E, 0x7F, 0xE7, 0xFC, 0x1F, 0x00, 0xFF, 0xF0, + 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0x11, 0x6C, + 0x00, 0x10, 0x07, 0x03, 0xF1, 0xFC, 0x7E, 0x0F, 0x80, 0xE0, 0x0F, 0xC0, + 0x3F, 0x80, 0x7F, 0x00, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x0E, 0x00, 0xFC, + 0x07, 0xF0, 0x0F, 0xE0, 0x1F, 0x00, 0xF0, 0x7F, 0x1F, 0x8F, 0xE0, 0xF0, + 0x08, 0x00, 0x1F, 0x07, 0xFC, 0x7F, 0xEF, 0x9F, 0xF0, 0xFF, 0x0F, 0x00, + 0xF0, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x0E, 0x00, 0xE0, 0x00, + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x00, 0xFE, 0x00, 0x1F, 0xFC, 0x03, 0xC0, + 0xF0, 0x38, 0x01, 0xC3, 0x80, 0x07, 0x18, 0x3D, 0x99, 0x87, 0xEC, 0x6C, + 0x71, 0xC3, 0xC3, 0x06, 0x1E, 0x18, 0x30, 0xF1, 0x81, 0x87, 0x8C, 0x18, + 0x7C, 0x60, 0xC3, 0x63, 0x8E, 0x3B, 0x8F, 0xDF, 0x8C, 0x3C, 0xF0, 0x70, + 0x00, 0x01, 0xC0, 0x00, 0x07, 0x80, 0x80, 0x1F, 0xFE, 0x00, 0x1F, 0xC0, + 0x00, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x07, 0xF0, 0x07, 0xF0, 0x07, + 0x70, 0x0F, 0x78, 0x0E, 0x78, 0x0E, 0x38, 0x1E, 0x3C, 0x1C, 0x3C, 0x3F, + 0xFC, 0x3F, 0xFE, 0x3F, 0xFE, 0x78, 0x0E, 0x78, 0x0F, 0x70, 0x0F, 0xF0, + 0x07, 0xFF, 0xC3, 0xFF, 0xCF, 0xFF, 0x3C, 0x3E, 0xF0, 0x7B, 0xC1, 0xEF, + 0x0F, 0xBF, 0xFC, 0xFF, 0xE3, 0xFF, 0xCF, 0x07, 0xBC, 0x0F, 0xF0, 0x3F, + 0xC0, 0xFF, 0x07, 0xFF, 0xFE, 0xFF, 0xFB, 0xFF, 0x80, 0x07, 0xE0, 0x1F, + 0xF8, 0x3F, 0xFC, 0x7C, 0x3E, 0x78, 0x1F, 0xF8, 0x0F, 0xF0, 0x00, 0xF0, + 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF8, 0x0F, 0x78, + 0x1F, 0x7C, 0x3E, 0x3F, 0xFE, 0x1F, 0xFC, 0x07, 0xF0, 0xFF, 0xE1, 0xFF, + 0xE3, 0xFF, 0xE7, 0x83, 0xEF, 0x03, 0xDE, 0x07, 0xFC, 0x07, 0xF8, 0x0F, + 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x03, 0xFE, 0x07, 0xBC, + 0x1F, 0x7F, 0xFC, 0xFF, 0xF1, 0xFF, 0x80, 0xFF, 0xF7, 0xFF, 0xBF, 0xFD, + 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1F, 0xFC, 0xFF, 0xE7, 0xFF, 0x3C, + 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x0F, 0x00, 0x03, 0xF0, 0x0F, 0xFC, 0x3F, 0xFE, 0x3E, 0x1F, + 0x78, 0x07, 0x78, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x7F, 0xF0, 0x7F, + 0xF0, 0x7F, 0xF0, 0x07, 0x78, 0x07, 0x7C, 0x0F, 0x3E, 0x1F, 0x3F, 0xFB, + 0x0F, 0xFB, 0x03, 0xE3, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, + 0x3F, 0xC0, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFC, + 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xF0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xE0, 0x3C, + 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, + 0xF8, 0xFF, 0x1F, 0xE3, 0xFC, 0x7B, 0xFE, 0x7F, 0xC3, 0xE0, 0xF0, 0x3E, + 0xF0, 0x3C, 0xF0, 0x78, 0xF0, 0xF0, 0xF1, 0xE0, 0xF3, 0xC0, 0xF7, 0x80, + 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0xFB, 0xC0, 0xF1, 0xE0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0x78, 0xF0, 0x3C, 0xF0, 0x3E, 0xF0, 0x1E, 0xF0, 0x1E, + 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, + 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0xF8, + 0x1F, 0xFE, 0x0F, 0xFF, 0x0F, 0xFF, 0x87, 0xFF, 0xC3, 0xFF, 0xE1, 0xFF, + 0xF9, 0xFF, 0xFC, 0xEF, 0xFE, 0x77, 0xFB, 0x3B, 0xFD, 0xDD, 0xFE, 0xFC, + 0xFF, 0x7E, 0x7F, 0x9F, 0x3F, 0xCF, 0x9F, 0xE7, 0x8F, 0xF3, 0xC7, 0xF8, + 0xE3, 0xC0, 0xF0, 0x1F, 0xF0, 0x3F, 0xF0, 0x7F, 0xE0, 0xFF, 0xE1, 0xFF, + 0xC3, 0xFD, 0xC7, 0xFB, 0x8F, 0xF3, 0x9F, 0xE7, 0x3F, 0xC7, 0x7F, 0x8F, + 0xFF, 0x0F, 0xFE, 0x1F, 0xFC, 0x1F, 0xF8, 0x1F, 0xF0, 0x3F, 0xE0, 0x3C, + 0x03, 0xE0, 0x0F, 0xFC, 0x0F, 0xFF, 0x87, 0xC7, 0xC7, 0x80, 0xF3, 0xC0, + 0x7B, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x01, 0xFE, + 0x00, 0xF7, 0x80, 0xF3, 0xC0, 0x78, 0xF0, 0xF8, 0x7F, 0xFC, 0x1F, 0xFC, + 0x03, 0xF8, 0x00, 0xFF, 0xE3, 0xFF, 0xEF, 0xFF, 0xBC, 0x1F, 0xF0, 0x3F, + 0xC0, 0xFF, 0x03, 0xFC, 0x1F, 0xFF, 0xFB, 0xFF, 0xCF, 0xFE, 0x3C, 0x00, + 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x00, 0x03, + 0xE0, 0x0F, 0xFC, 0x0F, 0xFF, 0x87, 0xC7, 0xC7, 0x80, 0xF3, 0xC0, 0x7B, + 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x01, 0xFE, 0x04, + 0xF7, 0x87, 0xF3, 0xC3, 0xF8, 0xF0, 0xF8, 0x7F, 0xFC, 0x1F, 0xFF, 0x83, + 0xF1, 0x80, 0x00, 0x00, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFC, 0xF0, 0x3E, + 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x3C, 0xFF, 0xF8, 0xFF, 0xF0, + 0xFF, 0xF8, 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, 0x3C, + 0xF0, 0x3C, 0xF0, 0x1F, 0x0F, 0xC0, 0x7F, 0xE1, 0xFF, 0xE7, 0xC3, 0xEF, + 0x03, 0xDE, 0x00, 0x3C, 0x00, 0x7F, 0x00, 0x7F, 0xF0, 0x3F, 0xF8, 0x0F, + 0xF8, 0x01, 0xF0, 0x01, 0xFE, 0x03, 0xDE, 0x0F, 0xBF, 0xFE, 0x3F, 0xF8, + 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, + 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, + 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xF7, 0x87, 0x9F, 0xFE, 0x3F, 0xF0, 0x3F, + 0x00, 0x70, 0x0E, 0xF0, 0x3D, 0xE0, 0x79, 0xC0, 0xE3, 0x81, 0xC7, 0x87, + 0x87, 0x0E, 0x0E, 0x1C, 0x1E, 0x78, 0x1C, 0xE0, 0x39, 0xC0, 0x73, 0x80, + 0x7E, 0x00, 0xFC, 0x01, 0xF8, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x70, + 0x38, 0x1C, 0xE0, 0xF0, 0x79, 0xE1, 0xF0, 0xF3, 0xC3, 0xE1, 0xE3, 0x87, + 0xC3, 0x87, 0x0F, 0x87, 0x0E, 0x3B, 0x9E, 0x1E, 0x77, 0x38, 0x1C, 0xEE, + 0x70, 0x39, 0xCC, 0xE0, 0x73, 0x99, 0xC0, 0x6E, 0x3F, 0x00, 0xFC, 0x7E, + 0x01, 0xF8, 0xFC, 0x03, 0xF0, 0xF8, 0x03, 0xE1, 0xE0, 0x07, 0x83, 0xC0, + 0x0F, 0x07, 0x80, 0xF0, 0x3C, 0xF0, 0xF9, 0xE1, 0xE1, 0xE7, 0x83, 0xCF, + 0x03, 0xFC, 0x03, 0xF0, 0x07, 0xE0, 0x07, 0x80, 0x0F, 0x00, 0x3F, 0x00, + 0xFF, 0x01, 0xFE, 0x07, 0x9E, 0x0F, 0x1E, 0x3C, 0x3C, 0xF8, 0x3D, 0xE0, + 0x78, 0xF0, 0x1E, 0x78, 0x1E, 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, 0x1E, + 0x78, 0x0E, 0x70, 0x0F, 0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x03, 0xC0, 0x03, + 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF8, 0x07, 0x80, 0x78, 0x07, 0x80, 0x7C, 0x03, 0xC0, 0x3C, 0x03, + 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFC, 0xF3, 0xCF, + 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFF, 0xFF, + 0xC0, 0xC1, 0x81, 0x03, 0x06, 0x04, 0x0C, 0x18, 0x10, 0x30, 0x60, 0x40, + 0xC1, 0x81, 0x03, 0x06, 0xFF, 0xFF, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, + 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0xFF, 0xFF, 0xC0, 0x0F, 0x00, + 0xF0, 0x0F, 0x01, 0xF8, 0x1B, 0x83, 0x9C, 0x39, 0xC3, 0x0C, 0x70, 0xE7, + 0x0E, 0xE0, 0x70, 0xFF, 0xFF, 0xFF, 0xFC, 0xE6, 0x30, 0x1F, 0x83, 0xFF, + 0x1F, 0xFD, 0xE1, 0xE0, 0x0F, 0x03, 0xF9, 0xFF, 0xDF, 0x1E, 0xF0, 0xF7, + 0x8F, 0xBF, 0xFC, 0xFF, 0xE3, 0xCF, 0x80, 0xF0, 0x07, 0x80, 0x3C, 0x01, + 0xE0, 0x0F, 0x00, 0x7B, 0xC3, 0xFF, 0x9F, 0xFE, 0xF8, 0xF7, 0x83, 0xFC, + 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xE3, 0xDF, 0xFE, 0xFF, 0xE7, 0xBE, + 0x00, 0x0F, 0x83, 0xFE, 0x7F, 0xF7, 0x8F, 0xF0, 0x7F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x77, 0x8F, 0x7F, 0xF3, 0xFE, 0x0F, 0x80, 0x00, 0x78, 0x03, + 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x8F, 0xBC, 0xFF, 0xEF, 0xFF, 0x78, 0xFF, + 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3D, 0xE3, 0xEF, 0xFF, 0x3F, + 0xF8, 0xFB, 0xC0, 0x1F, 0x81, 0xFE, 0x1F, 0xF9, 0xF1, 0xCF, 0x07, 0x7F, + 0xFB, 0xFF, 0xDE, 0x00, 0xF0, 0x03, 0xC3, 0x9F, 0xFC, 0x7F, 0xC0, 0xF8, + 0x00, 0x3E, 0xFD, 0xFB, 0xC7, 0x9F, 0xBF, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, + 0x8F, 0x1E, 0x3C, 0x78, 0xF0, 0x1E, 0x79, 0xFB, 0xDF, 0xFE, 0xF1, 0xFF, + 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC7, 0xDF, 0xFE, 0x7F, + 0xF1, 0xF7, 0x80, 0x3C, 0x01, 0xFF, 0x1E, 0x7F, 0xF0, 0xFE, 0x00, 0xF0, + 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x7C, 0xFF, 0xEF, 0xFF, 0xF9, + 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, + 0xFF, 0x0F, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, + 0xF3, 0xC0, 0x00, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, + 0xF3, 0xCF, 0xFF, 0xFF, 0x80, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, + 0x0F, 0x0F, 0xF1, 0xEF, 0x3C, 0xF7, 0x8F, 0xF0, 0xFF, 0x0F, 0xF8, 0xFF, + 0x8F, 0x3C, 0xF1, 0xCF, 0x1E, 0xF0, 0xEF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x8F, 0x9F, 0xFB, 0xFB, 0xFF, 0xFF, + 0xFC, 0xF8, 0xFF, 0x1E, 0x1F, 0xE3, 0xC3, 0xFC, 0x78, 0x7F, 0x8F, 0x0F, + 0xF1, 0xE1, 0xFE, 0x3C, 0x3F, 0xC7, 0x87, 0xF8, 0xF0, 0xFF, 0x1E, 0x1E, + 0xF7, 0xCF, 0xFE, 0xFF, 0xFF, 0x9F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, + 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xF0, 0x0F, 0x81, 0xFF, 0x1F, + 0xFC, 0xF1, 0xEF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7B, 0xC7, + 0x9F, 0xFC, 0x7F, 0xC0, 0xF8, 0x00, 0xF7, 0xC7, 0xFF, 0x3F, 0xFD, 0xF1, + 0xEF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC7, 0xBF, 0xFD, + 0xFF, 0xCF, 0x78, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x00, + 0x0F, 0x79, 0xFF, 0xDF, 0xFE, 0xF1, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, + 0x0F, 0xF0, 0x7B, 0xC7, 0xDF, 0xFE, 0x7F, 0xF1, 0xF7, 0x80, 0x3C, 0x01, + 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0xF3, 0xF7, 0xFF, 0xF8, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x1F, 0x87, 0xFC, 0xFF, 0xEF, + 0x0F, 0xF8, 0x0F, 0xF0, 0x7F, 0xE0, 0xFF, 0x01, 0xFF, 0x0F, 0xFF, 0xE7, + 0xFE, 0x1F, 0x80, 0x79, 0xE7, 0xBF, 0xFD, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, + 0x7D, 0xF3, 0xC0, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, + 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0xFF, 0xF7, 0xFF, 0x3E, 0xF0, 0xF0, + 0x7B, 0x83, 0x9E, 0x1C, 0xF1, 0xE3, 0x8E, 0x1C, 0x70, 0x77, 0x83, 0xB8, + 0x1D, 0xC0, 0x7E, 0x03, 0xE0, 0x1F, 0x00, 0x70, 0x00, 0xF0, 0xE1, 0xDC, + 0x78, 0x77, 0x1F, 0x3D, 0xE7, 0xCF, 0x79, 0xB3, 0x8E, 0x6C, 0xE3, 0xBB, + 0x38, 0xEE, 0xFC, 0x1F, 0x3F, 0x07, 0xC7, 0xC1, 0xF1, 0xF0, 0x7C, 0x78, + 0x0E, 0x1E, 0x00, 0x78, 0xF3, 0xC7, 0x8F, 0x78, 0x3B, 0x81, 0xFC, 0x07, + 0xC0, 0x1E, 0x01, 0xF0, 0x1F, 0xC0, 0xEF, 0x0F, 0x78, 0xF1, 0xE7, 0x87, + 0x00, 0xF0, 0x7B, 0x83, 0x9E, 0x1C, 0x71, 0xE3, 0x8E, 0x1E, 0x70, 0x73, + 0x83, 0xB8, 0x1F, 0xC0, 0x7E, 0x03, 0xE0, 0x0F, 0x00, 0x70, 0x03, 0x80, + 0x3C, 0x07, 0xC0, 0x3E, 0x01, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, + 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, + 0x1C, 0xF3, 0xCE, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0xBC, 0xF0, 0xE3, 0x8E, + 0x38, 0xE3, 0x8E, 0x3C, 0xF1, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0xE3, 0x8F, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x0F, 0x3D, 0xC7, 0x1C, + 0x71, 0xC7, 0x1C, 0xF3, 0xCE, 0x00, 0x78, 0x0F, 0xE0, 0xCF, 0x30, 0x7F, + 0x01, 0xE0}; + +const GFXglyph FreeSansBold12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 7, 0, 1}, // 0x20 ' ' + {0, 4, 17, 8, 3, -16}, // 0x21 '!' + {9, 10, 6, 11, 1, -17}, // 0x22 '"' + {17, 13, 16, 13, 0, -15}, // 0x23 '#' + {43, 13, 20, 13, 0, -17}, // 0x24 '$' + {76, 19, 17, 21, 1, -16}, // 0x25 '%' + {117, 16, 17, 17, 1, -16}, // 0x26 '&' + {151, 4, 6, 6, 1, -17}, // 0x27 ''' + {154, 6, 22, 8, 1, -17}, // 0x28 '(' + {171, 6, 22, 8, 1, -17}, // 0x29 ')' + {188, 7, 8, 9, 1, -17}, // 0x2A '*' + {195, 11, 11, 14, 2, -10}, // 0x2B '+' + {211, 4, 7, 6, 1, -2}, // 0x2C ',' + {215, 6, 3, 8, 1, -7}, // 0x2D '-' + {218, 4, 3, 6, 1, -2}, // 0x2E '.' + {220, 6, 17, 7, 0, -16}, // 0x2F '/' + {233, 12, 17, 13, 1, -16}, // 0x30 '0' + {259, 7, 17, 14, 3, -16}, // 0x31 '1' + {274, 12, 17, 13, 1, -16}, // 0x32 '2' + {300, 12, 17, 13, 1, -16}, // 0x33 '3' + {326, 11, 17, 13, 1, -16}, // 0x34 '4' + {350, 12, 17, 13, 1, -16}, // 0x35 '5' + {376, 12, 17, 13, 1, -16}, // 0x36 '6' + {402, 11, 17, 13, 1, -16}, // 0x37 '7' + {426, 12, 17, 13, 1, -16}, // 0x38 '8' + {452, 12, 17, 13, 1, -16}, // 0x39 '9' + {478, 4, 12, 6, 1, -11}, // 0x3A ':' + {484, 4, 16, 6, 1, -11}, // 0x3B ';' + {492, 12, 12, 14, 1, -11}, // 0x3C '<' + {510, 12, 9, 14, 1, -9}, // 0x3D '=' + {524, 12, 12, 14, 1, -11}, // 0x3E '>' + {542, 12, 18, 15, 2, -17}, // 0x3F '?' + {569, 21, 21, 23, 1, -17}, // 0x40 '@' + {625, 16, 18, 17, 0, -17}, // 0x41 'A' + {661, 14, 18, 17, 2, -17}, // 0x42 'B' + {693, 16, 18, 17, 1, -17}, // 0x43 'C' + {729, 15, 18, 17, 2, -17}, // 0x44 'D' + {763, 13, 18, 16, 2, -17}, // 0x45 'E' + {793, 12, 18, 15, 2, -17}, // 0x46 'F' + {820, 16, 18, 18, 1, -17}, // 0x47 'G' + {856, 14, 18, 18, 2, -17}, // 0x48 'H' + {888, 4, 18, 7, 2, -17}, // 0x49 'I' + {897, 11, 18, 14, 1, -17}, // 0x4A 'J' + {922, 16, 18, 17, 2, -17}, // 0x4B 'K' + {958, 11, 18, 15, 2, -17}, // 0x4C 'L' + {983, 17, 18, 21, 2, -17}, // 0x4D 'M' + {1022, 15, 18, 18, 2, -17}, // 0x4E 'N' + {1056, 17, 18, 19, 1, -17}, // 0x4F 'O' + {1095, 14, 18, 16, 2, -17}, // 0x50 'P' + {1127, 17, 19, 19, 1, -17}, // 0x51 'Q' + {1168, 16, 18, 17, 2, -17}, // 0x52 'R' + {1204, 15, 18, 16, 1, -17}, // 0x53 'S' + {1238, 12, 18, 15, 2, -17}, // 0x54 'T' + {1265, 14, 18, 18, 2, -17}, // 0x55 'U' + {1297, 15, 18, 16, 0, -17}, // 0x56 'V' + {1331, 23, 18, 23, 0, -17}, // 0x57 'W' + {1383, 15, 18, 16, 1, -17}, // 0x58 'X' + {1417, 16, 18, 15, 0, -17}, // 0x59 'Y' + {1453, 13, 18, 15, 1, -17}, // 0x5A 'Z' + {1483, 6, 23, 8, 2, -17}, // 0x5B '[' + {1501, 7, 17, 7, 0, -16}, // 0x5C '\' + {1516, 6, 23, 8, 0, -17}, // 0x5D ']' + {1534, 12, 11, 14, 1, -16}, // 0x5E '^' + {1551, 15, 2, 13, -1, 4}, // 0x5F '_' + {1555, 4, 3, 6, 0, -17}, // 0x60 '`' + {1557, 13, 13, 14, 1, -12}, // 0x61 'a' + {1579, 13, 18, 15, 2, -17}, // 0x62 'b' + {1609, 12, 13, 13, 1, -12}, // 0x63 'c' + {1629, 13, 18, 15, 1, -17}, // 0x64 'd' + {1659, 13, 13, 14, 1, -12}, // 0x65 'e' + {1681, 7, 18, 8, 1, -17}, // 0x66 'f' + {1697, 13, 18, 15, 1, -12}, // 0x67 'g' + {1727, 12, 18, 14, 2, -17}, // 0x68 'h' + {1754, 4, 18, 7, 2, -17}, // 0x69 'i' + {1763, 6, 23, 7, 0, -17}, // 0x6A 'j' + {1781, 12, 18, 14, 2, -17}, // 0x6B 'k' + {1808, 4, 18, 6, 2, -17}, // 0x6C 'l' + {1817, 19, 13, 21, 2, -12}, // 0x6D 'm' + {1848, 12, 13, 15, 2, -12}, // 0x6E 'n' + {1868, 13, 13, 15, 1, -12}, // 0x6F 'o' + {1890, 13, 18, 15, 2, -12}, // 0x70 'p' + {1920, 13, 18, 15, 1, -12}, // 0x71 'q' + {1950, 8, 13, 9, 2, -12}, // 0x72 'r' + {1963, 12, 13, 13, 1, -12}, // 0x73 's' + {1983, 6, 15, 8, 1, -14}, // 0x74 't' + {1995, 12, 13, 15, 2, -12}, // 0x75 'u' + {2015, 13, 13, 13, 0, -12}, // 0x76 'v' + {2037, 18, 13, 19, 0, -12}, // 0x77 'w' + {2067, 13, 13, 13, 0, -12}, // 0x78 'x' + {2089, 13, 18, 13, 0, -12}, // 0x79 'y' + {2119, 10, 13, 12, 1, -12}, // 0x7A 'z' + {2136, 6, 23, 9, 1, -17}, // 0x7B '{' + {2154, 2, 22, 7, 2, -17}, // 0x7C '|' + {2160, 6, 23, 9, 3, -17}, // 0x7D '}' + {2178, 12, 5, 12, 0, -7}}; // 0x7E '~' + +const GFXfont FreeSansBold12pt7b PROGMEM = { + (uint8_t *)FreeSansBold12pt7bBitmaps, (GFXglyph *)FreeSansBold12pt7bGlyphs, + 0x20, 0x7E, 29}; + +// Approx. 2858 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold18pt7b.h new file mode 100644 index 0000000..ed34c63 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold18pt7b.h @@ -0,0 +1,480 @@ +const uint8_t FreeSansBold18pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xE7, 0x39, 0xCE, 0x73, 0x80, + 0x0F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xC7, 0xFE, 0x3F, 0xF1, 0xFF, 0x8F, + 0xFC, 0x7D, 0xC1, 0xCE, 0x0E, 0x70, 0x70, 0x03, 0xC3, 0x80, 0x3C, 0x78, + 0x03, 0xC7, 0x80, 0x38, 0x78, 0x07, 0x87, 0x07, 0xFF, 0xFF, 0x7F, 0xFF, + 0xF7, 0xFF, 0xFF, 0x7F, 0xFF, 0xF0, 0xF0, 0xE0, 0x0F, 0x0E, 0x00, 0xF1, + 0xE0, 0x0F, 0x1E, 0x00, 0xE1, 0xE0, 0xFF, 0xFF, 0xCF, 0xFF, 0xFC, 0xFF, + 0xFF, 0xCF, 0xFF, 0xFC, 0x1C, 0x3C, 0x03, 0xC3, 0x80, 0x3C, 0x78, 0x03, + 0xC7, 0x80, 0x38, 0x78, 0x03, 0x87, 0x80, 0x00, 0x60, 0x00, 0x7F, 0x80, + 0x3F, 0xFC, 0x0F, 0xFF, 0xC3, 0xFF, 0xFC, 0xFC, 0xDF, 0x9F, 0x19, 0xFB, + 0xC3, 0x1F, 0x78, 0x63, 0xEF, 0x8C, 0x01, 0xFD, 0x80, 0x1F, 0xF0, 0x01, + 0xFF, 0xC0, 0x1F, 0xFE, 0x00, 0x7F, 0xE0, 0x03, 0xFE, 0x00, 0x67, 0xE0, + 0x0C, 0x7F, 0xE1, 0x8F, 0xFC, 0x31, 0xFF, 0xC6, 0x3E, 0xFC, 0xDF, 0x9F, + 0xFF, 0xF1, 0xFF, 0xFC, 0x0F, 0xFF, 0x00, 0x7F, 0x80, 0x01, 0x80, 0x00, + 0x30, 0x00, 0x06, 0x00, 0x0F, 0x00, 0x1C, 0x01, 0xFE, 0x00, 0xE0, 0x1F, + 0xF8, 0x0E, 0x00, 0xFF, 0xC0, 0x70, 0x0F, 0x0F, 0x07, 0x00, 0x70, 0x38, + 0x38, 0x03, 0x81, 0xC3, 0x80, 0x1C, 0x0E, 0x3C, 0x00, 0xF0, 0xF1, 0xC0, + 0x03, 0xFF, 0x1C, 0x00, 0x1F, 0xF8, 0xE0, 0x00, 0x7F, 0x8E, 0x00, 0x00, + 0xF0, 0x70, 0xF8, 0x00, 0x07, 0x1F, 0xF0, 0x00, 0x39, 0xFF, 0xC0, 0x03, + 0x8F, 0xFE, 0x00, 0x1C, 0xF0, 0x78, 0x01, 0xC7, 0x01, 0xC0, 0x0C, 0x38, + 0x0E, 0x00, 0xE1, 0xC0, 0x70, 0x06, 0x0F, 0x07, 0x80, 0x70, 0x3F, 0xF8, + 0x07, 0x01, 0xFF, 0xC0, 0x38, 0x07, 0xFC, 0x03, 0x80, 0x0F, 0x80, 0x01, + 0xF0, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0xC0, 0x03, 0xFF, 0x80, 0x1F, 0x1E, + 0x00, 0x7C, 0x78, 0x01, 0xF1, 0xE0, 0x07, 0xE7, 0x80, 0x0F, 0xBC, 0x00, + 0x1F, 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0xF0, 0xF0, 0xFF, + 0xE3, 0xC7, 0xE7, 0xCF, 0x3F, 0x0F, 0xF8, 0xF8, 0x3F, 0xE3, 0xE0, 0x7F, + 0x8F, 0x80, 0xFC, 0x3F, 0x03, 0xF0, 0x7E, 0x3F, 0xE1, 0xFF, 0xFF, 0x83, + 0xFF, 0xFF, 0x07, 0xFE, 0x7E, 0x07, 0xF0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFD, + 0xCE, 0x70, 0x07, 0x87, 0x83, 0xC3, 0xC1, 0xE1, 0xE0, 0xF0, 0x78, 0x78, + 0x3C, 0x1E, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, + 0x0F, 0x03, 0x81, 0xE0, 0xF0, 0x78, 0x1E, 0x0F, 0x03, 0x81, 0xE0, 0x70, + 0x3C, 0x0E, 0x07, 0x80, 0xF0, 0x38, 0x1E, 0x07, 0x83, 0xC0, 0xF0, 0x78, + 0x3C, 0x0F, 0x07, 0x83, 0xC0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, + 0xC1, 0xE0, 0xF0, 0x78, 0x78, 0x3C, 0x1E, 0x0F, 0x0F, 0x07, 0x87, 0x83, + 0xC1, 0xC1, 0xE0, 0xE0, 0xF0, 0x00, 0x06, 0x00, 0x60, 0x06, 0x07, 0x6E, + 0x7F, 0xE3, 0xFC, 0x0F, 0x01, 0xF8, 0x1F, 0x83, 0x9C, 0x10, 0x80, 0x03, + 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, 0x03, + 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0x8C, 0x63, + 0x37, 0xB0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x80, 0x01, + 0x81, 0xC0, 0xC0, 0x60, 0x70, 0x38, 0x18, 0x0C, 0x0E, 0x06, 0x03, 0x01, + 0x81, 0xC0, 0xC0, 0x60, 0x30, 0x38, 0x18, 0x0C, 0x0E, 0x07, 0x03, 0x01, + 0x81, 0xC0, 0xC0, 0x00, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x87, 0xFF, + 0xC7, 0xE3, 0xF3, 0xE0, 0xF9, 0xF0, 0x7D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, + 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, + 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xDF, 0x07, 0xCF, 0x83, 0xE7, 0xE3, + 0xF1, 0xFF, 0xF0, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x01, 0xC0, + 0xF0, 0x3C, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC1, 0xF0, 0x7C, + 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, + 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC0, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, + 0x8F, 0xFF, 0xE7, 0xE3, 0xF7, 0xE0, 0xFF, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, + 0x0F, 0x80, 0x07, 0xC0, 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xF0, 0x03, 0xF0, + 0x07, 0xF0, 0x07, 0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0x07, 0xC0, + 0x03, 0xE0, 0x03, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0x7F, 0xFF, 0x80, + 0x07, 0xE0, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, 0xFF, 0xCF, 0xC3, 0xF7, 0xC0, + 0xFB, 0xE0, 0x7D, 0xF0, 0x3E, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x0F, 0x80, + 0x3F, 0x80, 0x1F, 0xC0, 0x0F, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, + 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, 0xF7, 0xC3, 0xF3, 0xFF, 0xF8, 0xFF, + 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x01, 0xFC, + 0x01, 0xFC, 0x03, 0xFC, 0x07, 0x7C, 0x07, 0x7C, 0x0E, 0x7C, 0x0E, 0x7C, + 0x1C, 0x7C, 0x18, 0x7C, 0x38, 0x7C, 0x70, 0x7C, 0x60, 0x7C, 0xE0, 0x7C, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7C, 0x00, 0x7C, + 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x1F, 0xFF, 0x0F, 0xFF, + 0x8F, 0xFF, 0xC7, 0xFF, 0xE3, 0xC0, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, + 0x00, 0x79, 0xF0, 0x3F, 0xFE, 0x1F, 0xFF, 0x8F, 0xFF, 0xE7, 0xC3, 0xF0, + 0x00, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xFE, 0x03, + 0xFF, 0x03, 0xFF, 0xC3, 0xF3, 0xFF, 0xF1, 0xFF, 0xF8, 0x3F, 0xF0, 0x07, + 0xE0, 0x00, 0x03, 0xF8, 0x03, 0xFF, 0x81, 0xFF, 0xF0, 0xFF, 0xFE, 0x3E, + 0x1F, 0x9F, 0x03, 0xE7, 0xC0, 0x03, 0xE0, 0x00, 0xF8, 0xF8, 0x3E, 0xFF, + 0x8F, 0xFF, 0xF3, 0xFF, 0xFE, 0xFE, 0x1F, 0xBF, 0x03, 0xFF, 0x80, 0x7F, + 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xF7, 0x80, 0x7D, 0xF0, 0x3E, 0x7E, + 0x1F, 0x8F, 0xFF, 0xC1, 0xFF, 0xF0, 0x3F, 0xF0, 0x03, 0xF0, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xF8, + 0x00, 0xF8, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x1E, + 0x00, 0x1F, 0x00, 0x0F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xC0, 0x03, + 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xE0, 0x07, 0xFC, 0x0F, 0xFF, 0x07, + 0xFF, 0xC7, 0xC3, 0xF3, 0xC0, 0xF9, 0xE0, 0x3C, 0xF0, 0x1E, 0x78, 0x1F, + 0x1E, 0x1F, 0x07, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xF1, 0xF9, 0xF0, + 0x7D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, 0xF7, + 0xC3, 0xF3, 0xFF, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x07, + 0xE0, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, 0xFF, 0xC7, 0xE3, 0xF7, 0xE0, 0xFB, + 0xE0, 0x3D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, + 0xF7, 0xE3, 0xFB, 0xFF, 0xFC, 0xFF, 0xFE, 0x3F, 0xDF, 0x07, 0xCF, 0x80, + 0x07, 0x80, 0x03, 0xDF, 0x03, 0xE7, 0xC3, 0xE3, 0xFF, 0xF0, 0xFF, 0xF0, + 0x3F, 0xF0, 0x07, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xFF, 0xC6, 0x33, 0x9B, 0xD8, 0x00, 0x00, 0xC0, 0x00, + 0xF0, 0x01, 0xFC, 0x03, 0xFF, 0x03, 0xFF, 0x07, 0xFE, 0x0F, 0xFC, 0x03, + 0xF8, 0x00, 0xF0, 0x00, 0x3F, 0x80, 0x0F, 0xFC, 0x00, 0x7F, 0xE0, 0x07, + 0xFF, 0x00, 0x3F, 0xF0, 0x01, 0xFC, 0x00, 0x1F, 0x00, 0x00, 0xC0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xC0, 0x00, 0x3C, 0x00, 0x0F, 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0xF0, + 0x01, 0xFF, 0x80, 0x0F, 0xFC, 0x00, 0x7F, 0x00, 0x03, 0xC0, 0x07, 0xF0, + 0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF8, 0x3F, 0xF0, 0x0F, 0xE0, 0x03, 0xC0, + 0x00, 0xC0, 0x00, 0x00, 0x07, 0xF0, 0x07, 0xFF, 0x03, 0xFF, 0xF1, 0xFF, + 0xFC, 0x7E, 0x3F, 0xBF, 0x03, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, + 0xC0, 0x03, 0xF0, 0x01, 0xFC, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x3F, 0x80, + 0x1F, 0xC0, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, + 0x00, 0xF8, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xC0, 0x01, 0xF8, 0x07, 0xF0, 0x03, 0xE0, 0x01, 0xF8, 0x07, + 0x80, 0x00, 0x7C, 0x0F, 0x00, 0x00, 0x3C, 0x1E, 0x03, 0xE3, 0x9E, 0x3C, + 0x0F, 0xF7, 0x8E, 0x38, 0x1F, 0xFF, 0x0E, 0x78, 0x3E, 0x1F, 0x07, 0x70, + 0x38, 0x0F, 0x07, 0x70, 0x78, 0x0F, 0x07, 0xE0, 0x70, 0x0E, 0x07, 0xE0, + 0x70, 0x0E, 0x07, 0xE0, 0xE0, 0x0E, 0x07, 0xE0, 0xE0, 0x1E, 0x0F, 0xE0, + 0xE0, 0x1C, 0x0E, 0xE0, 0xE0, 0x3C, 0x1E, 0xE0, 0xF0, 0x3C, 0x3C, 0xF0, + 0xF0, 0xFC, 0x7C, 0x70, 0x7F, 0xFF, 0xF8, 0x78, 0x3F, 0xCF, 0xF0, 0x3C, + 0x1F, 0x07, 0xC0, 0x3E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0F, + 0xC0, 0x01, 0x00, 0x07, 0xF0, 0x0F, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x80, 0x01, 0xFF, + 0x80, 0x01, 0xFF, 0x80, 0x01, 0xF7, 0xC0, 0x03, 0xE7, 0xC0, 0x03, 0xE7, + 0xC0, 0x03, 0xE3, 0xE0, 0x07, 0xC3, 0xE0, 0x07, 0xC3, 0xE0, 0x07, 0xC1, + 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0xFF, 0xF8, 0x1F, 0xFF, + 0xF8, 0x1F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x3E, 0x00, 0x7C, 0x3E, 0x00, + 0x7E, 0x3E, 0x00, 0x3E, 0x7C, 0x00, 0x3E, 0x7C, 0x00, 0x3F, 0x7C, 0x00, + 0x1F, 0xFF, 0xFC, 0x0F, 0xFF, 0xF0, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0xF8, + 0x07, 0xEF, 0x80, 0x3E, 0xF8, 0x03, 0xEF, 0x80, 0x3E, 0xF8, 0x03, 0xEF, + 0x80, 0x3E, 0xF8, 0x07, 0xCF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xFF, 0xF8, + 0xFF, 0xFF, 0xCF, 0x80, 0x7E, 0xF8, 0x01, 0xEF, 0x80, 0x1F, 0xF8, 0x01, + 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x3E, 0xFF, 0xFF, 0xEF, 0xFF, + 0xFC, 0xFF, 0xFF, 0x8F, 0xFF, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xFF, 0x80, + 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xC3, 0xF8, 0x1F, 0x87, 0xE0, 0x1F, 0x9F, + 0x80, 0x1F, 0x3E, 0x00, 0x1F, 0x7C, 0x00, 0x3F, 0xF0, 0x00, 0x03, 0xE0, + 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x7D, 0xF0, 0x00, + 0xFB, 0xF0, 0x03, 0xF3, 0xF0, 0x0F, 0xC7, 0xF0, 0x3F, 0x87, 0xFF, 0xFE, + 0x07, 0xFF, 0xF8, 0x03, 0xFF, 0xC0, 0x01, 0xFE, 0x00, 0xFF, 0xFC, 0x07, + 0xFF, 0xF8, 0x3F, 0xFF, 0xE1, 0xFF, 0xFF, 0x8F, 0x80, 0xFE, 0x7C, 0x01, + 0xF3, 0xE0, 0x07, 0xDF, 0x00, 0x3E, 0xF8, 0x01, 0xF7, 0xC0, 0x07, 0xFE, + 0x00, 0x3F, 0xF0, 0x01, 0xFF, 0x80, 0x0F, 0xFC, 0x00, 0x7F, 0xE0, 0x03, + 0xFF, 0x00, 0x1F, 0xF8, 0x00, 0xFF, 0xC0, 0x0F, 0xFE, 0x00, 0x7D, 0xF0, + 0x03, 0xEF, 0x80, 0x3E, 0x7C, 0x07, 0xF3, 0xFF, 0xFF, 0x1F, 0xFF, 0xF0, + 0xFF, 0xFF, 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xDF, 0xFF, 0xFB, 0xFF, + 0xFF, 0x7F, 0xFF, 0xEF, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7F, 0xFF, 0xCF, 0xFF, + 0xF9, 0xFF, 0xFF, 0x3F, 0xFF, 0xE7, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, + 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, + 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0xFF, 0xEF, 0xFF, 0xF7, + 0xFF, 0xFB, 0xFF, 0xFD, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, + 0x00, 0x7C, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x03, 0xFF, 0xE0, 0x07, 0xFF, + 0xF8, 0x0F, 0xFF, 0xFC, 0x1F, 0xC0, 0xFE, 0x3F, 0x00, 0x7E, 0x7E, 0x00, + 0x3F, 0x7C, 0x00, 0x1F, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x03, 0xFF, 0xF8, 0x03, 0xFF, 0xF8, 0x03, + 0xFF, 0xF8, 0x03, 0xFF, 0xFC, 0x00, 0x0F, 0x7C, 0x00, 0x1F, 0x7C, 0x00, + 0x1F, 0x7E, 0x00, 0x3F, 0x3F, 0x00, 0x7F, 0x1F, 0xC1, 0xFF, 0x0F, 0xFF, + 0xFF, 0x07, 0xFF, 0xE7, 0x03, 0xFF, 0xC7, 0x00, 0xFF, 0x07, 0xF8, 0x01, + 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, + 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, + 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, + 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, + 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0x00, 0x1F, + 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, + 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, + 0x00, 0x1F, 0x00, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, + 0xF8, 0x1F, 0xFC, 0x3F, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, 0x07, 0xE0, + 0xF8, 0x01, 0xFB, 0xE0, 0x0F, 0xCF, 0x80, 0x7E, 0x3E, 0x03, 0xF0, 0xF8, + 0x1F, 0x83, 0xE0, 0xFC, 0x0F, 0x87, 0xE0, 0x3E, 0x3F, 0x00, 0xF8, 0xF8, + 0x03, 0xE7, 0xE0, 0x0F, 0xBF, 0x00, 0x3F, 0xF8, 0x00, 0xFF, 0xF0, 0x03, + 0xFF, 0xE0, 0x0F, 0xFF, 0x80, 0x3F, 0xBF, 0x00, 0xFC, 0x7E, 0x03, 0xE0, + 0xFC, 0x0F, 0x81, 0xF8, 0x3E, 0x07, 0xE0, 0xF8, 0x0F, 0xC3, 0xE0, 0x1F, + 0x8F, 0x80, 0x7F, 0x3E, 0x00, 0xFC, 0xF8, 0x01, 0xFB, 0xE0, 0x03, 0xF0, + 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, + 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, + 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, + 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0x00, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFB, 0xC3, 0xDF, 0xFB, + 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xF9, 0xC7, 0xDF, 0xF9, + 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF8, + 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, + 0x7F, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, + 0x3E, 0x1F, 0xF8, 0x01, 0xFF, 0xC0, 0x1F, 0xFE, 0x01, 0xFF, 0xE0, 0x1F, + 0xFF, 0x01, 0xFF, 0xF0, 0x1F, 0xFF, 0x81, 0xFF, 0xF8, 0x1F, 0xFF, 0xC1, + 0xFF, 0xBC, 0x1F, 0xFB, 0xE1, 0xFF, 0x9F, 0x1F, 0xF9, 0xF1, 0xFF, 0x8F, + 0x9F, 0xF8, 0x79, 0xFF, 0x87, 0xDF, 0xF8, 0x3D, 0xFF, 0x83, 0xFF, 0xF8, + 0x1F, 0xFF, 0x81, 0xFF, 0xF8, 0x0F, 0xFF, 0x80, 0xFF, 0xF8, 0x07, 0xFF, + 0x80, 0x3F, 0xF8, 0x03, 0xFF, 0x80, 0x1F, 0x00, 0x7F, 0x00, 0x01, 0xFF, + 0xF0, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x01, 0xFC, 0x1F, 0xC1, 0xF8, + 0x03, 0xF1, 0xF8, 0x00, 0xFC, 0xF8, 0x00, 0x3E, 0x7C, 0x00, 0x1F, 0x7C, + 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, 0xFF, + 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x1F, 0xF8, 0x00, 0x0F, + 0xBE, 0x00, 0x0F, 0x9F, 0x00, 0x07, 0xCF, 0xC0, 0x07, 0xE3, 0xF0, 0x07, + 0xE0, 0xFE, 0x0F, 0xE0, 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xE0, 0x03, 0xFF, + 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFF, 0xFC, 0x1F, 0xFF, 0xE3, 0xFF, 0xFE, + 0x7F, 0xFF, 0xEF, 0x80, 0xFF, 0xF0, 0x0F, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, + 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x1F, 0xFC, 0x07, 0xEF, 0xFF, 0xFD, + 0xFF, 0xFF, 0x3F, 0xFF, 0xC7, 0xFF, 0xE0, 0xF8, 0x00, 0x1F, 0x00, 0x03, + 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, + 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x01, 0xFF, + 0xF0, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x01, 0xFC, 0x1F, 0xC1, 0xF8, + 0x03, 0xF1, 0xF8, 0x00, 0xFC, 0xF8, 0x00, 0x3E, 0x7C, 0x00, 0x1F, 0x7C, + 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, 0xFF, + 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x1F, 0xF8, 0x01, 0x0F, + 0xBE, 0x01, 0xCF, 0x9F, 0x01, 0xFF, 0xCF, 0xC0, 0x7F, 0xE3, 0xF0, 0x1F, + 0xE0, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, 0xF8, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, + 0xEF, 0x80, 0x3F, 0xC3, 0x80, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x07, 0xFF, + 0xFE, 0x3F, 0xFF, 0xF9, 0xFF, 0xFF, 0xCF, 0x80, 0x3F, 0x7C, 0x00, 0xFB, + 0xE0, 0x07, 0xDF, 0x00, 0x3E, 0xF8, 0x01, 0xF7, 0xC0, 0x0F, 0x3E, 0x00, + 0xF9, 0xFF, 0xFF, 0x8F, 0xFF, 0xF8, 0x7F, 0xFF, 0xC3, 0xFF, 0xFF, 0x1F, + 0x00, 0xFC, 0xF8, 0x03, 0xE7, 0xC0, 0x1F, 0x3E, 0x00, 0xF9, 0xF0, 0x07, + 0xCF, 0x80, 0x3E, 0x7C, 0x01, 0xF3, 0xE0, 0x0F, 0x9F, 0x00, 0x7C, 0xF8, + 0x03, 0xF7, 0xC0, 0x0F, 0xC0, 0x07, 0xF8, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, + 0x87, 0xFF, 0xFC, 0x7E, 0x0F, 0xCF, 0xC0, 0x7E, 0xF8, 0x03, 0xEF, 0x80, + 0x3E, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0x00, 0x07, 0xFF, 0xC0, 0x3F, + 0xFF, 0x81, 0xFF, 0xFC, 0x03, 0xFF, 0xE0, 0x01, 0xFF, 0x00, 0x03, 0xF0, + 0x00, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xFC, 0x03, 0xFF, 0xE0, 0x7E, + 0x7F, 0xFF, 0xE3, 0xFF, 0xFC, 0x1F, 0xFF, 0x00, 0x3F, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0x00, 0x03, 0xE0, + 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, + 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, + 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, + 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, + 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, + 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, + 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, + 0x80, 0x1F, 0x7C, 0x03, 0xE7, 0xE0, 0x7E, 0x3F, 0xFF, 0xC3, 0xFF, 0xFC, + 0x0F, 0xFF, 0x00, 0x3F, 0xC0, 0xF8, 0x00, 0xFB, 0xE0, 0x03, 0xE7, 0xC0, + 0x1F, 0x9F, 0x00, 0x7C, 0x7C, 0x01, 0xF0, 0xF8, 0x07, 0xC3, 0xE0, 0x3E, + 0x0F, 0x80, 0xF8, 0x1E, 0x03, 0xE0, 0x7C, 0x1F, 0x01, 0xF0, 0x7C, 0x03, + 0xC1, 0xF0, 0x0F, 0x87, 0x80, 0x3E, 0x3E, 0x00, 0xF8, 0xF8, 0x01, 0xE3, + 0xC0, 0x07, 0xCF, 0x00, 0x1F, 0x7C, 0x00, 0x3D, 0xE0, 0x00, 0xFF, 0x80, + 0x03, 0xFE, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x00, 0x00, + 0xF8, 0x00, 0x03, 0xE0, 0x00, 0xF8, 0x07, 0xC0, 0x3F, 0xF8, 0x07, 0xE0, + 0x3E, 0xFC, 0x07, 0xE0, 0x3E, 0x7C, 0x0F, 0xE0, 0x3E, 0x7C, 0x0F, 0xE0, + 0x7E, 0x7C, 0x0F, 0xE0, 0x7C, 0x7C, 0x0F, 0xF0, 0x7C, 0x3E, 0x0F, 0xF0, + 0x7C, 0x3E, 0x1E, 0xF0, 0x78, 0x3E, 0x1E, 0x70, 0xF8, 0x1E, 0x1E, 0x70, + 0xF8, 0x1E, 0x1E, 0x78, 0xF8, 0x1F, 0x1E, 0x78, 0xF0, 0x1F, 0x3C, 0x78, + 0xF0, 0x0F, 0x3C, 0x39, 0xF0, 0x0F, 0x3C, 0x3D, 0xF0, 0x0F, 0x3C, 0x3D, + 0xE0, 0x0F, 0xBC, 0x3D, 0xE0, 0x07, 0xF8, 0x3D, 0xE0, 0x07, 0xF8, 0x1F, + 0xE0, 0x07, 0xF8, 0x1F, 0xC0, 0x03, 0xF8, 0x1F, 0xC0, 0x03, 0xF8, 0x1F, + 0xC0, 0x03, 0xF0, 0x0F, 0x80, 0x03, 0xF0, 0x0F, 0x80, 0x01, 0xF0, 0x0F, + 0x80, 0xFE, 0x01, 0xF9, 0xF8, 0x07, 0xE3, 0xF0, 0x3F, 0x0F, 0xC0, 0xF8, + 0x1F, 0x87, 0xE0, 0x7E, 0x3F, 0x00, 0xFC, 0xFC, 0x01, 0xF7, 0xE0, 0x07, + 0xFF, 0x00, 0x0F, 0xFC, 0x00, 0x3F, 0xE0, 0x00, 0x7F, 0x00, 0x00, 0xFC, + 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0x80, 0x03, 0xFF, 0x00, + 0x1F, 0x7E, 0x00, 0xFC, 0xF8, 0x03, 0xE3, 0xF0, 0x1F, 0x87, 0xC0, 0x7C, + 0x1F, 0x83, 0xF0, 0x3F, 0x1F, 0x80, 0xFC, 0x7E, 0x01, 0xFB, 0xF0, 0x07, + 0xF0, 0xFC, 0x01, 0xFF, 0xE0, 0x0F, 0x9F, 0x00, 0xFC, 0xFC, 0x07, 0xC3, + 0xE0, 0x7E, 0x1F, 0x83, 0xE0, 0x7C, 0x1F, 0x03, 0xF1, 0xF0, 0x0F, 0x8F, + 0x80, 0x7E, 0xF8, 0x01, 0xF7, 0xC0, 0x0F, 0xFC, 0x00, 0x3F, 0xE0, 0x00, + 0xFE, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, + 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x03, + 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7E, 0x00, 0x1F, + 0x80, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF8, + 0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x01, 0xFC, 0x00, 0x3F, 0x00, + 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x01, + 0xFC, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x38, 0x06, + 0x01, 0x80, 0x70, 0x0C, 0x03, 0x00, 0xE0, 0x18, 0x06, 0x01, 0xC0, 0x30, + 0x0C, 0x03, 0x00, 0xE0, 0x18, 0x06, 0x01, 0xC0, 0x30, 0x0C, 0x03, 0x80, + 0x60, 0x18, 0x07, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x03, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x0F, 0xF0, 0x0F, 0xF0, + 0x0F, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x1E, + 0x78, 0x1E, 0x78, 0x1E, 0x70, 0x0F, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0x07, 0xF8, 0x07, + 0xFF, 0x83, 0xFF, 0xF1, 0xFF, 0xFE, 0x7C, 0x1F, 0xBE, 0x03, 0xE0, 0x00, + 0xF8, 0x01, 0xFE, 0x0F, 0xFF, 0x8F, 0xFF, 0xE7, 0xF8, 0xFB, 0xF0, 0x3E, + 0xF8, 0x0F, 0xBE, 0x07, 0xEF, 0xC3, 0xFB, 0xFF, 0xFE, 0x7F, 0xFF, 0x8F, + 0xFB, 0xF1, 0xF8, 0xFC, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, + 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE7, 0xE0, 0xFB, 0xFC, + 0x3F, 0xFF, 0xCF, 0xFF, 0xF3, 0xF8, 0x7E, 0xFC, 0x0F, 0xBF, 0x03, 0xFF, + 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xF0, + 0x3F, 0xFC, 0x0F, 0xBF, 0x87, 0xEF, 0xFF, 0xF3, 0xFF, 0xFC, 0xFB, 0xFC, + 0x3E, 0x7E, 0x00, 0x03, 0xF0, 0x07, 0xFE, 0x0F, 0xFF, 0x87, 0xFF, 0xE7, + 0xE1, 0xFB, 0xE0, 0x7F, 0xE0, 0x3F, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, + 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0xFB, 0xE0, 0x7D, 0xF8, 0x7E, 0x7F, + 0xFE, 0x3F, 0xFE, 0x0F, 0xFE, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, + 0xF8, 0x1F, 0x1F, 0x0F, 0xFB, 0xE3, 0xFF, 0xFC, 0xFF, 0xFF, 0xBF, 0x8F, + 0xF7, 0xC0, 0x7F, 0xF8, 0x0F, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, + 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFE, 0x03, 0xF7, 0xC0, 0x7E, 0xFC, 0x3F, + 0xCF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xFB, 0xE0, 0xFC, 0x7C, 0x07, 0xE0, + 0x07, 0xFE, 0x03, 0xFF, 0xE0, 0xFF, 0xF8, 0x7E, 0x1F, 0x1F, 0x03, 0xCF, + 0x80, 0xFB, 0xE0, 0x1E, 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0xFF, 0xFB, 0xE0, + 0x00, 0xF8, 0x00, 0x3F, 0x03, 0xE7, 0xE1, 0xF9, 0xFF, 0xFC, 0x3F, 0xFE, + 0x07, 0xFF, 0x00, 0x7F, 0x00, 0x0F, 0xC7, 0xF3, 0xFC, 0xFF, 0x3E, 0x0F, + 0x83, 0xE3, 0xFE, 0xFF, 0xBF, 0xE3, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, + 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, + 0x0F, 0x80, 0x07, 0xC7, 0xC3, 0xFD, 0xF3, 0xFF, 0xFC, 0xFF, 0xFF, 0x7E, + 0x1F, 0xDF, 0x03, 0xFF, 0xC0, 0xFF, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, + 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xFC, 0x0F, 0xDF, 0x03, 0xF7, 0xE1, 0xFD, + 0xFF, 0xFF, 0x3F, 0xFF, 0xC7, 0xFD, 0xF0, 0x7C, 0x7C, 0x00, 0x1F, 0x00, + 0x07, 0xFF, 0x03, 0xF7, 0xE1, 0xF9, 0xFF, 0xFC, 0x3F, 0xFE, 0x01, 0xFE, + 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, + 0xC0, 0x03, 0xE0, 0x01, 0xF1, 0xF0, 0xFB, 0xFE, 0x7F, 0xFF, 0xBF, 0xFF, + 0xDF, 0xC3, 0xFF, 0xC0, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, + 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, + 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xC0, 0xFF, 0xFF, 0xF0, + 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC0, 0x3E, 0x7C, 0xF9, 0xF0, 0x00, 0x00, 0x1F, 0x3E, 0x7C, 0xF9, + 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, + 0x7C, 0xF9, 0xF3, 0xFF, 0xFF, 0xFE, 0xF8, 0xF8, 0x00, 0x7C, 0x00, 0x3E, + 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x3E, + 0xF8, 0x3E, 0x7C, 0x3F, 0x3E, 0x3F, 0x1F, 0x3F, 0x0F, 0x9F, 0x07, 0xDF, + 0x03, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0xF0, 0x7F, 0xF8, 0x3F, 0x7E, 0x1F, + 0x1F, 0x0F, 0x87, 0xC7, 0xC3, 0xF3, 0xE0, 0xF9, 0xF0, 0x7E, 0xF8, 0x1F, + 0x7C, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xF8, 0xF8, 0x3F, 0x1F, + 0x7F, 0x9F, 0xF3, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xF8, + 0x7F, 0xF8, 0x3F, 0x07, 0xFE, 0x07, 0xC0, 0xFF, 0xC0, 0xF8, 0x1F, 0xF8, + 0x1F, 0x03, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, 0x7C, 0x0F, 0xFC, 0x0F, 0x81, + 0xFF, 0x81, 0xF0, 0x3F, 0xF0, 0x3E, 0x07, 0xFE, 0x07, 0xC0, 0xFF, 0xC0, + 0xF8, 0x1F, 0xF8, 0x1F, 0x03, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, 0x7C, 0x0F, + 0x80, 0xF8, 0xF8, 0x7D, 0xFF, 0x3F, 0xFF, 0xDF, 0xFF, 0xEF, 0xE1, 0xFF, + 0xE0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, + 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, + 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xE0, 0x03, 0xF8, 0x01, 0xFF, 0xC0, 0x7F, + 0xFC, 0x1F, 0xFF, 0xC7, 0xF0, 0xFC, 0xF8, 0x0F, 0xBF, 0x01, 0xFF, 0xC0, + 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC, 0x01, 0xFF, 0xC0, + 0x7E, 0xF8, 0x0F, 0x9F, 0x87, 0xF1, 0xFF, 0xFC, 0x1F, 0xFF, 0x01, 0xFF, + 0xC0, 0x0F, 0xE0, 0x00, 0xF8, 0xF8, 0x3E, 0xFF, 0x8F, 0xFF, 0xF3, 0xFF, + 0xFC, 0xFE, 0x1F, 0xBF, 0x03, 0xEF, 0xC0, 0xFF, 0xE0, 0x1F, 0xF8, 0x07, + 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xFC, 0x0F, 0xFF, 0x03, 0xEF, + 0xE1, 0xFB, 0xFF, 0xFC, 0xFF, 0xFF, 0x3E, 0xFF, 0x0F, 0x8F, 0x83, 0xE0, + 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF8, 0x00, + 0x3E, 0x00, 0x00, 0x07, 0xE3, 0xE1, 0xFF, 0x7C, 0x7F, 0xFF, 0x9F, 0xFF, + 0xF7, 0xF1, 0xFE, 0xF8, 0x0F, 0xFF, 0x01, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, + 0xFF, 0x00, 0x7F, 0xE0, 0x0F, 0xFC, 0x01, 0xFF, 0xC0, 0x7E, 0xF8, 0x0F, + 0xDF, 0x83, 0xF9, 0xFF, 0xFF, 0x3F, 0xFF, 0xE1, 0xFF, 0x7C, 0x1F, 0x8F, + 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, + 0x00, 0x03, 0xE0, 0x00, 0x7C, 0xF8, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xE1, + 0xF8, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, + 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x00, 0x07, 0xF0, 0x0F, 0xFE, + 0x0F, 0xFF, 0x87, 0xFF, 0xE7, 0xE1, 0xF3, 0xE0, 0x79, 0xF8, 0x00, 0xFF, + 0x80, 0x3F, 0xFC, 0x1F, 0xFF, 0x83, 0xFF, 0xC0, 0x3F, 0xF0, 0x01, 0xFF, + 0xC0, 0x7D, 0xF0, 0x7E, 0xFF, 0xFE, 0x3F, 0xFF, 0x0F, 0xFF, 0x01, 0xFE, + 0x00, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0x3E, 0x1F, + 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xF3, + 0xF8, 0xFC, 0x3E, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, + 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, + 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xF0, 0xFF, 0xFF, + 0xFF, 0x7F, 0xFF, 0x9F, 0xF7, 0xC7, 0xE3, 0xE0, 0x7C, 0x07, 0xCF, 0x80, + 0xF9, 0xF0, 0x1F, 0x1F, 0x07, 0xC3, 0xE0, 0xF8, 0x7C, 0x1F, 0x07, 0x83, + 0xC0, 0xF8, 0xF8, 0x1F, 0x1F, 0x01, 0xE3, 0xC0, 0x3E, 0x78, 0x07, 0xDF, + 0x00, 0x7B, 0xC0, 0x0F, 0xF8, 0x01, 0xFF, 0x00, 0x1F, 0xC0, 0x03, 0xF8, + 0x00, 0x7F, 0x00, 0x07, 0xC0, 0x00, 0xFC, 0x1F, 0x03, 0xEF, 0x83, 0xE0, + 0x7D, 0xF0, 0x7E, 0x1F, 0x3E, 0x0F, 0xC3, 0xE3, 0xC3, 0xF8, 0x7C, 0x7C, + 0x7F, 0x0F, 0x0F, 0x8F, 0xF3, 0xE1, 0xF1, 0xDE, 0x7C, 0x1E, 0x7B, 0xCF, + 0x83, 0xEF, 0x39, 0xE0, 0x7D, 0xE7, 0x3C, 0x07, 0xB8, 0xFF, 0x80, 0xF7, + 0x1F, 0xE0, 0x1F, 0xE3, 0xFC, 0x03, 0xFC, 0x3F, 0x80, 0x3F, 0x07, 0xF0, + 0x07, 0xE0, 0xFC, 0x00, 0xFC, 0x1F, 0x80, 0x0F, 0x83, 0xF0, 0x00, 0xFC, + 0x1F, 0x9F, 0x07, 0xE7, 0xE3, 0xF0, 0xF8, 0xF8, 0x1F, 0x7E, 0x07, 0xDF, + 0x00, 0xFF, 0x80, 0x1F, 0xE0, 0x07, 0xF0, 0x00, 0xF8, 0x00, 0x7F, 0x00, + 0x3F, 0xE0, 0x0F, 0xF8, 0x07, 0xDF, 0x03, 0xF7, 0xE0, 0xF8, 0xF8, 0x7E, + 0x3F, 0x1F, 0x07, 0xEF, 0xC0, 0xF8, 0x7C, 0x03, 0xEF, 0x80, 0xF9, 0xF8, + 0x1F, 0x1F, 0x03, 0xE3, 0xE0, 0xF8, 0x7C, 0x1F, 0x07, 0xC3, 0xE0, 0xF8, + 0x78, 0x0F, 0x1F, 0x01, 0xF3, 0xC0, 0x3E, 0x78, 0x03, 0xDF, 0x00, 0x7F, + 0xC0, 0x0F, 0xF8, 0x00, 0xFF, 0x00, 0x1F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, + 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1E, 0x00, 0x07, 0xC0, 0x07, 0xF8, + 0x00, 0xFE, 0x00, 0x1F, 0x80, 0x03, 0xE0, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, + 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x7E, 0x00, 0xFC, 0x01, 0xF8, 0x03, 0xF0, + 0x03, 0xF0, 0x07, 0xE0, 0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, + 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x87, + 0xC7, 0xE3, 0xF1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, + 0xE0, 0xF0, 0xF9, 0xF8, 0xF0, 0x7E, 0x0F, 0x83, 0xC1, 0xE0, 0xF0, 0x78, + 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xFC, 0x7E, 0x1F, 0x07, 0x80, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE0, 0xF0, 0x7C, 0x3E, 0x1F, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, + 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x1F, 0x83, 0xC7, 0xE7, 0xC3, + 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC7, 0xE3, 0xE1, + 0xF0, 0xF0, 0x00, 0x3C, 0x00, 0xFE, 0x0F, 0xFE, 0x1E, 0x1F, 0xFC, 0x0F, + 0xC0, 0x0F, 0x00}; + +const GFXglyph FreeSansBold18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 10, 0, 1}, // 0x20 ' ' + {0, 5, 25, 12, 4, -24}, // 0x21 '!' + {16, 13, 9, 17, 2, -25}, // 0x22 '"' + {31, 20, 24, 19, 0, -23}, // 0x23 '#' + {91, 19, 29, 19, 0, -25}, // 0x24 '$' + {160, 29, 25, 31, 1, -24}, // 0x25 '%' + {251, 22, 25, 25, 2, -24}, // 0x26 '&' + {320, 5, 9, 9, 2, -25}, // 0x27 ''' + {326, 9, 33, 12, 1, -25}, // 0x28 '(' + {364, 9, 33, 12, 1, -25}, // 0x29 ')' + {402, 12, 11, 14, 0, -25}, // 0x2A '*' + {419, 16, 16, 20, 2, -15}, // 0x2B '+' + {451, 5, 11, 9, 2, -4}, // 0x2C ',' + {458, 9, 4, 12, 1, -10}, // 0x2D '-' + {463, 5, 5, 9, 2, -4}, // 0x2E '.' + {467, 9, 25, 10, 0, -24}, // 0x2F '/' + {496, 17, 25, 19, 1, -24}, // 0x30 '0' + {550, 10, 25, 19, 3, -24}, // 0x31 '1' + {582, 17, 25, 19, 1, -24}, // 0x32 '2' + {636, 17, 25, 19, 1, -24}, // 0x33 '3' + {690, 16, 25, 19, 2, -24}, // 0x34 '4' + {740, 17, 25, 19, 1, -24}, // 0x35 '5' + {794, 18, 25, 19, 1, -24}, // 0x36 '6' + {851, 17, 25, 19, 1, -24}, // 0x37 '7' + {905, 17, 25, 19, 1, -24}, // 0x38 '8' + {959, 17, 25, 19, 1, -24}, // 0x39 '9' + {1013, 5, 18, 9, 2, -17}, // 0x3A ':' + {1025, 5, 24, 9, 2, -17}, // 0x3B ';' + {1040, 18, 17, 20, 1, -16}, // 0x3C '<' + {1079, 17, 12, 20, 2, -13}, // 0x3D '=' + {1105, 18, 17, 20, 1, -16}, // 0x3E '>' + {1144, 18, 26, 21, 2, -25}, // 0x3F '?' + {1203, 32, 31, 34, 1, -25}, // 0x40 '@' + {1327, 24, 26, 24, 0, -25}, // 0x41 'A' + {1405, 20, 26, 25, 3, -25}, // 0x42 'B' + {1470, 23, 26, 25, 1, -25}, // 0x43 'C' + {1545, 21, 26, 25, 3, -25}, // 0x44 'D' + {1614, 19, 26, 23, 3, -25}, // 0x45 'E' + {1676, 17, 26, 22, 3, -25}, // 0x46 'F' + {1732, 24, 26, 27, 1, -25}, // 0x47 'G' + {1810, 20, 26, 26, 3, -25}, // 0x48 'H' + {1875, 5, 26, 11, 3, -25}, // 0x49 'I' + {1892, 16, 26, 20, 1, -25}, // 0x4A 'J' + {1944, 22, 26, 25, 3, -25}, // 0x4B 'K' + {2016, 17, 26, 22, 3, -25}, // 0x4C 'L' + {2072, 24, 26, 30, 3, -25}, // 0x4D 'M' + {2150, 20, 26, 26, 3, -25}, // 0x4E 'N' + {2215, 25, 26, 27, 1, -25}, // 0x4F 'O' + {2297, 19, 26, 24, 3, -25}, // 0x50 'P' + {2359, 25, 27, 27, 1, -25}, // 0x51 'Q' + {2444, 21, 26, 25, 3, -25}, // 0x52 'R' + {2513, 20, 26, 24, 2, -25}, // 0x53 'S' + {2578, 19, 26, 23, 2, -25}, // 0x54 'T' + {2640, 20, 26, 26, 3, -25}, // 0x55 'U' + {2705, 22, 26, 23, 1, -25}, // 0x56 'V' + {2777, 32, 26, 34, 1, -25}, // 0x57 'W' + {2881, 22, 26, 24, 1, -25}, // 0x58 'X' + {2953, 21, 26, 22, 1, -25}, // 0x59 'Y' + {3022, 19, 26, 21, 1, -25}, // 0x5A 'Z' + {3084, 8, 33, 12, 2, -25}, // 0x5B '[' + {3117, 10, 25, 10, 0, -24}, // 0x5C '\' + {3149, 8, 33, 12, 1, -25}, // 0x5D ']' + {3182, 16, 15, 20, 2, -23}, // 0x5E '^' + {3212, 21, 3, 19, -1, 5}, // 0x5F '_' + {3220, 7, 5, 9, 1, -25}, // 0x60 '`' + {3225, 18, 19, 20, 1, -18}, // 0x61 'a' + {3268, 18, 26, 22, 2, -25}, // 0x62 'b' + {3327, 17, 19, 20, 1, -18}, // 0x63 'c' + {3368, 19, 26, 22, 1, -25}, // 0x64 'd' + {3430, 18, 19, 20, 1, -18}, // 0x65 'e' + {3473, 10, 26, 12, 1, -25}, // 0x66 'f' + {3506, 18, 26, 21, 1, -18}, // 0x67 'g' + {3565, 17, 26, 21, 2, -25}, // 0x68 'h' + {3621, 5, 26, 10, 2, -25}, // 0x69 'i' + {3638, 7, 33, 10, 0, -25}, // 0x6A 'j' + {3667, 17, 26, 20, 2, -25}, // 0x6B 'k' + {3723, 5, 26, 9, 2, -25}, // 0x6C 'l' + {3740, 27, 19, 31, 2, -18}, // 0x6D 'm' + {3805, 17, 19, 21, 2, -18}, // 0x6E 'n' + {3846, 19, 19, 21, 1, -18}, // 0x6F 'o' + {3892, 18, 26, 22, 2, -18}, // 0x70 'p' + {3951, 19, 26, 22, 1, -18}, // 0x71 'q' + {4013, 11, 19, 14, 2, -18}, // 0x72 'r' + {4040, 17, 19, 19, 1, -18}, // 0x73 's' + {4081, 9, 23, 12, 1, -22}, // 0x74 't' + {4107, 17, 19, 21, 2, -18}, // 0x75 'u' + {4148, 19, 19, 19, 0, -18}, // 0x76 'v' + {4194, 27, 19, 27, 0, -18}, // 0x77 'w' + {4259, 18, 19, 19, 1, -18}, // 0x78 'x' + {4302, 19, 26, 19, 0, -18}, // 0x79 'y' + {4364, 16, 19, 18, 1, -18}, // 0x7A 'z' + {4402, 9, 33, 14, 1, -25}, // 0x7B '{' + {4440, 3, 33, 10, 4, -25}, // 0x7C '|' + {4453, 9, 33, 14, 3, -25}, // 0x7D '}' + {4491, 15, 6, 18, 1, -10}}; // 0x7E '~' + +const GFXfont FreeSansBold18pt7b PROGMEM = { + (uint8_t *)FreeSansBold18pt7bBitmaps, (GFXglyph *)FreeSansBold18pt7bGlyphs, + 0x20, 0x7E, 42}; + +// Approx. 5175 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold24pt7b.h new file mode 100644 index 0000000..b128e21 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold24pt7b.h @@ -0,0 +1,783 @@ +const uint8_t FreeSansBold24pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDF, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xC7, 0x0E, 0x1C, 0x00, 0x00, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFE, 0x1F, 0xFF, 0x87, 0xFF, 0xE1, + 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, 0x87, 0xFF, 0xE1, 0xFD, 0xF0, 0x3E, + 0x7C, 0x0F, 0x9F, 0x03, 0xE3, 0x80, 0x70, 0xE0, 0x1C, 0x00, 0xF8, 0x3E, + 0x00, 0x3E, 0x0F, 0x80, 0x0F, 0x83, 0xE0, 0x03, 0xE0, 0xF8, 0x00, 0xF8, + 0x7C, 0x00, 0x7C, 0x1F, 0x00, 0x1F, 0x07, 0xC1, 0xFF, 0xFF, 0xFF, 0x7F, + 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, + 0x03, 0xE0, 0xF8, 0x00, 0xF8, 0x3E, 0x00, 0x3E, 0x1F, 0x00, 0x1F, 0x07, + 0xC0, 0x07, 0xC1, 0xF0, 0x01, 0xF0, 0x7C, 0x00, 0x7C, 0x1F, 0x03, 0xFF, + 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xF3, + 0xFF, 0xFF, 0xFC, 0x0F, 0x87, 0xC0, 0x07, 0xC1, 0xF0, 0x01, 0xF0, 0x7C, + 0x00, 0x7C, 0x1F, 0x00, 0x1F, 0x07, 0xC0, 0x07, 0xC3, 0xE0, 0x03, 0xE0, + 0xF8, 0x00, 0xF8, 0x3E, 0x00, 0x3E, 0x0F, 0x80, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, + 0x80, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF8, 0x7F, 0x73, 0xFE, 0x7F, 0x38, + 0xFF, 0x3F, 0x1C, 0x3F, 0xDF, 0x8E, 0x0F, 0xEF, 0xC7, 0x07, 0xF7, 0xE3, + 0x80, 0x03, 0xF9, 0xC0, 0x01, 0xFE, 0xE0, 0x00, 0x7F, 0xF0, 0x00, 0x3F, + 0xFC, 0x00, 0x0F, 0xFF, 0xC0, 0x03, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x80, + 0x0F, 0xFF, 0xE0, 0x01, 0xFF, 0xF8, 0x00, 0xE7, 0xFC, 0x00, 0x71, 0xFF, + 0x00, 0x38, 0x7F, 0xFF, 0x1C, 0x1F, 0xFF, 0x8E, 0x0F, 0xFF, 0xC7, 0x07, + 0xFF, 0xE3, 0x87, 0xFB, 0xF9, 0xC3, 0xF9, 0xFE, 0xE7, 0xFC, 0x7F, 0xFF, + 0xFC, 0x3F, 0xFF, 0xFC, 0x0F, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x00, 0x3F, + 0xE0, 0x00, 0x03, 0x80, 0x00, 0x01, 0xC0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x70, 0x00, 0x03, 0xE0, 0x00, 0x3C, 0x00, 0x1F, 0xF0, 0x00, 0x78, 0x00, + 0x7F, 0xF8, 0x01, 0xE0, 0x01, 0xFF, 0xF0, 0x03, 0xC0, 0x07, 0xFF, 0xF0, + 0x0F, 0x00, 0x0F, 0x83, 0xE0, 0x1E, 0x00, 0x3E, 0x03, 0xE0, 0x78, 0x00, + 0x78, 0x03, 0xC0, 0xF0, 0x00, 0xF0, 0x07, 0x83, 0xC0, 0x01, 0xE0, 0x0F, + 0x07, 0x80, 0x03, 0xE0, 0x3E, 0x1E, 0x00, 0x03, 0xE0, 0xF8, 0x3C, 0x00, + 0x07, 0xFF, 0xF0, 0xF0, 0x00, 0x07, 0xFF, 0xC1, 0xE0, 0x00, 0x07, 0xFF, + 0x07, 0x80, 0x00, 0x07, 0xFC, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x01, 0xE0, 0xFF, 0x80, 0x00, + 0x07, 0x87, 0xFF, 0xC0, 0x00, 0x0F, 0x0F, 0xFF, 0x80, 0x00, 0x3C, 0x3F, + 0xFF, 0x80, 0x00, 0x78, 0xFC, 0x1F, 0x00, 0x01, 0xE1, 0xF0, 0x1F, 0x00, + 0x03, 0xC3, 0xC0, 0x1E, 0x00, 0x0F, 0x07, 0x80, 0x3C, 0x00, 0x1E, 0x0F, + 0x00, 0x78, 0x00, 0x78, 0x1F, 0x01, 0xF0, 0x00, 0xF0, 0x1F, 0x07, 0xC0, + 0x03, 0xC0, 0x3F, 0xFF, 0x80, 0x07, 0x80, 0x3F, 0xFE, 0x00, 0x1E, 0x00, + 0x7F, 0xF8, 0x00, 0x7C, 0x00, 0x3F, 0xE0, 0x00, 0xF0, 0x00, 0x1F, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, + 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x3F, 0xCF, 0xC0, 0x00, 0xFE, + 0x1F, 0x00, 0x03, 0xF8, 0x7C, 0x00, 0x0F, 0xE1, 0xF0, 0x00, 0x3F, 0xC7, + 0xC0, 0x00, 0x7F, 0x3E, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xC0, + 0x00, 0x07, 0xFE, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, 0x80, 0x00, + 0x07, 0xFF, 0x03, 0xE0, 0x3F, 0xFE, 0x0F, 0x83, 0xFF, 0xF8, 0x3E, 0x1F, + 0xF3, 0xF1, 0xF8, 0x7F, 0x07, 0xE7, 0xE3, 0xFC, 0x1F, 0xFF, 0x0F, 0xE0, + 0x3F, 0xFC, 0x3F, 0x80, 0x7F, 0xF0, 0xFE, 0x01, 0xFF, 0x83, 0xF8, 0x03, + 0xFE, 0x0F, 0xF0, 0x0F, 0xF0, 0x3F, 0xE0, 0x7F, 0xE0, 0x7F, 0xC3, 0xFF, + 0xC1, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFB, 0xFC, + 0x0F, 0xFF, 0xC7, 0xF8, 0x1F, 0xFE, 0x0F, 0xE0, 0x0F, 0xE0, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0x7C, 0xF8, 0xE1, 0xC0, 0x00, + 0xF0, 0x0F, 0x80, 0xF8, 0x07, 0xC0, 0x7C, 0x07, 0xE0, 0x3E, 0x03, 0xF0, + 0x1F, 0x80, 0xF8, 0x0F, 0xC0, 0x7E, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, + 0xC0, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x7E, 0x03, 0xF0, + 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x00, 0xF8, 0x07, 0xE0, 0x3F, 0x01, + 0xF8, 0x07, 0xC0, 0x3F, 0x01, 0xF8, 0x07, 0xC0, 0x3F, 0x00, 0xF8, 0x07, + 0xE0, 0x1F, 0x00, 0xF8, 0x03, 0xE0, 0x1F, 0x00, 0x7C, 0x01, 0xE0, 0x78, + 0x03, 0xE0, 0x0F, 0x80, 0x7C, 0x01, 0xF0, 0x0F, 0x80, 0x3E, 0x01, 0xF0, + 0x0F, 0xC0, 0x3E, 0x01, 0xF8, 0x0F, 0xC0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, + 0x7E, 0x01, 0xF8, 0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, + 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x7E, 0x03, 0xE0, 0x3F, 0x01, 0xF8, + 0x0F, 0xC0, 0x7C, 0x07, 0xE0, 0x3F, 0x01, 0xF0, 0x1F, 0x80, 0xF8, 0x0F, + 0xC0, 0x7C, 0x07, 0xE0, 0x3E, 0x03, 0xF0, 0x1F, 0x01, 0xF0, 0x00, 0x03, + 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x06, 0x38, 0xDF, 0xFF, 0xFF, 0xFF, + 0x9F, 0xFE, 0x07, 0xC0, 0x1F, 0xC0, 0x3F, 0x80, 0xF7, 0x83, 0xC7, 0x87, + 0x8F, 0x02, 0x08, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, + 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, + 0x00, 0x3E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, + 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x87, 0x0E, 0x1C, 0x78, 0xEF, 0xDF, 0x38, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x00, 0x38, 0x03, 0xC0, 0x1C, 0x00, 0xE0, 0x07, 0x00, + 0x70, 0x03, 0x80, 0x1C, 0x01, 0xE0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x38, + 0x01, 0xC0, 0x0E, 0x00, 0xF0, 0x07, 0x00, 0x38, 0x03, 0xC0, 0x1C, 0x00, + 0xE0, 0x07, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x01, 0xE0, 0x0E, 0x00, 0x70, + 0x03, 0x80, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x03, 0xFF, 0xC0, 0x0F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF8, 0x1F, + 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x3F, 0xC3, 0xFC, 0x7F, 0x81, 0xFE, 0x7F, + 0x00, 0xFE, 0x7F, 0x00, 0xFE, 0x7F, 0x00, 0xFE, 0xFE, 0x00, 0x7F, 0xFE, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0x7F, + 0x00, 0xFE, 0x7F, 0x00, 0xFE, 0x7F, 0x00, 0xFE, 0x7F, 0x81, 0xFE, 0x3F, + 0xC3, 0xFC, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, 0xF8, 0x1F, 0xFF, 0xF8, 0x0F, + 0xFF, 0xF0, 0x03, 0xFF, 0xC0, 0x00, 0xFF, 0x00, 0x00, 0x3C, 0x01, 0xF0, + 0x07, 0xC0, 0x3F, 0x01, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, + 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, + 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, + 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x01, 0xFE, 0x00, 0x0F, 0xFF, 0x80, + 0x3F, 0xFF, 0x80, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0x9F, + 0xE0, 0xFF, 0x7F, 0x80, 0xFF, 0xFE, 0x01, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, + 0x03, 0xFF, 0xF0, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, + 0x7F, 0x80, 0x00, 0xFE, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x7F, + 0xC0, 0x01, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xE0, 0x00, 0xFF, 0x00, + 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0x7F, 0x00, 0x01, + 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xBF, + 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x01, 0xFE, 0x00, 0x0F, + 0xFF, 0x80, 0x7F, 0xFF, 0x81, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0x8F, 0xFF, + 0xFF, 0x1F, 0xE1, 0xFF, 0x7F, 0x81, 0xFE, 0xFE, 0x01, 0xFD, 0xFC, 0x03, + 0xFB, 0xF8, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x7F, + 0x00, 0x01, 0xFC, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0x7F, 0xC0, + 0x00, 0xFF, 0xE0, 0x00, 0x3F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x3F, 0xC0, + 0x00, 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, + 0x07, 0xFF, 0xF8, 0x0F, 0xF7, 0xF8, 0x3F, 0xCF, 0xFF, 0xFF, 0x9F, 0xFF, + 0xFE, 0x1F, 0xFF, 0xF8, 0x1F, 0xFF, 0xE0, 0x0F, 0xFF, 0x80, 0x07, 0xF8, + 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x03, 0xFE, 0x00, 0x0F, 0xF8, + 0x00, 0x7F, 0xE0, 0x03, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0x7B, 0xF8, 0x01, + 0xEF, 0xE0, 0x0F, 0x3F, 0x80, 0x78, 0xFE, 0x01, 0xE3, 0xF8, 0x0F, 0x0F, + 0xE0, 0x38, 0x3F, 0x81, 0xE0, 0xFE, 0x07, 0x03, 0xF8, 0x3C, 0x0F, 0xE1, + 0xE0, 0x3F, 0x87, 0x00, 0xFE, 0x3C, 0x03, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0x00, 0xFE, 0x00, 0x03, 0xF8, 0x00, 0x0F, 0xE0, 0x00, 0x3F, 0x80, + 0x00, 0xFE, 0x00, 0x03, 0xF8, 0x00, 0x0F, 0xE0, 0x1F, 0xFF, 0xFC, 0x3F, + 0xFF, 0xF8, 0x7F, 0xFF, 0xF0, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xC7, 0xFF, + 0xFF, 0x8F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x78, 0x00, + 0x01, 0xF1, 0xF8, 0x03, 0xEF, 0xFE, 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xFE, + 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xFC, 0xFE, 0x07, 0xFC, 0x00, 0x07, 0xF8, + 0x00, 0x07, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, + 0x00, 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0xF8, + 0x0F, 0xF7, 0xF8, 0x3F, 0xEF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0x0F, 0xFF, + 0xFC, 0x0F, 0xFF, 0xE0, 0x0F, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x00, 0xFF, + 0x00, 0x07, 0xFF, 0x80, 0x1F, 0xFF, 0xC0, 0x7F, 0xFF, 0x81, 0xFF, 0xFF, + 0x87, 0xFF, 0xFF, 0x8F, 0xF0, 0xFF, 0x3F, 0xC0, 0xFE, 0x7F, 0x00, 0x00, + 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE3, 0xF0, 0x1F, + 0xDF, 0xF8, 0x3F, 0xFF, 0xFC, 0x7F, 0xFF, 0xFC, 0xFF, 0xFF, 0xF9, 0xFF, + 0x87, 0xFB, 0xFC, 0x07, 0xF7, 0xF8, 0x0F, 0xFF, 0xE0, 0x0F, 0xFF, 0xC0, + 0x1F, 0xFF, 0x80, 0x3F, 0xFF, 0x00, 0x7F, 0x7E, 0x00, 0xFE, 0xFC, 0x01, + 0xFD, 0xFC, 0x07, 0xFB, 0xF8, 0x0F, 0xE3, 0xFC, 0x7F, 0xC7, 0xFF, 0xFF, + 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xF8, 0x0F, 0xFF, 0xE0, 0x07, 0xFF, 0x80, + 0x03, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x3F, 0x00, + 0x00, 0xFC, 0x00, 0x03, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x80, 0x00, + 0x7F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, + 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xF8, 0x00, 0x07, 0xF0, + 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFC, 0x00, + 0x01, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, + 0x3F, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x03, 0xF8, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x03, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0x1F, + 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, 0x3F, 0xFF, 0xF8, 0x7F, 0x83, 0xFC, 0x7F, + 0x00, 0xFC, 0x7E, 0x00, 0xFC, 0x7E, 0x00, 0x7C, 0x7E, 0x00, 0x7C, 0x7E, + 0x00, 0xFC, 0x3F, 0x00, 0xF8, 0x3F, 0x83, 0xF8, 0x0F, 0xFF, 0xF0, 0x07, + 0xFF, 0xC0, 0x0F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF8, 0x3F, 0xC3, 0xFC, 0x7F, + 0x00, 0xFE, 0x7F, 0x00, 0xFE, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, + 0x00, 0xFE, 0x7F, 0x83, 0xFE, 0x7F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x1F, + 0xFF, 0xF8, 0x0F, 0xFF, 0xF0, 0x07, 0xFF, 0xC0, 0x00, 0xFF, 0x00, 0x00, + 0xFF, 0x00, 0x03, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0x1F, 0xFF, 0xF0, 0x3F, + 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x7F, 0xC3, 0xFC, 0x7F, 0x01, 0xFE, 0xFF, + 0x00, 0xFE, 0xFE, 0x00, 0x7E, 0xFE, 0x00, 0x7E, 0xFE, 0x00, 0x7F, 0xFE, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0x7F, + 0x01, 0xFF, 0x7F, 0xC3, 0xFF, 0x7F, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x1F, + 0xFF, 0xFF, 0x0F, 0xFF, 0x7F, 0x07, 0xFE, 0x7F, 0x01, 0xFC, 0x7E, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x7F, 0x01, 0xFC, 0x7F, + 0x83, 0xFC, 0x7F, 0xFF, 0xF8, 0x3F, 0xFF, 0xF8, 0x3F, 0xFF, 0xF0, 0x1F, + 0xFF, 0xE0, 0x07, 0xFF, 0x80, 0x01, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x1C, 0x38, 0x71, 0xE7, 0xBF, 0x7C, 0xE0, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x3C, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0xF0, 0x01, + 0xFF, 0xE0, 0x0F, 0xFF, 0xC0, 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, 0x7F, 0xFC, + 0x01, 0xFF, 0xC0, 0x03, 0xFC, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0xE0, 0x00, + 0x1F, 0xF8, 0x00, 0x3F, 0xFE, 0x00, 0x0F, 0xFF, 0x80, 0x07, 0xFF, 0xE0, + 0x01, 0xFF, 0xF8, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xF0, 0x00, 0x0F, 0xE0, + 0x00, 0x03, 0xC0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x80, 0x00, + 0x01, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x07, 0xFC, 0x00, 0x0F, 0xFE, 0x00, + 0x1F, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0xFC, + 0x00, 0x1F, 0xFC, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0xE0, + 0x01, 0xFF, 0xC0, 0x0F, 0xFF, 0x80, 0xFF, 0xF8, 0x0F, 0xFF, 0x80, 0xFF, + 0xFC, 0x03, 0xFF, 0xC0, 0x07, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0x1E, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x07, 0xFF, 0xC0, 0x1F, + 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x7F, 0xFF, 0xFC, 0x7F, + 0x83, 0xFE, 0x7F, 0x01, 0xFE, 0xFF, 0x00, 0xFF, 0xFE, 0x00, 0x7F, 0xFE, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0x00, + 0x01, 0xFE, 0x00, 0x03, 0xFE, 0x00, 0x07, 0xFC, 0x00, 0x0F, 0xF8, 0x00, + 0x3F, 0xF0, 0x00, 0x3F, 0xE0, 0x00, 0x7F, 0x80, 0x00, 0x7F, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xE0, + 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, + 0x03, 0xFE, 0x01, 0xFF, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x7F, + 0x80, 0x00, 0x3F, 0x80, 0x1F, 0xC0, 0x00, 0x03, 0xF8, 0x07, 0xF0, 0x00, + 0x00, 0x1F, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF0, 0x3F, 0x00, 0x00, 0x00, + 0x3E, 0x0F, 0xC0, 0x07, 0xE3, 0xC3, 0xE1, 0xF0, 0x03, 0xFE, 0xF8, 0x3C, + 0x7E, 0x01, 0xFF, 0xFF, 0x07, 0x8F, 0x80, 0x7E, 0x1F, 0xC0, 0x7B, 0xF0, + 0x1F, 0x81, 0xF8, 0x0F, 0x7C, 0x03, 0xE0, 0x1F, 0x01, 0xEF, 0x80, 0xF8, + 0x03, 0xC0, 0x3F, 0xF0, 0x1E, 0x00, 0x78, 0x07, 0xFC, 0x07, 0xC0, 0x0F, + 0x00, 0xFF, 0x80, 0xF0, 0x01, 0xE0, 0x1F, 0xF0, 0x1E, 0x00, 0x38, 0x07, + 0xFE, 0x07, 0xC0, 0x0F, 0x00, 0xFF, 0xC0, 0xF8, 0x01, 0xE0, 0x1E, 0xF8, + 0x1F, 0x00, 0x38, 0x07, 0xDF, 0x03, 0xE0, 0x0F, 0x00, 0xF3, 0xF0, 0x7C, + 0x03, 0xE0, 0x3E, 0x3E, 0x0F, 0xC0, 0xFC, 0x0F, 0x87, 0xC0, 0xFC, 0x3F, + 0xC7, 0xF0, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, 0xC1, 0xFF, 0xEF, 0xFF, + 0x01, 0xFC, 0x1F, 0xF8, 0xFF, 0x80, 0x1F, 0xC0, 0xFC, 0x07, 0xC0, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x60, 0x00, 0x01, 0xFF, 0xFF, + 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xF0, + 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, + 0x1F, 0xFC, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, + 0x3F, 0xFE, 0x00, 0x00, 0x3F, 0x7E, 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, + 0x7F, 0x7F, 0x00, 0x00, 0x7E, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x80, 0x00, + 0xFE, 0x3F, 0x80, 0x01, 0xFC, 0x1F, 0x80, 0x01, 0xFC, 0x1F, 0xC0, 0x01, + 0xF8, 0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x03, 0xF8, 0x0F, 0xE0, 0x03, + 0xF0, 0x0F, 0xE0, 0x07, 0xF0, 0x07, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, + 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8, 0x1F, + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFC, 0x1F, 0xC0, 0x01, 0xFC, 0x3F, + 0x80, 0x01, 0xFC, 0x3F, 0x80, 0x00, 0xFE, 0x3F, 0x80, 0x00, 0xFE, 0x7F, + 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x7F, 0xFF, + 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, + 0x8F, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0x3F, 0x80, 0x1F, 0xF7, 0xF0, + 0x01, 0xFE, 0xFE, 0x00, 0x1F, 0xDF, 0xC0, 0x03, 0xFB, 0xF8, 0x00, 0x7F, + 0x7F, 0x00, 0x1F, 0xCF, 0xE0, 0x07, 0xF9, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, + 0xFF, 0x87, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xE3, + 0xFF, 0xFF, 0xFE, 0x7F, 0x00, 0x1F, 0xEF, 0xE0, 0x01, 0xFD, 0xFC, 0x00, + 0x1F, 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, 0x7F, 0xFE, 0x00, 0x0F, 0xFF, + 0xC0, 0x01, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0x00, 0x1F, 0xEF, 0xFF, 0xFF, + 0xFD, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xF8, 0xFF, + 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x03, 0xFF, + 0xF8, 0x00, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, + 0xE0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0x81, 0xFF, 0x0F, 0xF8, 0x01, 0xFE, + 0x3F, 0xC0, 0x07, 0xF9, 0xFE, 0x00, 0x0F, 0xE7, 0xF8, 0x00, 0x1F, 0xDF, + 0xC0, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x0F, 0xE0, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, + 0x7F, 0x00, 0x01, 0xFD, 0xFC, 0x00, 0x07, 0xF7, 0xF8, 0x00, 0x3F, 0xCF, + 0xF0, 0x00, 0xFE, 0x3F, 0xE0, 0x07, 0xF8, 0x7F, 0xE0, 0x7F, 0xC0, 0xFF, + 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, + 0xFE, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x07, 0xFC, 0x00, 0xFF, 0xFF, 0xC0, + 0x0F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, + 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF8, 0xFE, 0x00, 0xFF, 0xCF, 0xE0, 0x03, + 0xFC, 0xFE, 0x00, 0x1F, 0xEF, 0xE0, 0x01, 0xFE, 0xFE, 0x00, 0x0F, 0xEF, + 0xE0, 0x00, 0xFE, 0xFE, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x7F, 0xFE, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0x7F, 0xFE, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x7F, + 0xFE, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x7F, 0xFE, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x7F, 0xFE, 0x00, 0x0F, 0xEF, 0xE0, 0x00, 0xFE, 0xFE, 0x00, 0x1F, + 0xEF, 0xE0, 0x01, 0xFE, 0xFE, 0x00, 0x3F, 0xCF, 0xE0, 0x0F, 0xFC, 0xFF, + 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0x7F, + 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xF7, + 0xFF, 0xFF, 0xFB, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x0F, 0xE0, 0x00, + 0x07, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, + 0xFE, 0x7F, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x9F, 0xC0, 0x00, 0x0F, 0xE0, + 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFF, 0xFF, + 0xFC, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, + 0xFC, 0xFF, 0xFF, 0xFC, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, + 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, + 0x07, 0xFE, 0x03, 0xFF, 0x0F, 0xF0, 0x01, 0xFE, 0x3F, 0xC0, 0x01, 0xFC, + 0x7F, 0x00, 0x01, 0xFD, 0xFE, 0x00, 0x03, 0xFB, 0xF8, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0xFE, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF8, + 0x00, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xC0, + 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xBF, 0x80, 0x00, 0x3F, 0x7F, 0x00, + 0x00, 0x7E, 0xFF, 0x00, 0x01, 0xFC, 0xFF, 0x00, 0x03, 0xF9, 0xFF, 0x00, + 0x0F, 0xF1, 0xFF, 0x00, 0x3F, 0xE3, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0xFF, + 0xFF, 0x83, 0xFF, 0xFF, 0xDF, 0x03, 0xFF, 0xFF, 0x9E, 0x03, 0xFF, 0xFE, + 0x3C, 0x01, 0xFF, 0xF0, 0x78, 0x00, 0x7F, 0x80, 0x00, 0xFE, 0x00, 0x0F, + 0xFF, 0xC0, 0x01, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, 0x7F, + 0xFE, 0x00, 0x0F, 0xFF, 0xC0, 0x01, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, + 0x80, 0x03, 0xFF, 0xF0, 0x00, 0x7F, 0xFE, 0x00, 0x0F, 0xFF, 0xC0, 0x01, + 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, + 0x00, 0x1F, 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, 0x7F, 0xFE, 0x00, 0x0F, + 0xFF, 0xC0, 0x01, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, + 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFC, + 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, + 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x07, + 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xF0, + 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xFF, 0xE0, + 0x1F, 0xFF, 0x80, 0x7F, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xE0, 0x1F, + 0xFF, 0xC0, 0xFF, 0xFF, 0x87, 0xFD, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0x8F, + 0xFF, 0xFC, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0x00, 0x1F, 0xE0, 0x00, 0xFE, + 0x00, 0x0F, 0xF3, 0xF8, 0x00, 0x7F, 0x8F, 0xE0, 0x03, 0xFC, 0x3F, 0x80, + 0x1F, 0xE0, 0xFE, 0x00, 0xFF, 0x83, 0xF8, 0x07, 0xFC, 0x0F, 0xE0, 0x1F, + 0xE0, 0x3F, 0x80, 0xFF, 0x00, 0xFE, 0x07, 0xF8, 0x03, 0xF8, 0x3F, 0xC0, + 0x0F, 0xE1, 0xFE, 0x00, 0x3F, 0x8F, 0xF0, 0x00, 0xFE, 0x7F, 0x80, 0x03, + 0xFB, 0xFC, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0xFF, + 0xFF, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x3F, 0xF7, + 0xF8, 0x00, 0xFF, 0x8F, 0xF0, 0x03, 0xFC, 0x3F, 0xC0, 0x0F, 0xE0, 0x7F, + 0x80, 0x3F, 0x80, 0xFF, 0x00, 0xFE, 0x01, 0xFE, 0x03, 0xF8, 0x07, 0xFC, + 0x0F, 0xE0, 0x0F, 0xF0, 0x3F, 0x80, 0x1F, 0xE0, 0xFE, 0x00, 0x3F, 0xC3, + 0xF8, 0x00, 0xFF, 0x8F, 0xE0, 0x01, 0xFE, 0x3F, 0x80, 0x03, 0xFC, 0xFE, + 0x00, 0x07, 0xFB, 0xF8, 0x00, 0x1F, 0xF0, 0xFE, 0x00, 0x01, 0xFC, 0x00, + 0x03, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, + 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x03, + 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x3F, + 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x03, 0xF8, + 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x3F, 0x80, + 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x03, 0xF8, 0x00, + 0x07, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xE0, 0x03, + 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFC, 0x00, + 0x7F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xC0, + 0x1F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFC, + 0x07, 0xFF, 0xFF, 0xBE, 0x03, 0xEF, 0xFF, 0xDF, 0x01, 0xF7, 0xFF, 0xEF, + 0x80, 0xFB, 0xFF, 0xF7, 0xC0, 0xFD, 0xFF, 0xFB, 0xF0, 0x7C, 0xFF, 0xFC, + 0xF8, 0x3E, 0x7F, 0xFE, 0x7C, 0x1F, 0x3F, 0xFF, 0x3E, 0x0F, 0x9F, 0xFF, + 0x9F, 0x8F, 0x8F, 0xFF, 0xC7, 0xC7, 0xC7, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, + 0xF1, 0xF1, 0xF1, 0xFF, 0xF8, 0xFC, 0xF8, 0xFF, 0xFC, 0x3E, 0xF8, 0x7F, + 0xFE, 0x1F, 0x7C, 0x3F, 0xFF, 0x0F, 0xBE, 0x1F, 0xFF, 0x87, 0xDF, 0x0F, + 0xFF, 0xC3, 0xFF, 0x07, 0xFF, 0xE0, 0xFF, 0x83, 0xFF, 0xF0, 0x7F, 0xC1, + 0xFF, 0xF8, 0x3F, 0xE0, 0xFF, 0xFC, 0x1F, 0xF0, 0x7F, 0xFE, 0x07, 0xF0, + 0x3F, 0xFF, 0x03, 0xF8, 0x1F, 0xC0, 0xFE, 0x00, 0x07, 0xFF, 0xF0, 0x00, + 0x7F, 0xFF, 0x80, 0x07, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xC0, 0x07, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xF0, + 0x07, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xF8, 0x07, 0xFF, 0xEF, 0xC0, 0x7F, + 0xFE, 0xFE, 0x07, 0xFF, 0xE7, 0xE0, 0x7F, 0xFE, 0x7F, 0x07, 0xFF, 0xE3, + 0xF0, 0x7F, 0xFE, 0x1F, 0x87, 0xFF, 0xE1, 0xFC, 0x7F, 0xFE, 0x0F, 0xC7, + 0xFF, 0xE0, 0xFE, 0x7F, 0xFE, 0x07, 0xE7, 0xFF, 0xE0, 0x3F, 0x7F, 0xFE, + 0x03, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xE0, 0x0F, + 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xE0, 0x07, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, + 0xE0, 0x03, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0xFF, 0xFE, 0x00, + 0x0F, 0xFF, 0xE0, 0x00, 0x7F, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xFF, + 0x80, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, + 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x3F, 0xC0, + 0x0F, 0xF8, 0x3F, 0xC0, 0x01, 0xFE, 0x1F, 0xC0, 0x00, 0x7F, 0x1F, 0xE0, + 0x00, 0x3F, 0xCF, 0xE0, 0x00, 0x0F, 0xE7, 0xF0, 0x00, 0x07, 0xF7, 0xF8, + 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFE, + 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x0F, 0xFF, + 0xC0, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x01, 0xFF, + 0xFC, 0x00, 0x01, 0xFE, 0xFE, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x7F, + 0x3F, 0xC0, 0x00, 0x7F, 0x8F, 0xE0, 0x00, 0x3F, 0x87, 0xF8, 0x00, 0x3F, + 0xC1, 0xFE, 0x00, 0x3F, 0xC0, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xFF, 0xFF, + 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, + 0xE0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0xE0, 0x3F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFC, 0xFF, + 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xE0, 0x0F, 0xFB, 0xF8, 0x00, 0xFF, + 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0x01, 0xFF, 0xF8, 0x00, + 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0x03, 0xFF, 0xF8, + 0x03, 0xFE, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xF3, + 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xF8, 0x0F, 0xE0, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x0F, 0xE0, + 0x00, 0x03, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x0F, + 0xE0, 0x00, 0x03, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, + 0xE0, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, + 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x3F, 0xC0, 0x07, 0xF8, 0x3F, 0xC0, + 0x01, 0xFE, 0x1F, 0xC0, 0x00, 0x7F, 0x1F, 0xE0, 0x00, 0x3F, 0xCF, 0xE0, + 0x00, 0x0F, 0xE7, 0xF0, 0x00, 0x07, 0xF7, 0xF8, 0x00, 0x03, 0xFF, 0xF8, + 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x3F, 0xFF, + 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x21, 0xFE, + 0xFE, 0x00, 0x38, 0xFE, 0x7F, 0x00, 0x3E, 0x7F, 0x3F, 0xC0, 0x3F, 0xFF, + 0x8F, 0xE0, 0x0F, 0xFF, 0x87, 0xF8, 0x03, 0xFF, 0xC1, 0xFE, 0x00, 0xFF, + 0xC0, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, + 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, + 0xCF, 0xC0, 0x01, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, + 0xF8, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, 0xFE, 0x00, 0x3F, 0xEF, 0xE0, + 0x01, 0xFE, 0xFE, 0x00, 0x0F, 0xEF, 0xE0, 0x00, 0xFE, 0xFE, 0x00, 0x0F, + 0xEF, 0xE0, 0x00, 0xFE, 0xFE, 0x00, 0x0F, 0xEF, 0xE0, 0x01, 0xFC, 0xFE, + 0x00, 0x3F, 0xCF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x8F, + 0xE0, 0x07, 0xF8, 0xFE, 0x00, 0x1F, 0xCF, 0xE0, 0x01, 0xFC, 0xFE, 0x00, + 0x1F, 0xCF, 0xE0, 0x01, 0xFC, 0xFE, 0x00, 0x1F, 0xCF, 0xE0, 0x01, 0xFC, + 0xFE, 0x00, 0x1F, 0xCF, 0xE0, 0x01, 0xFC, 0xFE, 0x00, 0x1F, 0xCF, 0xE0, + 0x01, 0xFC, 0xFE, 0x00, 0x1F, 0xEF, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0xC0, + 0x00, 0x3F, 0xFF, 0x80, 0x0F, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xF0, 0x3F, + 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0xFF, 0xCF, 0xF0, 0x03, + 0xFE, 0xFE, 0x00, 0x1F, 0xEF, 0xE0, 0x00, 0xFE, 0xFE, 0x00, 0x0F, 0xEF, + 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x7F, 0xFC, + 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xF0, + 0x07, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xE0, 0x00, + 0x03, 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x07, + 0xFF, 0xE0, 0x00, 0x7F, 0xFE, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, + 0x00, 0x0F, 0xE7, 0xFC, 0x03, 0xFE, 0x7F, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, + 0xFC, 0x1F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFC, 0x00, + 0x07, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x0F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0xFE, 0x00, + 0x0F, 0xFF, 0xC0, 0x01, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, + 0x7F, 0xFE, 0x00, 0x0F, 0xFF, 0xC0, 0x01, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, + 0x00, 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0x80, 0x03, + 0xFF, 0xF0, 0x00, 0x7F, 0xFE, 0x00, 0x0F, 0xFF, 0xC0, 0x01, 0xFF, 0xF8, + 0x00, 0x3F, 0xFF, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0x1F, + 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, 0x7F, 0xFE, 0x00, 0x0F, 0xFF, 0xC0, + 0x01, 0xFF, 0xFC, 0x00, 0x7F, 0xBF, 0xC0, 0x1F, 0xE7, 0xFC, 0x07, 0xFC, + 0x7F, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, + 0xFE, 0x00, 0x7F, 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x03, + 0xFF, 0xF0, 0x00, 0x1F, 0xDF, 0xC0, 0x01, 0xFC, 0xFE, 0x00, 0x0F, 0xE7, + 0xF0, 0x00, 0x7F, 0x1F, 0xC0, 0x03, 0xF0, 0xFE, 0x00, 0x3F, 0x87, 0xF0, + 0x01, 0xFC, 0x1F, 0xC0, 0x0F, 0xC0, 0xFE, 0x00, 0xFE, 0x03, 0xF0, 0x07, + 0xF0, 0x1F, 0x80, 0x3F, 0x00, 0xFE, 0x03, 0xF8, 0x03, 0xF0, 0x1F, 0xC0, + 0x1F, 0x80, 0xFC, 0x00, 0xFE, 0x07, 0xE0, 0x03, 0xF0, 0x7F, 0x00, 0x1F, + 0x83, 0xF0, 0x00, 0xFE, 0x1F, 0x80, 0x03, 0xF1, 0xF8, 0x00, 0x1F, 0x8F, + 0xC0, 0x00, 0xFC, 0x7E, 0x00, 0x03, 0xF3, 0xE0, 0x00, 0x1F, 0xBF, 0x00, + 0x00, 0xFD, 0xF8, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0xFC, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0xFF, + 0x80, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0xFF, 0x00, 0x3F, 0x80, 0x1F, 0xFF, 0xE0, 0x07, 0xF0, 0x03, 0xFD, + 0xFC, 0x01, 0xFE, 0x00, 0x7F, 0x3F, 0x80, 0x3F, 0xE0, 0x0F, 0xE7, 0xF0, + 0x07, 0xFC, 0x01, 0xFC, 0x7F, 0x00, 0xFF, 0x80, 0x7F, 0x8F, 0xE0, 0x1F, + 0xF0, 0x0F, 0xE1, 0xFC, 0x07, 0xFF, 0x01, 0xFC, 0x3F, 0x80, 0xFB, 0xE0, + 0x3F, 0x83, 0xF0, 0x1F, 0x7C, 0x07, 0xE0, 0x7F, 0x03, 0xEF, 0x81, 0xFC, + 0x0F, 0xE0, 0x7D, 0xF0, 0x3F, 0x80, 0xFC, 0x1F, 0x9F, 0x07, 0xF0, 0x1F, + 0x83, 0xE3, 0xE0, 0xFC, 0x03, 0xF0, 0x7C, 0x7C, 0x1F, 0x80, 0x7F, 0x0F, + 0x8F, 0x87, 0xF0, 0x07, 0xE1, 0xF0, 0xF8, 0xFC, 0x00, 0xFC, 0x7E, 0x1F, + 0x1F, 0x80, 0x1F, 0x8F, 0x83, 0xE3, 0xF0, 0x01, 0xF9, 0xF0, 0x7C, 0x7E, + 0x00, 0x3F, 0x3E, 0x0F, 0x9F, 0x80, 0x07, 0xE7, 0xC0, 0xFB, 0xF0, 0x00, + 0xFD, 0xF0, 0x1F, 0x7E, 0x00, 0x0F, 0xBE, 0x03, 0xEF, 0xC0, 0x01, 0xFF, + 0xC0, 0x7D, 0xF0, 0x00, 0x3F, 0xF8, 0x0F, 0xFE, 0x00, 0x03, 0xFF, 0x00, + 0xFF, 0xC0, 0x00, 0x7F, 0xC0, 0x1F, 0xF0, 0x00, 0x0F, 0xF8, 0x03, 0xFE, + 0x00, 0x01, 0xFF, 0x00, 0x7F, 0xC0, 0x00, 0x1F, 0xE0, 0x07, 0xF8, 0x00, + 0x03, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x00, 0x07, + 0xE0, 0x03, 0xF8, 0x00, 0x7F, 0x80, 0x07, 0xF9, 0xFF, 0x00, 0x3F, 0xC3, + 0xFC, 0x00, 0xFF, 0x07, 0xF8, 0x07, 0xF8, 0x1F, 0xE0, 0x1F, 0xC0, 0x3F, + 0xC0, 0xFF, 0x00, 0xFF, 0x07, 0xF8, 0x01, 0xFE, 0x1F, 0xE0, 0x03, 0xF8, + 0xFF, 0x00, 0x0F, 0xF3, 0xF8, 0x00, 0x1F, 0xDF, 0xE0, 0x00, 0x3F, 0xFF, + 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0x00, + 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x03, 0xFF, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x03, + 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x7F, 0x7F, 0x80, 0x03, 0xF8, + 0xFF, 0x00, 0x1F, 0xE1, 0xFC, 0x00, 0x7F, 0x07, 0xF8, 0x03, 0xFC, 0x0F, + 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80, 0x7F, 0x83, 0xFC, 0x01, 0xFE, + 0x0F, 0xF0, 0x03, 0xFC, 0x7F, 0x80, 0x0F, 0xFB, 0xFE, 0x00, 0x1F, 0xE0, + 0xFF, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x7F, 0x9F, 0xE0, 0x03, 0xFC, 0xFF, + 0x00, 0x3F, 0xC3, 0xFC, 0x01, 0xFE, 0x0F, 0xE0, 0x0F, 0xE0, 0x7F, 0x00, + 0xFF, 0x01, 0xFC, 0x07, 0xF0, 0x0F, 0xE0, 0x7F, 0x80, 0x3F, 0x83, 0xF8, + 0x01, 0xFC, 0x3F, 0xC0, 0x07, 0xF1, 0xFC, 0x00, 0x3F, 0x8F, 0xE0, 0x00, + 0xFE, 0xFE, 0x00, 0x07, 0xF7, 0xF0, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0xFF, + 0xF8, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x0F, 0xE0, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x3F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x03, + 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x3F, 0xC0, 0x00, + 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xFE, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, + 0x00, 0x0F, 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, + 0xE1, 0xFC, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, + 0xFC, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, + 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x03, 0xC0, 0x07, 0x00, + 0x1C, 0x00, 0x78, 0x00, 0xE0, 0x03, 0x80, 0x0F, 0x00, 0x1C, 0x00, 0x70, + 0x01, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x00, 0x70, 0x01, 0xC0, 0x07, + 0x00, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, + 0x78, 0x00, 0xE0, 0x03, 0x80, 0x0F, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xE0, + 0x03, 0x80, 0x0E, 0x00, 0x3C, 0x00, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x3F, 0x87, 0xF0, + 0xFE, 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x3F, 0x87, 0xF0, 0xFE, + 0x1F, 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, + 0xC3, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xF0, + 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0x80, 0x03, 0xFE, 0x00, 0x0F, 0xFC, 0x00, + 0x7D, 0xF0, 0x01, 0xF7, 0xC0, 0x0F, 0xDF, 0x80, 0x3E, 0x3E, 0x00, 0xF8, + 0xFC, 0x07, 0xE1, 0xF0, 0x1F, 0x07, 0xC0, 0xFC, 0x1F, 0x83, 0xE0, 0x3E, + 0x0F, 0x80, 0xFC, 0x7E, 0x01, 0xF1, 0xF0, 0x07, 0xC7, 0xC0, 0x1F, 0xBE, + 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3E, 0x0F, 0x83, 0xC0, 0xF0, 0x38, 0x1E, + 0x01, 0xFF, 0x00, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, + 0x7F, 0xFF, 0xF8, 0x7F, 0xFF, 0xFC, 0x7F, 0x03, 0xFC, 0x7E, 0x01, 0xFC, + 0x00, 0x01, 0xFC, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xFC, 0x03, 0xFF, 0xFC, + 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x7F, 0xC1, 0xFC, 0xFF, 0x01, 0xFC, + 0xFE, 0x01, 0xFC, 0xFE, 0x03, 0xFC, 0xFE, 0x03, 0xFC, 0xFF, 0x07, 0xFC, + 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFC, 0x7F, 0xFF, 0xFC, 0x3F, 0xFD, 0xFE, + 0x1F, 0xF0, 0xFF, 0x07, 0xE0, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0x00, + 0x03, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x0F, + 0xC0, 0x3F, 0x9F, 0xF8, 0x1F, 0xDF, 0xFF, 0x0F, 0xFF, 0xFF, 0xC7, 0xFF, + 0xFF, 0xE3, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0xFE, 0xFF, 0x80, 0xFF, 0x7F, + 0x80, 0x3F, 0xBF, 0xC0, 0x1F, 0xFF, 0xC0, 0x07, 0xFF, 0xE0, 0x03, 0xFF, + 0xF0, 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x3F, + 0xFF, 0x80, 0x3F, 0xFF, 0xC0, 0x1F, 0xDF, 0xF0, 0x1F, 0xEF, 0xFC, 0x1F, + 0xF7, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFE, 0xFF, + 0xF8, 0x7F, 0x3F, 0xF0, 0x00, 0x07, 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x07, + 0xFF, 0xC0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xC7, 0xFF, + 0xFF, 0x9F, 0xF0, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x7F, 0xFC, 0x00, + 0x03, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, + 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0xFD, + 0xFE, 0x03, 0xFB, 0xFE, 0x0F, 0xF3, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0x87, + 0xFF, 0xFE, 0x07, 0xFF, 0xF8, 0x03, 0xFF, 0xE0, 0x01, 0xFE, 0x00, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, + 0x00, 0x03, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x3F, 0x80, 0x7E, 0x1F, 0xC0, 0xFF, 0xCF, 0xE1, 0xFF, 0xF7, + 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0x83, + 0xFF, 0x7F, 0x80, 0xFF, 0xBF, 0x80, 0x3F, 0xFF, 0xC0, 0x1F, 0xFF, 0xC0, + 0x07, 0xFF, 0xE0, 0x03, 0xFF, 0xF0, 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0xFC, + 0x00, 0x7F, 0xFE, 0x00, 0x3F, 0xFF, 0x80, 0x3F, 0xDF, 0xC0, 0x1F, 0xEF, + 0xF0, 0x1F, 0xF7, 0xFC, 0x1F, 0xF9, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFE, + 0x3F, 0xFF, 0xFF, 0x0F, 0xFF, 0xBF, 0x81, 0xFF, 0x9F, 0xC0, 0x3F, 0x00, + 0x00, 0x00, 0xFE, 0x00, 0x03, 0xFF, 0x80, 0x0F, 0xFF, 0xE0, 0x1F, 0xFF, + 0xF0, 0x3F, 0xFF, 0xF8, 0x3F, 0xC3, 0xF8, 0x7F, 0x80, 0xFC, 0x7F, 0x00, + 0xFC, 0x7F, 0x00, 0x7C, 0xFE, 0x00, 0x7E, 0xFE, 0x00, 0x7E, 0xFF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x7F, 0x7F, 0x00, + 0xFE, 0x3F, 0xC1, 0xFE, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, 0xF8, 0x0F, 0xFF, + 0xF0, 0x03, 0xFF, 0xC0, 0x00, 0xFF, 0x00, 0x01, 0xFC, 0x1F, 0xF0, 0xFF, + 0xC3, 0xFF, 0x1F, 0xFC, 0x7F, 0x81, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, + 0x0F, 0xFF, 0xBF, 0xFE, 0xFF, 0xFB, 0xFF, 0xE1, 0xFC, 0x07, 0xF0, 0x1F, + 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, + 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, + 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x00, 0x00, 0xF8, 0x7F, 0x07, 0xFE, + 0x7F, 0x0F, 0xFF, 0x7F, 0x1F, 0xFF, 0x7F, 0x3F, 0xFF, 0xFF, 0x3F, 0xFF, + 0xFF, 0x7F, 0xC3, 0xFF, 0x7F, 0x81, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, + 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0x7F, 0x81, 0xFF, 0x7F, 0xC3, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F, 0xFF, + 0xFF, 0x1F, 0xFF, 0xFF, 0x0F, 0xFF, 0x7F, 0x07, 0xFE, 0x7F, 0x01, 0xF8, + 0x7F, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x7F, 0x00, + 0xFF, 0x7F, 0x01, 0xFE, 0x7F, 0xC3, 0xFE, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, + 0xF8, 0x0F, 0xFF, 0xE0, 0x01, 0xFF, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, + 0x03, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, + 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x3F, 0x83, + 0xF8, 0xFF, 0xC7, 0xF7, 0xFF, 0xCF, 0xEF, 0xFF, 0xDF, 0xFF, 0xFF, 0xBF, + 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0x01, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, + 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0xE0, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, + 0x3F, 0xFF, 0x00, 0x7F, 0xFE, 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x03, + 0xFF, 0xF0, 0x07, 0xFF, 0xE0, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x3F, + 0xFF, 0x00, 0x7F, 0xFE, 0x00, 0xFF, 0xFC, 0x01, 0xFC, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x1F, 0xC7, 0xF1, 0xFC, 0x7F, 0x1F, 0xC7, 0xF0, 0x00, + 0x00, 0x00, 0x07, 0xF1, 0xFC, 0x7F, 0x1F, 0xC7, 0xF1, 0xFC, 0x7F, 0x1F, + 0xC7, 0xF1, 0xFC, 0x7F, 0x1F, 0xC7, 0xF1, 0xFC, 0x7F, 0x1F, 0xC7, 0xF1, + 0xFC, 0x7F, 0x1F, 0xC7, 0xF1, 0xFC, 0x7F, 0x1F, 0xC7, 0xF1, 0xFC, 0x7F, + 0x1F, 0xC7, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFE, 0xFE, 0x00, + 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x03, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, + 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x3F, 0x80, 0x00, 0x7F, 0x00, 0x00, 0xFE, + 0x00, 0x01, 0xFC, 0x03, 0xFB, 0xF8, 0x0F, 0xE7, 0xF0, 0x3F, 0xCF, 0xE0, + 0xFF, 0x1F, 0xC3, 0xFC, 0x3F, 0x87, 0xF0, 0x7F, 0x1F, 0xC0, 0xFE, 0x7F, + 0x01, 0xFD, 0xFC, 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0xF0, 0x0F, 0xFF, 0xE0, + 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0xE0, 0x7F, 0xDF, 0xC0, 0xFF, 0x3F, 0xC1, + 0xFC, 0x3F, 0x83, 0xF8, 0x3F, 0x87, 0xF0, 0x7F, 0x8F, 0xE0, 0x7F, 0x1F, + 0xC0, 0xFF, 0x3F, 0x80, 0xFE, 0x7F, 0x01, 0xFE, 0xFE, 0x01, 0xFD, 0xFC, + 0x03, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFE, 0x1F, 0x80, 0x7E, + 0x0F, 0xE7, 0xFE, 0x1F, 0xF8, 0xFE, 0xFF, 0xF3, 0xFF, 0xCF, 0xFF, 0xFF, + 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x83, 0xFF, 0x0F, 0xFF, 0xF0, 0x1F, 0xE0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, + 0xFF, 0xE0, 0x1F, 0xC0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, + 0xC0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xC0, 0x7F, 0xFE, + 0x01, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xC0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, + 0xFF, 0xE0, 0x1F, 0xC0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, + 0xC0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xC0, 0x7F, 0xFE, + 0x01, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xC0, 0x7F, 0xFE, 0x01, 0xFC, 0x07, + 0xF0, 0xFE, 0x1F, 0xC1, 0xFC, 0xFF, 0xE3, 0xFB, 0xFF, 0xE7, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x80, 0xFF, + 0xFE, 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x03, 0xFF, 0xF0, 0x07, 0xFF, + 0xE0, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x3F, 0xFF, 0x00, 0x7F, 0xFE, + 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0xE0, + 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x3F, 0xFF, 0x00, 0x7F, 0xFE, 0x00, + 0xFE, 0x00, 0x7F, 0x80, 0x01, 0xFF, 0xF0, 0x01, 0xFF, 0xFE, 0x01, 0xFF, + 0xFF, 0x81, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xF1, 0xFF, 0x07, 0xFC, 0xFF, + 0x01, 0xFE, 0x7F, 0x00, 0x7F, 0x7F, 0x80, 0x3F, 0xFF, 0x80, 0x0F, 0xFF, + 0xC0, 0x07, 0xFF, 0xE0, 0x03, 0xFF, 0xF0, 0x01, 0xFF, 0xF8, 0x00, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x7F, 0xBF, 0x80, 0x3F, 0x9F, 0xE0, 0x3F, + 0xCF, 0xF8, 0x3F, 0xE3, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, + 0xF0, 0x0F, 0xFF, 0xF0, 0x03, 0xFF, 0xE0, 0x00, 0x3F, 0xC0, 0x00, 0xFE, + 0x1F, 0x80, 0x7F, 0x3F, 0xF0, 0x3F, 0xBF, 0xFE, 0x1F, 0xDF, 0xFF, 0x8F, + 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xF3, 0xFF, 0x07, 0xFD, 0xFF, 0x01, 0xFE, + 0xFF, 0x00, 0x7F, 0x7F, 0x80, 0x3F, 0xFF, 0x80, 0x0F, 0xFF, 0xC0, 0x07, + 0xFF, 0xE0, 0x03, 0xFF, 0xF0, 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0xFC, 0x00, + 0x7F, 0xFF, 0x00, 0x7F, 0xFF, 0x80, 0x3F, 0xBF, 0xE0, 0x3F, 0xDF, 0xF8, + 0x3F, 0xCF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xE3, 0xFB, 0xFF, 0xE1, 0xFD, + 0xFF, 0xF0, 0xFE, 0x7F, 0xE0, 0x7F, 0x0F, 0xC0, 0x3F, 0x80, 0x00, 0x1F, + 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x1F, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x81, 0xFF, 0x9F, 0xC3, 0xFF, + 0xEF, 0xE1, 0xFF, 0xF7, 0xF1, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFD, 0xFF, + 0x07, 0xFE, 0xFF, 0x01, 0xFF, 0x7F, 0x00, 0x7F, 0xFF, 0x80, 0x3F, 0xFF, + 0x80, 0x0F, 0xFF, 0xC0, 0x07, 0xFF, 0xE0, 0x03, 0xFF, 0xF0, 0x01, 0xFF, + 0xF8, 0x00, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x7F, 0xBF, 0x80, 0x3F, + 0xDF, 0xE0, 0x3F, 0xEF, 0xF8, 0x3F, 0xF3, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, + 0xFC, 0x7F, 0xFE, 0xFE, 0x1F, 0xFF, 0x7F, 0x03, 0xFF, 0x3F, 0x80, 0x7E, + 0x1F, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, 0x00, + 0x01, 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x1F, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0xFE, 0x1F, 0xFC, + 0x7F, 0xFB, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0x80, + 0xFF, 0x01, 0xFC, 0x03, 0xF8, 0x07, 0xF0, 0x0F, 0xE0, 0x1F, 0xC0, 0x3F, + 0x80, 0x7F, 0x00, 0xFE, 0x01, 0xFC, 0x03, 0xF8, 0x07, 0xF0, 0x0F, 0xE0, + 0x1F, 0xC0, 0x3F, 0x80, 0x7F, 0x00, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x07, + 0xFF, 0xE0, 0x0F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x7F, + 0x81, 0xFE, 0x7F, 0x00, 0xFE, 0x7F, 0x00, 0xFE, 0x7F, 0xC0, 0x00, 0x7F, + 0xFC, 0x00, 0x7F, 0xFF, 0x80, 0x3F, 0xFF, 0xF0, 0x1F, 0xFF, 0xFC, 0x07, + 0xFF, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x0F, 0xFF, 0x00, 0x01, 0xFF, 0x00, + 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0x7F, 0x00, 0x7F, 0x7F, 0x81, 0xFE, 0x7F, + 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x1F, 0xFF, 0xF8, 0x0F, 0xFF, 0xF0, 0x01, + 0xFF, 0x80, 0x3F, 0x83, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, + 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, + 0x83, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, + 0x83, 0xF8, 0x3F, 0x83, 0xF8, 0x3F, 0x83, 0xFF, 0x3F, 0xF1, 0xFF, 0x0F, + 0xF0, 0x7F, 0xFE, 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x03, 0xFF, 0xF0, + 0x07, 0xFF, 0xE0, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x3F, 0xFF, 0x00, + 0x7F, 0xFE, 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xF8, 0x03, 0xFF, 0xF0, 0x07, + 0xFF, 0xE0, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x3F, 0xFF, 0x00, 0x7F, + 0xFE, 0x00, 0xFF, 0xFC, 0x03, 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x3F, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xDF, 0xFF, 0xBF, 0x9F, 0xFF, 0x7F, 0x1F, + 0xFC, 0xFE, 0x0F, 0xE0, 0x00, 0x7F, 0x00, 0x3F, 0xBF, 0x80, 0x1F, 0x9F, + 0xC0, 0x1F, 0xC7, 0xE0, 0x0F, 0xE3, 0xF8, 0x07, 0xE1, 0xFC, 0x07, 0xF0, + 0x7E, 0x03, 0xF8, 0x3F, 0x81, 0xF8, 0x1F, 0xC0, 0xFC, 0x07, 0xE0, 0xFE, + 0x03, 0xF8, 0x7E, 0x00, 0xFC, 0x3F, 0x00, 0x7E, 0x1F, 0x80, 0x3F, 0x1F, + 0x80, 0x0F, 0xCF, 0xC0, 0x07, 0xE7, 0xE0, 0x03, 0xF7, 0xE0, 0x00, 0xFF, + 0xF0, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xF8, 0x00, 0x0F, 0xFC, 0x00, 0x07, + 0xFE, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0xFC, 0x03, 0xF8, 0x0F, 0xFF, 0xC0, 0x7F, 0x01, 0xFF, 0xF8, 0x0F, 0xE0, + 0x3F, 0x3F, 0x03, 0xFE, 0x07, 0xE7, 0xE0, 0x7F, 0xC1, 0xFC, 0xFE, 0x0F, + 0xF8, 0x3F, 0x9F, 0xC1, 0xFF, 0x07, 0xE1, 0xF8, 0x3D, 0xE0, 0xFC, 0x3F, + 0x0F, 0xBE, 0x3F, 0x87, 0xF1, 0xF7, 0xC7, 0xE0, 0x7E, 0x3E, 0xF8, 0xFC, + 0x0F, 0xC7, 0xDF, 0x1F, 0x81, 0xF9, 0xF1, 0xE3, 0xF0, 0x3F, 0x3E, 0x3E, + 0xFC, 0x03, 0xF7, 0xC7, 0xDF, 0x80, 0x7E, 0xF8, 0xFB, 0xF0, 0x0F, 0xDE, + 0x1F, 0x7C, 0x00, 0xFF, 0xC1, 0xFF, 0x80, 0x1F, 0xF8, 0x3F, 0xF0, 0x03, + 0xFF, 0x07, 0xFE, 0x00, 0x7F, 0xC0, 0xFF, 0x80, 0x07, 0xF8, 0x1F, 0xF0, + 0x00, 0xFF, 0x01, 0xFE, 0x00, 0x1F, 0xE0, 0x3F, 0x80, 0x01, 0xFC, 0x07, + 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x7F, 0x81, 0xFE, 0x3F, 0x81, 0xFC, 0x3F, + 0xC3, 0xFC, 0x1F, 0xC3, 0xF8, 0x0F, 0xE7, 0xF0, 0x0F, 0xEF, 0xF0, 0x07, + 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x03, 0xFF, 0xC0, 0x01, 0xFF, 0x80, 0x00, + 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x03, + 0xFF, 0xC0, 0x07, 0xFF, 0xC0, 0x07, 0xFF, 0xE0, 0x0F, 0xE7, 0xF0, 0x1F, + 0xE7, 0xF0, 0x1F, 0xC3, 0xF8, 0x3F, 0xC3, 0xFC, 0x7F, 0x81, 0xFC, 0x7F, + 0x01, 0xFE, 0xFF, 0x00, 0xFF, 0x7F, 0x00, 0x3F, 0xBF, 0x80, 0x1F, 0xDF, + 0xC0, 0x0F, 0xC7, 0xF0, 0x07, 0xE3, 0xF8, 0x07, 0xF1, 0xFC, 0x03, 0xF0, + 0x7F, 0x01, 0xF8, 0x3F, 0x81, 0xFC, 0x0F, 0xC0, 0xFC, 0x07, 0xF0, 0x7E, + 0x03, 0xF8, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0x7E, 0x1F, 0x80, 0x3F, 0x8F, + 0xC0, 0x0F, 0xCF, 0xC0, 0x07, 0xE7, 0xE0, 0x03, 0xFB, 0xF0, 0x00, 0xFD, + 0xF0, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0xFC, 0x00, 0x0F, 0xFC, 0x00, 0x07, + 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, 0x80, 0x00, + 0x1F, 0xC0, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x03, 0xF0, 0x00, + 0x03, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x0F, 0xFC, 0x00, + 0x07, 0xFC, 0x00, 0x03, 0xFC, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x7F, 0xFF, + 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFE, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xBF, + 0xFF, 0xFC, 0x00, 0x3F, 0xE0, 0x03, 0xFE, 0x00, 0x1F, 0xE0, 0x01, 0xFE, + 0x00, 0x1F, 0xE0, 0x01, 0xFE, 0x00, 0x1F, 0xE0, 0x01, 0xFE, 0x00, 0x1F, + 0xE0, 0x01, 0xFE, 0x00, 0x1F, 0xE0, 0x01, 0xFE, 0x00, 0x1F, 0xE0, 0x01, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xF8, 0x1F, 0xC1, 0xFE, 0x0F, 0xF0, 0xFF, + 0x87, 0xE0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, + 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x3F, + 0x0F, 0xF0, 0x7F, 0x03, 0xF8, 0x1F, 0xE0, 0x1F, 0x80, 0x7C, 0x03, 0xE0, + 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, + 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xE0, 0x3F, 0xE0, 0xFF, 0x07, 0xF8, 0x1F, + 0xC0, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFC, 0x07, 0xF0, 0x3F, 0xC1, 0xFE, 0x0F, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, + 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, + 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x80, 0x7F, 0x81, 0xFC, + 0x0F, 0xE0, 0xFF, 0x0F, 0xC0, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, + 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, + 0x0F, 0xC3, 0xFE, 0x1F, 0xE0, 0xFF, 0x07, 0xF0, 0x3F, 0x00, 0x1F, 0x00, + 0x03, 0xFE, 0x00, 0x1F, 0xF8, 0x0F, 0xFF, 0xF0, 0xFF, 0x0F, 0xFF, 0xF0, + 0x1F, 0xF8, 0x00, 0x7F, 0x80, 0x00, 0xF8}; + +const GFXglyph FreeSansBold24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 13, 0, 1}, // 0x20 ' ' + {0, 7, 34, 16, 5, -33}, // 0x21 '!' + {30, 18, 12, 22, 2, -33}, // 0x22 '"' + {57, 26, 33, 26, 0, -31}, // 0x23 '#' + {165, 25, 40, 26, 1, -34}, // 0x24 '$' + {290, 39, 34, 42, 1, -32}, // 0x25 '%' + {456, 30, 35, 34, 3, -33}, // 0x26 '&' + {588, 7, 12, 12, 3, -33}, // 0x27 ''' + {599, 13, 44, 16, 2, -33}, // 0x28 '(' + {671, 13, 44, 16, 1, -33}, // 0x29 ')' + {743, 15, 15, 18, 1, -33}, // 0x2A '*' + {772, 23, 22, 27, 2, -21}, // 0x2B '+' + {836, 7, 15, 12, 2, -6}, // 0x2C ',' + {850, 13, 6, 16, 1, -15}, // 0x2D '-' + {860, 7, 7, 12, 2, -6}, // 0x2E '.' + {867, 13, 34, 13, 0, -32}, // 0x2F '/' + {923, 24, 35, 26, 1, -33}, // 0x30 '0' + {1028, 14, 33, 26, 4, -32}, // 0x31 '1' + {1086, 23, 34, 26, 2, -33}, // 0x32 '2' + {1184, 23, 35, 26, 2, -33}, // 0x33 '3' + {1285, 22, 33, 26, 2, -32}, // 0x34 '4' + {1376, 23, 34, 26, 2, -32}, // 0x35 '5' + {1474, 23, 35, 26, 2, -33}, // 0x36 '6' + {1575, 23, 33, 26, 1, -32}, // 0x37 '7' + {1670, 24, 35, 26, 1, -33}, // 0x38 '8' + {1775, 24, 35, 26, 1, -33}, // 0x39 '9' + {1880, 7, 25, 12, 2, -24}, // 0x3A ':' + {1902, 7, 33, 12, 2, -24}, // 0x3B ';' + {1931, 23, 23, 27, 2, -22}, // 0x3C '<' + {1998, 23, 18, 27, 2, -19}, // 0x3D '=' + {2050, 23, 23, 27, 2, -22}, // 0x3E '>' + {2117, 24, 35, 29, 3, -34}, // 0x3F '?' + {2222, 43, 41, 46, 1, -34}, // 0x40 '@' + {2443, 32, 34, 33, 0, -33}, // 0x41 'A' + {2579, 27, 34, 33, 4, -33}, // 0x42 'B' + {2694, 30, 36, 34, 2, -34}, // 0x43 'C' + {2829, 28, 34, 34, 4, -33}, // 0x44 'D' + {2948, 25, 34, 31, 4, -33}, // 0x45 'E' + {3055, 24, 34, 30, 4, -33}, // 0x46 'F' + {3157, 31, 36, 36, 2, -34}, // 0x47 'G' + {3297, 27, 34, 35, 4, -33}, // 0x48 'H' + {3412, 7, 34, 15, 4, -33}, // 0x49 'I' + {3442, 22, 35, 27, 1, -33}, // 0x4A 'J' + {3539, 30, 34, 34, 4, -33}, // 0x4B 'K' + {3667, 23, 34, 29, 4, -33}, // 0x4C 'L' + {3765, 33, 34, 41, 4, -33}, // 0x4D 'M' + {3906, 28, 34, 35, 4, -33}, // 0x4E 'N' + {4025, 33, 36, 37, 2, -34}, // 0x4F 'O' + {4174, 26, 34, 32, 4, -33}, // 0x50 'P' + {4285, 33, 37, 37, 2, -34}, // 0x51 'Q' + {4438, 28, 34, 34, 4, -33}, // 0x52 'R' + {4557, 28, 36, 32, 2, -34}, // 0x53 'S' + {4683, 27, 34, 30, 2, -33}, // 0x54 'T' + {4798, 27, 35, 35, 4, -33}, // 0x55 'U' + {4917, 29, 34, 31, 1, -33}, // 0x56 'V' + {5041, 43, 34, 45, 1, -33}, // 0x57 'W' + {5224, 30, 34, 32, 1, -33}, // 0x58 'X' + {5352, 29, 34, 30, 1, -33}, // 0x59 'Y' + {5476, 26, 34, 29, 1, -33}, // 0x5A 'Z' + {5587, 11, 43, 16, 3, -33}, // 0x5B '[' + {5647, 14, 34, 13, -1, -32}, // 0x5C '\' + {5707, 11, 43, 16, 1, -33}, // 0x5D ']' + {5767, 22, 20, 27, 3, -32}, // 0x5E '^' + {5822, 28, 4, 26, -1, 6}, // 0x5F '_' + {5836, 9, 7, 12, 1, -35}, // 0x60 '`' + {5844, 24, 26, 27, 2, -24}, // 0x61 'a' + {5922, 25, 35, 29, 3, -33}, // 0x62 'b' + {6032, 23, 26, 26, 2, -24}, // 0x63 'c' + {6107, 25, 35, 29, 2, -33}, // 0x64 'd' + {6217, 24, 26, 27, 2, -24}, // 0x65 'e' + {6295, 14, 34, 16, 1, -33}, // 0x66 'f' + {6355, 24, 36, 29, 2, -24}, // 0x67 'g' + {6463, 23, 34, 28, 3, -33}, // 0x68 'h' + {6561, 7, 34, 13, 3, -33}, // 0x69 'i' + {6591, 10, 45, 13, 0, -33}, // 0x6A 'j' + {6648, 23, 34, 27, 3, -33}, // 0x6B 'k' + {6746, 7, 34, 13, 3, -33}, // 0x6C 'l' + {6776, 36, 25, 42, 3, -24}, // 0x6D 'm' + {6889, 23, 25, 29, 3, -24}, // 0x6E 'n' + {6961, 25, 26, 29, 2, -24}, // 0x6F 'o' + {7043, 25, 36, 29, 3, -24}, // 0x70 'p' + {7156, 25, 36, 29, 2, -24}, // 0x71 'q' + {7269, 15, 25, 18, 3, -24}, // 0x72 'r' + {7316, 24, 26, 26, 1, -24}, // 0x73 's' + {7394, 12, 32, 16, 2, -30}, // 0x74 't' + {7442, 23, 26, 29, 3, -24}, // 0x75 'u' + {7517, 25, 25, 25, 0, -24}, // 0x76 'v' + {7596, 35, 25, 37, 1, -24}, // 0x77 'w' + {7706, 24, 25, 26, 1, -24}, // 0x78 'x' + {7781, 25, 36, 26, 0, -24}, // 0x79 'y' + {7894, 21, 25, 24, 1, -24}, // 0x7A 'z' + {7960, 13, 43, 18, 2, -33}, // 0x7B '{' + {8030, 4, 44, 13, 5, -33}, // 0x7C '|' + {8052, 13, 43, 18, 3, -33}, // 0x7D '}' + {8122, 21, 8, 23, 1, -14}}; // 0x7E '~' + +const GFXfont FreeSansBold24pt7b PROGMEM = { + (uint8_t *)FreeSansBold24pt7bBitmaps, (GFXglyph *)FreeSansBold24pt7bGlyphs, + 0x20, 0x7E, 56}; + +// Approx. 8815 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold9pt7b.h new file mode 100644 index 0000000..3d850f4 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBold9pt7b.h @@ -0,0 +1,207 @@ +const uint8_t FreeSansBold9pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFE, 0x48, 0x7E, 0xEF, 0xDF, 0xBF, 0x74, 0x40, 0x19, 0x86, + 0x67, 0xFD, 0xFF, 0x33, 0x0C, 0xC3, 0x33, 0xFE, 0xFF, 0x99, 0x86, 0x61, + 0x90, 0x10, 0x1F, 0x1F, 0xDE, 0xFF, 0x3F, 0x83, 0xC0, 0xFC, 0x1F, 0x09, + 0xFC, 0xFE, 0xF7, 0xF1, 0xE0, 0x40, 0x38, 0x10, 0x7C, 0x30, 0xC6, 0x20, + 0xC6, 0x40, 0xC6, 0x40, 0x7C, 0x80, 0x39, 0x9C, 0x01, 0x3E, 0x03, 0x63, + 0x02, 0x63, 0x04, 0x63, 0x0C, 0x3E, 0x08, 0x1C, 0x0E, 0x01, 0xF8, 0x3B, + 0x83, 0xB8, 0x3F, 0x01, 0xE0, 0x3E, 0x67, 0x76, 0xE3, 0xEE, 0x1C, 0xF3, + 0xC7, 0xFE, 0x3F, 0x70, 0xFF, 0xF4, 0x18, 0x63, 0x1C, 0x73, 0x8E, 0x38, + 0xE3, 0x8E, 0x18, 0x70, 0xC3, 0x06, 0x08, 0x61, 0x83, 0x0E, 0x38, 0x71, + 0xC7, 0x1C, 0x71, 0xC6, 0x38, 0xE3, 0x18, 0x40, 0x21, 0x3E, 0x45, 0x28, + 0x38, 0x70, 0xE7, 0xFF, 0xE7, 0x0E, 0x1C, 0xFC, 0x9C, 0xFF, 0xC0, 0xFC, + 0x08, 0xC4, 0x23, 0x10, 0x84, 0x62, 0x11, 0x88, 0x00, 0x3E, 0x3F, 0x9D, + 0xDC, 0x7E, 0x3F, 0x1F, 0x8F, 0xC7, 0xE3, 0xF1, 0xDD, 0xCF, 0xE3, 0xE0, + 0x08, 0xFF, 0xF3, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x80, 0x3E, 0x3F, 0xB8, + 0xFC, 0x70, 0x38, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x0F, 0xF7, 0xF8, + 0x3C, 0x7F, 0xE7, 0xE7, 0x07, 0x0C, 0x0E, 0x07, 0x07, 0xE7, 0xE7, 0x7E, + 0x3C, 0x0E, 0x1E, 0x1E, 0x2E, 0x2E, 0x4E, 0x4E, 0x8E, 0xFF, 0xFF, 0x0E, + 0x0E, 0x0E, 0x7F, 0x3F, 0x90, 0x18, 0x0D, 0xE7, 0xFB, 0x9E, 0x07, 0x03, + 0x81, 0xF1, 0xFF, 0xE7, 0xC0, 0x3E, 0x3F, 0x9C, 0xFC, 0x0E, 0xE7, 0xFB, + 0xDF, 0xC7, 0xE3, 0xF1, 0xDD, 0xEF, 0xE3, 0xE0, 0xFF, 0xFF, 0xC0, 0xE0, + 0xE0, 0x60, 0x70, 0x30, 0x38, 0x1C, 0x0C, 0x0E, 0x07, 0x03, 0x80, 0x3F, + 0x1F, 0xEE, 0x3F, 0x87, 0xE3, 0xCF, 0xC7, 0xFB, 0xCF, 0xE1, 0xF8, 0x7F, + 0x3D, 0xFE, 0x3F, 0x00, 0x3E, 0x3F, 0xBD, 0xDC, 0x7E, 0x3F, 0x1F, 0xDE, + 0xFF, 0x3B, 0x81, 0xF9, 0xCF, 0xE3, 0xC0, 0xFC, 0x00, 0x07, 0xE0, 0xFC, + 0x00, 0x07, 0xE5, 0xE0, 0x00, 0x83, 0xC7, 0xDF, 0x0C, 0x07, 0x80, 0xF8, + 0x1F, 0x01, 0x80, 0xFF, 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x70, + 0x3F, 0x03, 0xE0, 0x38, 0x7D, 0xF1, 0xE0, 0x80, 0x00, 0x3E, 0x3F, 0xB8, + 0xFC, 0x70, 0x38, 0x1C, 0x1C, 0x1C, 0x1C, 0x0E, 0x00, 0x03, 0x81, 0xC0, + 0x03, 0xF0, 0x0F, 0xFC, 0x1E, 0x0E, 0x38, 0x02, 0x70, 0xE9, 0x63, 0x19, + 0xC2, 0x19, 0xC6, 0x11, 0xC6, 0x33, 0xC6, 0x32, 0x63, 0xFE, 0x73, 0xDC, + 0x3C, 0x00, 0x1F, 0xF8, 0x07, 0xF0, 0x07, 0x00, 0xF0, 0x0F, 0x80, 0xF8, + 0x1D, 0x81, 0x9C, 0x19, 0xC3, 0x8C, 0x3F, 0xE7, 0xFE, 0x70, 0x66, 0x07, + 0xE0, 0x70, 0xFF, 0x9F, 0xFB, 0x83, 0xF0, 0x7E, 0x0F, 0xFF, 0x3F, 0xF7, + 0x06, 0xE0, 0xFC, 0x1F, 0x83, 0xFF, 0xEF, 0xF8, 0x1F, 0x83, 0xFE, 0x78, + 0xE7, 0x07, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x07, 0x07, 0x78, + 0xF3, 0xFE, 0x1F, 0x80, 0xFF, 0x8F, 0xFC, 0xE0, 0xEE, 0x0E, 0xE0, 0x7E, + 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x0E, 0xE0, 0xEF, 0xFC, 0xFF, 0x80, + 0xFF, 0xFF, 0xF8, 0x1C, 0x0E, 0x07, 0xFB, 0xFD, 0xC0, 0xE0, 0x70, 0x38, + 0x1F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF8, 0x1C, 0x0E, 0x07, 0xFB, 0xFD, 0xC0, + 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x00, 0x0F, 0x87, 0xF9, 0xE3, 0xB8, 0x3E, + 0x01, 0xC0, 0x38, 0xFF, 0x1F, 0xE0, 0x6E, 0x0D, 0xE3, 0x9F, 0xD0, 0xF2, + 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xFF, 0xFF, 0xFF, 0x07, 0xE0, + 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xE7, 0xE7, 0xE7, 0x7E, 0x3C, + 0xE0, 0xEE, 0x1C, 0xE3, 0x8E, 0x70, 0xEE, 0x0F, 0xC0, 0xFE, 0x0F, 0x70, + 0xE7, 0x0E, 0x38, 0xE1, 0xCE, 0x0E, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xFF, 0xFF, 0xF8, 0x7F, 0xE1, + 0xFF, 0x87, 0xFE, 0x1F, 0xEC, 0x7F, 0xB3, 0x7E, 0xCD, 0xFB, 0x37, 0xEC, + 0xDF, 0x9E, 0x7E, 0x79, 0xF9, 0xE7, 0xE7, 0x9C, 0xE0, 0xFE, 0x1F, 0xC3, + 0xFC, 0x7F, 0xCF, 0xD9, 0xFB, 0xBF, 0x37, 0xE7, 0xFC, 0x7F, 0x87, 0xF0, + 0xFE, 0x0E, 0x0F, 0x81, 0xFF, 0x1E, 0x3C, 0xE0, 0xEE, 0x03, 0xF0, 0x1F, + 0x80, 0xFC, 0x07, 0xE0, 0x3B, 0x83, 0x9E, 0x3C, 0x7F, 0xC0, 0xF8, 0x00, + 0xFF, 0x9F, 0xFB, 0x87, 0xF0, 0x7E, 0x0F, 0xC3, 0xFF, 0xF7, 0xFC, 0xE0, + 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x00, 0x0F, 0x81, 0xFF, 0x1E, 0x3C, 0xE0, + 0xEE, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE1, 0xBB, 0x8F, 0x9E, 0x3C, + 0x7F, 0xE0, 0xFB, 0x80, 0x08, 0xFF, 0x8F, 0xFC, 0xE0, 0xEE, 0x0E, 0xE0, + 0xEE, 0x0E, 0xFF, 0xCF, 0xFC, 0xE0, 0xEE, 0x0E, 0xE0, 0xEE, 0x0E, 0xE0, + 0xF0, 0x3F, 0x0F, 0xFB, 0xC7, 0xF0, 0x7E, 0x01, 0xFC, 0x1F, 0xF0, 0x3F, + 0x00, 0xFC, 0x1D, 0xC7, 0xBF, 0xE1, 0xF8, 0xFF, 0xFF, 0xC7, 0x03, 0x81, + 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0xFC, + 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, + 0xC7, 0xBF, 0xE1, 0xF0, 0x60, 0x67, 0x0E, 0x70, 0xE3, 0x0C, 0x30, 0xC3, + 0x9C, 0x19, 0x81, 0x98, 0x1F, 0x80, 0xF0, 0x0F, 0x00, 0xF0, 0x06, 0x00, + 0x61, 0xC3, 0xB8, 0xE1, 0x9C, 0x70, 0xCE, 0x3C, 0xE3, 0x36, 0x71, 0x9B, + 0x30, 0xED, 0x98, 0x36, 0x7C, 0x1B, 0x3C, 0x0F, 0x1E, 0x07, 0x8F, 0x01, + 0xC3, 0x80, 0xE1, 0x80, 0x70, 0xE7, 0x8E, 0x39, 0xC1, 0xF8, 0x1F, 0x80, + 0xF0, 0x07, 0x00, 0xF0, 0x1F, 0x81, 0x9C, 0x39, 0xC7, 0x0E, 0x70, 0xE0, + 0xE0, 0xFC, 0x39, 0xC7, 0x18, 0xC3, 0xB8, 0x36, 0x07, 0xC0, 0x70, 0x0E, + 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0xFF, 0xFF, 0xC0, 0xE0, 0xE0, 0xF0, + 0x70, 0x70, 0x70, 0x78, 0x38, 0x38, 0x1F, 0xFF, 0xF8, 0xFF, 0xEE, 0xEE, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xF0, 0x86, 0x10, 0x86, 0x10, 0x84, 0x30, + 0x84, 0x30, 0x80, 0xFF, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, 0xF0, + 0x18, 0x1C, 0x3C, 0x3E, 0x36, 0x66, 0x63, 0xC3, 0xFF, 0xC0, 0xCC, 0x3F, + 0x1F, 0xEE, 0x38, 0x0E, 0x3F, 0x9E, 0xEE, 0x3B, 0x9E, 0xFF, 0x9E, 0xE0, + 0xE0, 0x38, 0x0E, 0x03, 0xBC, 0xFF, 0xBC, 0xEE, 0x1F, 0x87, 0xE1, 0xF8, + 0x7F, 0x3B, 0xFE, 0xEF, 0x00, 0x1F, 0x3F, 0xDC, 0x7C, 0x0E, 0x07, 0x03, + 0x80, 0xE3, 0x7F, 0x8F, 0x00, 0x03, 0x81, 0xC0, 0xE7, 0x77, 0xFB, 0xBF, + 0x8F, 0xC7, 0xE3, 0xF1, 0xFD, 0xEF, 0xF3, 0xB8, 0x3E, 0x3F, 0x9C, 0xDC, + 0x3F, 0xFF, 0xFF, 0x81, 0xC3, 0x7F, 0x8F, 0x00, 0x3B, 0xDD, 0xFF, 0xB9, + 0xCE, 0x73, 0x9C, 0xE7, 0x00, 0x3B, 0xBF, 0xDD, 0xFC, 0x7E, 0x3F, 0x1F, + 0x8F, 0xEF, 0x7F, 0x9D, 0xC0, 0xFC, 0x77, 0xF1, 0xF0, 0xE0, 0x70, 0x38, + 0x1D, 0xEF, 0xFF, 0x9F, 0x8F, 0xC7, 0xE3, 0xF1, 0xF8, 0xFC, 0x7E, 0x38, + 0xFC, 0x7F, 0xFF, 0xFF, 0xFE, 0x77, 0x07, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x7F, 0xE0, 0xE0, 0x70, 0x38, 0x1C, 0x7E, 0x77, 0x73, 0xF1, 0xF8, 0xFE, + 0x77, 0x39, 0xDC, 0x6E, 0x38, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0x7B, + 0xFF, 0xFE, 0x39, 0xF8, 0xE7, 0xE3, 0x9F, 0x8E, 0x7E, 0x39, 0xF8, 0xE7, + 0xE3, 0x9F, 0x8E, 0x70, 0xEF, 0x7F, 0xF8, 0xFC, 0x7E, 0x3F, 0x1F, 0x8F, + 0xC7, 0xE3, 0xF1, 0xC0, 0x1E, 0x1F, 0xE7, 0x3B, 0x87, 0xE1, 0xF8, 0x7E, + 0x1D, 0xCE, 0x7F, 0x87, 0x80, 0xEF, 0x3F, 0xEF, 0x3B, 0x87, 0xE1, 0xF8, + 0x7E, 0x1F, 0xCE, 0xFF, 0xBB, 0xCE, 0x03, 0x80, 0xE0, 0x38, 0x00, 0x3B, + 0xBF, 0xFD, 0xFC, 0x7E, 0x3F, 0x1F, 0x8F, 0xEF, 0x7F, 0x9D, 0xC0, 0xE0, + 0x70, 0x38, 0x1C, 0xEF, 0xFF, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x80, 0x3E, + 0x3F, 0xB8, 0xFC, 0x0F, 0xC3, 0xFC, 0x3F, 0xC7, 0xFF, 0x1F, 0x00, 0x73, + 0xBF, 0xF7, 0x39, 0xCE, 0x73, 0x9E, 0x70, 0xE3, 0xF1, 0xF8, 0xFC, 0x7E, + 0x3F, 0x1F, 0x8F, 0xC7, 0xFF, 0xBD, 0xC0, 0xE1, 0x98, 0x67, 0x39, 0xCC, + 0x33, 0x0D, 0xC3, 0xE0, 0x78, 0x1E, 0x07, 0x00, 0xE3, 0x1D, 0x9E, 0x66, + 0x79, 0x99, 0xE6, 0x77, 0xB8, 0xD2, 0xC3, 0xCF, 0x0F, 0x3C, 0x3C, 0xF0, + 0x73, 0x80, 0x73, 0x9C, 0xE3, 0xF0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0xFC, + 0x73, 0x9C, 0xE0, 0xE1, 0xD8, 0x67, 0x39, 0xCE, 0x33, 0x0E, 0xC3, 0xE0, + 0x78, 0x1E, 0x03, 0x00, 0xC0, 0x70, 0x38, 0x0E, 0x00, 0xFE, 0xFE, 0x0E, + 0x1C, 0x38, 0x38, 0x70, 0xE0, 0xFF, 0xFF, 0x37, 0x66, 0x66, 0x6E, 0xE6, + 0x66, 0x66, 0x67, 0x30, 0xFF, 0xFF, 0x80, 0xCE, 0x66, 0x66, 0x67, 0x76, + 0x66, 0x66, 0x6E, 0xC0, 0x71, 0x8E}; + +const GFXglyph FreeSansBold9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 3, 13, 6, 2, -12}, // 0x21 '!' + {5, 7, 5, 9, 1, -12}, // 0x22 '"' + {10, 10, 12, 10, 0, -11}, // 0x23 '#' + {25, 9, 15, 10, 1, -13}, // 0x24 '$' + {42, 16, 13, 16, 0, -12}, // 0x25 '%' + {68, 12, 13, 13, 1, -12}, // 0x26 '&' + {88, 3, 5, 5, 1, -12}, // 0x27 ''' + {90, 6, 17, 6, 1, -12}, // 0x28 '(' + {103, 6, 17, 6, 0, -12}, // 0x29 ')' + {116, 5, 6, 7, 1, -12}, // 0x2A '*' + {120, 7, 8, 11, 2, -7}, // 0x2B '+' + {127, 3, 5, 4, 1, -1}, // 0x2C ',' + {129, 5, 2, 6, 0, -5}, // 0x2D '-' + {131, 3, 2, 4, 1, -1}, // 0x2E '.' + {132, 5, 13, 5, 0, -12}, // 0x2F '/' + {141, 9, 13, 10, 1, -12}, // 0x30 '0' + {156, 5, 13, 10, 2, -12}, // 0x31 '1' + {165, 9, 13, 10, 1, -12}, // 0x32 '2' + {180, 8, 13, 10, 1, -12}, // 0x33 '3' + {193, 8, 13, 10, 2, -12}, // 0x34 '4' + {206, 9, 13, 10, 1, -12}, // 0x35 '5' + {221, 9, 13, 10, 1, -12}, // 0x36 '6' + {236, 9, 13, 10, 0, -12}, // 0x37 '7' + {251, 10, 13, 10, 0, -12}, // 0x38 '8' + {268, 9, 13, 10, 1, -12}, // 0x39 '9' + {283, 3, 9, 4, 1, -8}, // 0x3A ':' + {287, 3, 12, 4, 1, -8}, // 0x3B ';' + {292, 9, 9, 11, 1, -8}, // 0x3C '<' + {303, 9, 6, 11, 1, -6}, // 0x3D '=' + {310, 9, 9, 11, 1, -8}, // 0x3E '>' + {321, 9, 13, 11, 1, -12}, // 0x3F '?' + {336, 16, 15, 18, 0, -12}, // 0x40 '@' + {366, 12, 13, 13, 0, -12}, // 0x41 'A' + {386, 11, 13, 13, 1, -12}, // 0x42 'B' + {404, 12, 13, 13, 1, -12}, // 0x43 'C' + {424, 12, 13, 13, 1, -12}, // 0x44 'D' + {444, 9, 13, 12, 1, -12}, // 0x45 'E' + {459, 9, 13, 11, 1, -12}, // 0x46 'F' + {474, 11, 13, 14, 1, -12}, // 0x47 'G' + {492, 11, 13, 13, 1, -12}, // 0x48 'H' + {510, 3, 13, 6, 1, -12}, // 0x49 'I' + {515, 8, 13, 10, 1, -12}, // 0x4A 'J' + {528, 12, 13, 13, 1, -12}, // 0x4B 'K' + {548, 8, 13, 11, 1, -12}, // 0x4C 'L' + {561, 14, 13, 16, 1, -12}, // 0x4D 'M' + {584, 11, 13, 14, 1, -12}, // 0x4E 'N' + {602, 13, 13, 14, 1, -12}, // 0x4F 'O' + {624, 11, 13, 12, 1, -12}, // 0x50 'P' + {642, 13, 14, 14, 1, -12}, // 0x51 'Q' + {665, 12, 13, 13, 1, -12}, // 0x52 'R' + {685, 11, 13, 12, 1, -12}, // 0x53 'S' + {703, 9, 13, 12, 2, -12}, // 0x54 'T' + {718, 11, 13, 13, 1, -12}, // 0x55 'U' + {736, 12, 13, 12, 0, -12}, // 0x56 'V' + {756, 17, 13, 17, 0, -12}, // 0x57 'W' + {784, 12, 13, 12, 0, -12}, // 0x58 'X' + {804, 11, 13, 12, 1, -12}, // 0x59 'Y' + {822, 9, 13, 11, 1, -12}, // 0x5A 'Z' + {837, 4, 17, 6, 1, -12}, // 0x5B '[' + {846, 5, 13, 5, 0, -12}, // 0x5C '\' + {855, 4, 17, 6, 0, -12}, // 0x5D ']' + {864, 8, 8, 11, 1, -12}, // 0x5E '^' + {872, 10, 1, 10, 0, 4}, // 0x5F '_' + {874, 3, 2, 5, 0, -12}, // 0x60 '`' + {875, 10, 10, 10, 1, -9}, // 0x61 'a' + {888, 10, 13, 11, 1, -12}, // 0x62 'b' + {905, 9, 10, 10, 1, -9}, // 0x63 'c' + {917, 9, 13, 11, 1, -12}, // 0x64 'd' + {932, 9, 10, 10, 1, -9}, // 0x65 'e' + {944, 5, 13, 6, 1, -12}, // 0x66 'f' + {953, 9, 14, 11, 1, -9}, // 0x67 'g' + {969, 9, 13, 11, 1, -12}, // 0x68 'h' + {984, 3, 13, 5, 1, -12}, // 0x69 'i' + {989, 4, 17, 5, 0, -12}, // 0x6A 'j' + {998, 9, 13, 10, 1, -12}, // 0x6B 'k' + {1013, 3, 13, 5, 1, -12}, // 0x6C 'l' + {1018, 14, 10, 16, 1, -9}, // 0x6D 'm' + {1036, 9, 10, 11, 1, -9}, // 0x6E 'n' + {1048, 10, 10, 11, 1, -9}, // 0x6F 'o' + {1061, 10, 14, 11, 1, -9}, // 0x70 'p' + {1079, 9, 14, 11, 1, -9}, // 0x71 'q' + {1095, 6, 10, 7, 1, -9}, // 0x72 'r' + {1103, 9, 10, 10, 1, -9}, // 0x73 's' + {1115, 5, 12, 6, 1, -11}, // 0x74 't' + {1123, 9, 10, 11, 1, -9}, // 0x75 'u' + {1135, 10, 10, 10, 0, -9}, // 0x76 'v' + {1148, 14, 10, 14, 0, -9}, // 0x77 'w' + {1166, 10, 10, 10, 0, -9}, // 0x78 'x' + {1179, 10, 14, 10, 0, -9}, // 0x79 'y' + {1197, 8, 10, 9, 1, -9}, // 0x7A 'z' + {1207, 4, 17, 7, 1, -12}, // 0x7B '{' + {1216, 1, 17, 5, 2, -12}, // 0x7C '|' + {1219, 4, 17, 7, 2, -12}, // 0x7D '}' + {1228, 8, 2, 9, 0, -4}}; // 0x7E '~' + +const GFXfont FreeSansBold9pt7b PROGMEM = {(uint8_t *)FreeSansBold9pt7bBitmaps, + (GFXglyph *)FreeSansBold9pt7bGlyphs, + 0x20, 0x7E, 22}; + +// Approx. 1902 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique12pt7b.h new file mode 100644 index 0000000..f7c769c --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique12pt7b.h @@ -0,0 +1,316 @@ +const uint8_t FreeSansBoldOblique12pt7bBitmaps[] PROGMEM = { + 0x1C, 0x3C, 0x78, 0xE1, 0xC3, 0x8F, 0x1C, 0x38, 0x70, 0xC1, 0x83, 0x00, + 0x1C, 0x78, 0xF0, 0x71, 0xFC, 0xFE, 0x3B, 0x8E, 0xC3, 0x30, 0xC0, 0x01, + 0x8C, 0x07, 0x38, 0x0C, 0x61, 0xFF, 0xF3, 0xFF, 0xE7, 0xFF, 0x83, 0x9C, + 0x0E, 0x70, 0x1C, 0xE1, 0xFF, 0xF3, 0xFF, 0xC7, 0xFF, 0x83, 0x18, 0x0E, + 0x70, 0x18, 0xC0, 0x73, 0x80, 0x00, 0x40, 0x07, 0xF0, 0x3F, 0xF0, 0xFF, + 0xF3, 0xC9, 0xE7, 0xB3, 0xCF, 0x60, 0x1F, 0xC0, 0x3F, 0xC0, 0x3F, 0xE0, + 0x1F, 0xE0, 0x1B, 0xE0, 0x33, 0xDE, 0x47, 0xBC, 0x8F, 0x7F, 0x7C, 0x7F, + 0xF0, 0x7F, 0x80, 0x18, 0x00, 0x20, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x87, + 0x80, 0xC3, 0xF0, 0x61, 0xFE, 0x10, 0xE1, 0x8C, 0x30, 0x66, 0x0C, 0x3B, + 0x03, 0xFC, 0x80, 0x7E, 0x60, 0x0F, 0x30, 0x00, 0x18, 0x70, 0x0C, 0x7E, + 0x03, 0x1F, 0xC1, 0x8E, 0x30, 0xC3, 0x1C, 0x60, 0xFE, 0x18, 0x1F, 0x8C, + 0x07, 0x80, 0x01, 0xE0, 0x07, 0xF0, 0x1F, 0xE0, 0x79, 0xC0, 0xF3, 0x81, + 0xEE, 0x01, 0xF8, 0x01, 0xE0, 0x1F, 0xC6, 0x7B, 0xDD, 0xE3, 0xF7, 0x87, + 0xEF, 0x07, 0x9F, 0x1F, 0x3F, 0xFF, 0x3F, 0xDE, 0x3F, 0x1C, 0x7F, 0xEE, + 0xCC, 0x03, 0x83, 0x81, 0x81, 0xC1, 0xC0, 0xE0, 0xE0, 0x70, 0x70, 0x38, + 0x3C, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x18, 0x0E, 0x07, + 0x01, 0x80, 0x06, 0x03, 0x81, 0xC0, 0x60, 0x38, 0x1C, 0x0E, 0x07, 0x03, + 0x81, 0xC0, 0xE0, 0xE0, 0x70, 0x38, 0x38, 0x1C, 0x1C, 0x0E, 0x0E, 0x06, + 0x07, 0x07, 0x00, 0x0C, 0x0C, 0x4F, 0xFF, 0x1C, 0x3C, 0x6C, 0x44, 0x03, + 0x80, 0x38, 0x07, 0x00, 0x70, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0xE0, 0x0E, + 0x00, 0xE0, 0x0C, 0x00, 0x7B, 0xDC, 0x23, 0x33, 0x00, 0x7F, 0xFF, 0xF0, + 0x7F, 0xE0, 0x00, 0xC0, 0x30, 0x18, 0x04, 0x03, 0x00, 0x80, 0x60, 0x10, + 0x0C, 0x02, 0x01, 0x80, 0x40, 0x30, 0x08, 0x06, 0x01, 0x00, 0xC0, 0x00, + 0x03, 0xC0, 0x7F, 0x87, 0xFC, 0x78, 0xF3, 0xC7, 0xBC, 0x3D, 0xE1, 0xEF, + 0x0F, 0xF0, 0x7F, 0x87, 0xBC, 0x3D, 0xE1, 0xEF, 0x1E, 0x78, 0xF3, 0xFF, + 0x0F, 0xF0, 0x3E, 0x00, 0x03, 0x83, 0x83, 0xCF, 0xEF, 0xF0, 0x78, 0x38, + 0x1C, 0x0E, 0x0F, 0x07, 0x03, 0x81, 0xC1, 0xE0, 0xF0, 0x70, 0x38, 0x00, + 0x03, 0xF0, 0x0F, 0xF8, 0x7F, 0xF8, 0xF1, 0xF3, 0xC1, 0xE7, 0x83, 0xC0, + 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xE0, 0x0F, 0x00, 0x7C, 0x01, 0xE0, + 0x07, 0x00, 0x1F, 0xFC, 0x3F, 0xF8, 0xFF, 0xF0, 0x07, 0xE0, 0xFF, 0x8F, + 0xFE, 0xF8, 0xF7, 0x87, 0x80, 0x78, 0x0F, 0x80, 0xFC, 0x07, 0xE0, 0x0F, + 0x80, 0x3C, 0x01, 0xEF, 0x0F, 0x78, 0xF3, 0xFF, 0x8F, 0xF8, 0x3F, 0x00, + 0x00, 0x78, 0x07, 0xC0, 0x7E, 0x03, 0xF0, 0x37, 0x03, 0x38, 0x31, 0xC3, + 0x9E, 0x38, 0xF1, 0x87, 0x1F, 0xFE, 0xFF, 0xF7, 0xFF, 0x80, 0xF0, 0x07, + 0x00, 0x38, 0x03, 0xC0, 0x07, 0xFC, 0x1F, 0xF0, 0xFF, 0xC3, 0x00, 0x1C, + 0x00, 0x7F, 0x81, 0xFF, 0x0F, 0xFE, 0x38, 0xF8, 0x01, 0xE0, 0x07, 0x80, + 0x1E, 0xF0, 0xF3, 0xC7, 0xCF, 0xFE, 0x1F, 0xF0, 0x3F, 0x00, 0x03, 0xE0, + 0x7F, 0x87, 0xFE, 0x78, 0xF3, 0xC0, 0x3D, 0xE1, 0xFF, 0x8F, 0xFE, 0xF8, + 0xF7, 0xC7, 0xBC, 0x3D, 0xE1, 0xEF, 0x1E, 0x7C, 0xF3, 0xFF, 0x0F, 0xF0, + 0x1F, 0x00, 0x7F, 0xFB, 0xFF, 0xDF, 0xFE, 0x00, 0xE0, 0x0E, 0x00, 0xE0, + 0x0E, 0x00, 0xE0, 0x0F, 0x00, 0x70, 0x07, 0x00, 0x78, 0x03, 0x80, 0x3C, + 0x01, 0xC0, 0x0E, 0x00, 0xF0, 0x00, 0x03, 0xF0, 0x1F, 0xE0, 0xFF, 0xC7, + 0x8F, 0x1C, 0x3C, 0x71, 0xE0, 0xFF, 0x03, 0xF8, 0x3F, 0xF1, 0xF1, 0xE7, + 0x87, 0xBC, 0x1E, 0xF0, 0x7B, 0xE3, 0xCF, 0xFF, 0x1F, 0xF8, 0x1F, 0x80, + 0x03, 0xE0, 0x3F, 0xE1, 0xFF, 0x8F, 0x9F, 0x3C, 0x3D, 0xE0, 0xF7, 0x83, + 0xDE, 0x1F, 0x78, 0xFD, 0xFF, 0xE3, 0xFF, 0x87, 0xDE, 0x00, 0xF3, 0xC7, + 0x8F, 0xFE, 0x1F, 0xF0, 0x3F, 0x00, 0x1C, 0xF3, 0x80, 0x00, 0x00, 0x00, + 0x01, 0xCF, 0x38, 0x0E, 0x3C, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, + 0xE3, 0x81, 0x06, 0x18, 0x60, 0x00, 0x00, 0x01, 0xC0, 0x7E, 0x1F, 0xE7, + 0xF8, 0x7E, 0x03, 0xE0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x00, 0x78, 0x00, + 0xC0, 0x3F, 0xFC, 0xFF, 0xF3, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, + 0xFF, 0x9F, 0xFC, 0x7F, 0xF0, 0x30, 0x01, 0xE0, 0x0F, 0xE0, 0x3F, 0xC0, + 0x7F, 0x80, 0x7C, 0x07, 0xE1, 0xFE, 0x7F, 0x87, 0xE0, 0x38, 0x00, 0x00, + 0x00, 0x0F, 0xC1, 0xFF, 0x8F, 0xFC, 0xF1, 0xFF, 0x07, 0xF0, 0x3C, 0x01, + 0xE0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0, 0x1C, 0x01, 0xC0, 0x0E, 0x00, + 0x00, 0x07, 0x80, 0x3C, 0x01, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x03, 0xFF, + 0x80, 0x3C, 0x0F, 0x01, 0xC0, 0x0E, 0x0E, 0x00, 0x1C, 0x70, 0xF7, 0x73, + 0x87, 0xF8, 0xCC, 0x31, 0xE3, 0x61, 0x87, 0x0D, 0x8C, 0x1C, 0x3C, 0x30, + 0x61, 0xB1, 0x81, 0x86, 0xC6, 0x0C, 0x3B, 0x18, 0x71, 0xCC, 0x63, 0xCE, + 0x31, 0xFB, 0xF0, 0xE3, 0xCF, 0x01, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x07, + 0xFF, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x1F, 0x80, + 0x1F, 0xC0, 0x0F, 0xE0, 0x0F, 0xF0, 0x07, 0x7C, 0x07, 0x1E, 0x03, 0x8F, + 0x03, 0x87, 0x83, 0xC3, 0xC1, 0xFF, 0xE1, 0xFF, 0xF0, 0xFF, 0xFC, 0xF0, + 0x1E, 0x70, 0x0F, 0x78, 0x07, 0xB8, 0x03, 0xC0, 0x0F, 0xFE, 0x0F, 0xFF, + 0x87, 0xFF, 0xE3, 0xC0, 0xF1, 0xC0, 0x78, 0xE0, 0x3C, 0xF0, 0x3C, 0x7F, + 0xFC, 0x3F, 0xFC, 0x1F, 0xFF, 0x0E, 0x07, 0xCF, 0x01, 0xE7, 0x80, 0xF3, + 0x80, 0x79, 0xC0, 0x79, 0xFF, 0xF8, 0xFF, 0xFC, 0x7F, 0xF8, 0x00, 0x01, + 0xF8, 0x03, 0xFF, 0x03, 0xFF, 0xC3, 0xE1, 0xF3, 0xC0, 0x79, 0xE0, 0x3D, + 0xE0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, + 0x0F, 0x00, 0xE7, 0x80, 0xF3, 0xE0, 0xF0, 0xFF, 0xF8, 0x3F, 0xF0, 0x07, + 0xE0, 0x00, 0x1F, 0xFC, 0x0F, 0xFF, 0x87, 0xFF, 0xC3, 0x81, 0xF1, 0xC0, + 0x79, 0xE0, 0x3C, 0xF0, 0x1E, 0x78, 0x0F, 0x38, 0x07, 0x9C, 0x03, 0xDE, + 0x03, 0xCF, 0x01, 0xE7, 0x81, 0xF3, 0x80, 0xF1, 0xC1, 0xF1, 0xFF, 0xF0, + 0xFF, 0xF0, 0x7F, 0xE0, 0x00, 0x0F, 0xFF, 0x1F, 0xFF, 0x1F, 0xFF, 0x1C, + 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, + 0xFC, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x70, 0x00, 0x70, 0x00, 0xFF, + 0xF8, 0xFF, 0xF8, 0xFF, 0xF8, 0x1F, 0xFF, 0x1F, 0xFE, 0x1F, 0xFE, 0x1C, + 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3F, 0xF8, 0x3F, 0xF8, 0x3F, + 0xF8, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x70, 0x00, 0xF0, 0x00, 0xF0, + 0x00, 0xF0, 0x00, 0xE0, 0x00, 0x01, 0xFC, 0x03, 0xFF, 0x03, 0xFF, 0xC3, + 0xE0, 0xF3, 0xC0, 0x39, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0xF0, 0x7F, + 0x78, 0x3F, 0xBC, 0x1F, 0xDE, 0x01, 0xCF, 0x00, 0xE7, 0xC0, 0xF1, 0xF0, + 0xF8, 0xFF, 0xFC, 0x3F, 0xEC, 0x07, 0xE6, 0x00, 0x1E, 0x03, 0x8F, 0x01, + 0xC7, 0x01, 0xE3, 0x80, 0xF3, 0xC0, 0x79, 0xE0, 0x38, 0xF0, 0x1C, 0x7F, + 0xFE, 0x3F, 0xFF, 0x3F, 0xFF, 0x9E, 0x03, 0x8F, 0x01, 0xC7, 0x01, 0xE3, + 0x80, 0xF3, 0xC0, 0x71, 0xE0, 0x38, 0xF0, 0x3C, 0x70, 0x1E, 0x00, 0x1E, + 0x3C, 0x78, 0xE1, 0xC7, 0x8F, 0x1E, 0x38, 0x71, 0xE3, 0xC7, 0x8E, 0x1C, + 0x78, 0xF1, 0xE0, 0x00, 0x1C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x38, + 0x00, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xC0, 0x07, 0x3C, 0x3C, + 0xF0, 0xF3, 0xC3, 0x8F, 0x1E, 0x3F, 0xF8, 0x7F, 0xC0, 0xFC, 0x00, 0x1E, + 0x07, 0xC7, 0x83, 0xE1, 0xE1, 0xE0, 0x70, 0xF0, 0x1C, 0x78, 0x0F, 0x3C, + 0x03, 0xDE, 0x00, 0xFF, 0x00, 0x3F, 0xC0, 0x0F, 0xF0, 0x07, 0xDE, 0x01, + 0xE7, 0xC0, 0x78, 0xF0, 0x1C, 0x3E, 0x0F, 0x07, 0x83, 0xC0, 0xF0, 0xF0, + 0x3C, 0x38, 0x07, 0x80, 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xC0, + 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x38, 0x01, 0xC0, 0x1E, 0x00, 0xF0, 0x07, + 0x80, 0x38, 0x01, 0xC0, 0x1F, 0xFE, 0xFF, 0xF7, 0xFF, 0x80, 0x1F, 0x03, + 0xF1, 0xF0, 0x3F, 0x1F, 0x07, 0xF1, 0xF0, 0x7F, 0x3F, 0x0F, 0xE3, 0xF0, + 0xEE, 0x3B, 0x1E, 0xE3, 0xB1, 0xDE, 0x3B, 0x1D, 0xE7, 0xB3, 0x9C, 0x7B, + 0x39, 0xC7, 0x37, 0x9C, 0x73, 0x73, 0xCF, 0x3F, 0x3C, 0xF3, 0xE3, 0x8F, + 0x3E, 0x38, 0xE3, 0xC3, 0x8E, 0x3C, 0x78, 0x1E, 0x03, 0x87, 0xC0, 0xE1, + 0xF0, 0x38, 0x7C, 0x1E, 0x1F, 0x87, 0x8F, 0xE1, 0xC3, 0xB8, 0x70, 0xEF, + 0x1C, 0x39, 0xCF, 0x1E, 0x73, 0xC7, 0x8E, 0xE1, 0xC3, 0xB8, 0x70, 0xEE, + 0x1C, 0x1F, 0x8F, 0x07, 0xE3, 0xC1, 0xF0, 0xE0, 0x3C, 0x38, 0x0F, 0x00, + 0x01, 0xF8, 0x03, 0xFF, 0x03, 0xFF, 0xC3, 0xE3, 0xE3, 0xC0, 0xF9, 0xE0, + 0x3D, 0xE0, 0x1E, 0xF0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x03, 0xDE, + 0x01, 0xEF, 0x00, 0xF7, 0xC0, 0xF1, 0xF0, 0xF0, 0xFF, 0xF0, 0x3F, 0xF0, + 0x07, 0xE0, 0x00, 0x1F, 0xFC, 0x1F, 0xFE, 0x1F, 0xFF, 0x1C, 0x1F, 0x1C, + 0x0F, 0x3C, 0x0F, 0x3C, 0x0F, 0x3C, 0x1E, 0x3F, 0xFC, 0x3F, 0xFC, 0x7F, + 0xF0, 0x78, 0x00, 0x78, 0x00, 0x70, 0x00, 0x70, 0x00, 0xF0, 0x00, 0xF0, + 0x00, 0xF0, 0x00, 0x01, 0xF8, 0x03, 0xFF, 0x03, 0xFF, 0xC3, 0xE3, 0xE3, + 0xC0, 0xF9, 0xC0, 0x3D, 0xE0, 0x1E, 0xF0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, + 0xFC, 0x03, 0xDE, 0x09, 0xEF, 0x0E, 0xE7, 0xC7, 0xF1, 0xF1, 0xF0, 0xFF, + 0xF8, 0x3F, 0xFE, 0x07, 0xE6, 0x00, 0x02, 0x00, 0x0F, 0xFE, 0x0F, 0xFF, + 0x87, 0xFF, 0xE3, 0x81, 0xF1, 0xC0, 0x78, 0xE0, 0x3C, 0xF0, 0x1C, 0x78, + 0x1E, 0x3F, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, 0x8F, 0x03, 0xC7, 0x81, 0xE3, + 0x80, 0xF1, 0xC0, 0xF1, 0xE0, 0x78, 0xF0, 0x3C, 0x78, 0x1F, 0x00, 0x03, + 0xF8, 0x0F, 0xFE, 0x1F, 0xFF, 0x1E, 0x1F, 0x3C, 0x0F, 0x3C, 0x0F, 0x3C, + 0x00, 0x3F, 0x00, 0x1F, 0xF0, 0x0F, 0xFC, 0x01, 0xFE, 0x00, 0x3E, 0xF0, + 0x1E, 0xF0, 0x1E, 0xF8, 0x3C, 0x7F, 0xF8, 0x7F, 0xF0, 0x1F, 0xC0, 0x7F, + 0xFE, 0xFF, 0xFD, 0xFF, 0xF8, 0x1C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, + 0x03, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xE0, 0x01, + 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x38, 0x00, 0x1E, 0x07, 0x1C, + 0x0F, 0x3C, 0x0F, 0x3C, 0x0F, 0x3C, 0x0E, 0x38, 0x0E, 0x78, 0x1E, 0x78, + 0x1E, 0x78, 0x1E, 0x78, 0x1C, 0x70, 0x1C, 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, + 0x38, 0xF8, 0x78, 0xFF, 0xF0, 0x7F, 0xE0, 0x1F, 0x80, 0xF0, 0x1F, 0xE0, + 0x39, 0xC0, 0xF3, 0x81, 0xC7, 0x07, 0x8E, 0x0E, 0x1C, 0x3C, 0x3C, 0x70, + 0x79, 0xE0, 0xF3, 0x80, 0xEF, 0x01, 0xDC, 0x03, 0xB8, 0x07, 0xE0, 0x0F, + 0x80, 0x1F, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x70, 0x7F, 0x87, 0x83, + 0xFC, 0x3C, 0x3D, 0xE1, 0xE1, 0xEF, 0x1F, 0x0E, 0x78, 0xD8, 0xF3, 0xC6, + 0xC7, 0x0E, 0x76, 0x78, 0x73, 0x33, 0x83, 0xB9, 0x9C, 0x1D, 0xCD, 0xC0, + 0xEC, 0x6E, 0x07, 0xE3, 0xE0, 0x3E, 0x1F, 0x01, 0xF0, 0xF0, 0x0F, 0x87, + 0x80, 0x78, 0x38, 0x03, 0xC1, 0xC0, 0x00, 0x0F, 0x03, 0xC3, 0xC1, 0xE0, + 0xF8, 0xF0, 0x1E, 0x78, 0x07, 0x9E, 0x00, 0xFF, 0x00, 0x3F, 0x80, 0x0F, + 0xC0, 0x01, 0xE0, 0x00, 0xF8, 0x00, 0x3F, 0x00, 0x1F, 0xC0, 0x0F, 0xF0, + 0x07, 0x9E, 0x03, 0xC7, 0x80, 0xF0, 0xF0, 0x78, 0x3C, 0x3C, 0x0F, 0x80, + 0x78, 0x1E, 0xF0, 0x79, 0xE0, 0xF3, 0xC3, 0xC3, 0xCF, 0x07, 0x9E, 0x0F, + 0x78, 0x0F, 0xE0, 0x1F, 0x80, 0x3F, 0x00, 0x3C, 0x00, 0x70, 0x00, 0xE0, + 0x03, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x1F, 0xFF, + 0x0F, 0xFF, 0x87, 0xFF, 0xC0, 0x03, 0xC0, 0x03, 0xE0, 0x03, 0xE0, 0x03, + 0xE0, 0x03, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xFF, 0xF0, 0xFF, 0xF8, 0x7F, 0xFC, + 0x00, 0x0F, 0xC3, 0xF0, 0xFC, 0x38, 0x1E, 0x07, 0x01, 0xC0, 0x70, 0x1C, + 0x0F, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x07, 0x01, 0xC0, 0x70, 0x1C, 0x0F, + 0x03, 0x80, 0xFC, 0x3F, 0x0F, 0xC0, 0x08, 0x88, 0xC4, 0x44, 0x66, 0x66, + 0x66, 0x62, 0x22, 0x33, 0x33, 0x30, 0x0F, 0xC3, 0xF0, 0xFC, 0x07, 0x03, + 0xC0, 0xE0, 0x38, 0x0E, 0x03, 0x81, 0xC0, 0x70, 0x1C, 0x07, 0x03, 0xC0, + 0xE0, 0x38, 0x0E, 0x03, 0x81, 0xE0, 0x70, 0xFC, 0x3F, 0x0F, 0xC0, 0x03, + 0x80, 0xF0, 0x1E, 0x07, 0xE1, 0xDC, 0x3B, 0x8E, 0x71, 0x86, 0x70, 0xFC, + 0x1F, 0x83, 0x80, 0x7F, 0xFE, 0xFF, 0xFC, 0xE6, 0x30, 0x07, 0xE0, 0xFF, + 0x8F, 0xFE, 0x70, 0xE0, 0x07, 0x03, 0xF8, 0xFF, 0xCF, 0x9E, 0xF0, 0xF7, + 0x8F, 0x3F, 0xF8, 0xFF, 0xC3, 0xDF, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, + 0x00, 0xF0, 0x01, 0xE0, 0x03, 0x9F, 0x07, 0xFF, 0x0F, 0xFF, 0x3E, 0x3E, + 0x78, 0x3C, 0xF0, 0x79, 0xC0, 0xF3, 0x81, 0xEF, 0x07, 0x9F, 0x1F, 0x3F, + 0xFC, 0x7F, 0xF0, 0xEF, 0x80, 0x07, 0xC0, 0xFF, 0x8F, 0xFE, 0xF8, 0xF7, + 0x87, 0xB8, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0xF7, 0x8F, 0x1F, 0xF8, 0xFF, + 0x81, 0xF0, 0x00, 0x00, 0x1E, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x03, + 0xC0, 0xF7, 0x87, 0xFE, 0x1F, 0xFC, 0x7C, 0x78, 0xF0, 0x73, 0xC0, 0xE7, + 0x81, 0x8F, 0x07, 0x1E, 0x0E, 0x3E, 0x3C, 0x7F, 0xF8, 0x7F, 0xE0, 0x7D, + 0xC0, 0x07, 0xC0, 0xFF, 0x8F, 0xFE, 0xF0, 0xF7, 0x87, 0xFF, 0xFF, 0xFF, + 0xFE, 0x00, 0xF0, 0x07, 0xC7, 0x9F, 0xF8, 0xFF, 0x81, 0xF0, 0x00, 0x07, + 0x87, 0xC7, 0xE3, 0xC1, 0xC3, 0xF9, 0xFC, 0x78, 0x3C, 0x1C, 0x0E, 0x07, + 0x07, 0x83, 0x81, 0xC0, 0xE0, 0xF0, 0x78, 0x00, 0x03, 0xDE, 0x1F, 0xF8, + 0x7F, 0xF1, 0xF1, 0xE3, 0xC1, 0xCF, 0x03, 0x9E, 0x06, 0x3C, 0x0C, 0x78, + 0x38, 0xF8, 0xF1, 0xFF, 0xC1, 0xFF, 0x81, 0xF7, 0x00, 0x0E, 0x3C, 0x3C, + 0x78, 0xF0, 0x7F, 0xC0, 0x7E, 0x00, 0x1E, 0x00, 0x70, 0x01, 0xC0, 0x07, + 0x00, 0x3C, 0x00, 0xF7, 0xC3, 0xBF, 0x8F, 0xFF, 0x3C, 0x3D, 0xE0, 0xE7, + 0x83, 0x9C, 0x0E, 0x70, 0x79, 0xC1, 0xEF, 0x07, 0x3C, 0x1C, 0xE0, 0x73, + 0x83, 0xC0, 0x0E, 0x3C, 0x70, 0x00, 0x03, 0x8F, 0x1E, 0x38, 0x71, 0xE3, + 0xC7, 0x0E, 0x1C, 0x78, 0xF1, 0xC0, 0x03, 0xC0, 0xE0, 0x38, 0x00, 0x00, + 0x01, 0xE0, 0x70, 0x1C, 0x07, 0x03, 0xC0, 0xF0, 0x38, 0x0E, 0x03, 0x81, + 0xE0, 0x70, 0x1C, 0x07, 0x03, 0xC0, 0xF0, 0xF8, 0x3E, 0x0F, 0x00, 0x0E, + 0x00, 0x1C, 0x00, 0x38, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0x87, 0x87, 0x1E, + 0x0E, 0x78, 0x3D, 0xE0, 0x7F, 0x80, 0xFE, 0x01, 0xFE, 0x03, 0xFC, 0x0F, + 0x38, 0x1E, 0x78, 0x38, 0xF0, 0x70, 0xF0, 0xE1, 0xE0, 0x0E, 0x3C, 0x78, + 0xE1, 0xC3, 0x8F, 0x1E, 0x38, 0x71, 0xE3, 0xC7, 0x0E, 0x1C, 0x78, 0xF1, + 0xC0, 0x1C, 0xF1, 0xE0, 0xEF, 0xDF, 0x87, 0xFF, 0xFE, 0x7C, 0x78, 0xF3, + 0xC3, 0x87, 0x9C, 0x1C, 0x38, 0xE1, 0xE1, 0xC7, 0x0E, 0x0E, 0x78, 0x70, + 0xF3, 0xC3, 0x87, 0x9C, 0x3C, 0x38, 0xE1, 0xE1, 0xC7, 0x0E, 0x0E, 0x00, + 0x3D, 0xF0, 0xEF, 0xE3, 0xFF, 0xCF, 0x0F, 0x78, 0x39, 0xC0, 0xE7, 0x03, + 0x9C, 0x1E, 0xF0, 0x7B, 0xC1, 0xCE, 0x07, 0x38, 0x1C, 0xE0, 0xF0, 0x07, + 0xE0, 0x7F, 0xE3, 0xFF, 0x9F, 0x1F, 0x78, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, + 0x1F, 0xF0, 0x7B, 0xE3, 0xE7, 0xFF, 0x1F, 0xF8, 0x1F, 0x80, 0x0E, 0x7C, + 0x0F, 0xFE, 0x0F, 0xFF, 0x1F, 0x1F, 0x1E, 0x0F, 0x1E, 0x0F, 0x1C, 0x0F, + 0x1C, 0x0F, 0x3C, 0x1E, 0x3E, 0x3E, 0x3F, 0xFC, 0x3F, 0xF8, 0x7B, 0xE0, + 0x78, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0xF0, 0x00, 0x07, 0xBC, + 0x7F, 0xF3, 0xFF, 0x9F, 0x1E, 0x78, 0x3B, 0xC0, 0xEF, 0x03, 0x3C, 0x0C, + 0xF0, 0x73, 0xE3, 0xCF, 0xFF, 0x1F, 0xF8, 0x3C, 0xE0, 0x03, 0x80, 0x1E, + 0x00, 0x78, 0x01, 0xC0, 0x07, 0x00, 0x3D, 0xCE, 0xE3, 0xF8, 0xF0, 0x78, + 0x1E, 0x07, 0x01, 0xC0, 0xF0, 0x3C, 0x0E, 0x03, 0x80, 0xE0, 0x00, 0x1F, + 0xC3, 0xFE, 0x7F, 0xFF, 0x0F, 0xF0, 0x0F, 0xE0, 0x7F, 0xC1, 0xFE, 0x03, + 0xEE, 0x1E, 0xFF, 0xC7, 0xFC, 0x3F, 0x00, 0x1E, 0x1E, 0x1C, 0x7F, 0xFF, + 0x3C, 0x38, 0x38, 0x38, 0x78, 0x78, 0x70, 0x7C, 0xF8, 0x78, 0x38, 0x3C, + 0xE0, 0xE3, 0x83, 0x9E, 0x0E, 0x70, 0x79, 0xC1, 0xE7, 0x07, 0x3C, 0x1C, + 0xF0, 0xF3, 0xE7, 0xCF, 0xFF, 0x1F, 0xF8, 0x3C, 0xE0, 0xF0, 0x77, 0x87, + 0xBC, 0x38, 0xE3, 0xC7, 0x1C, 0x39, 0xE1, 0xCE, 0x0E, 0xE0, 0x77, 0x03, + 0xF0, 0x0F, 0x80, 0x78, 0x03, 0xC0, 0x00, 0xF1, 0xC3, 0xF8, 0xE3, 0xFC, + 0xF1, 0xDE, 0x79, 0xEF, 0x3C, 0xE7, 0xB6, 0x73, 0xDB, 0x70, 0xED, 0xB8, + 0x7C, 0xF8, 0x3E, 0x7C, 0x1F, 0x3C, 0x0F, 0x1E, 0x07, 0x8E, 0x00, 0x0F, + 0x1E, 0x0F, 0x3C, 0x0F, 0x38, 0x07, 0x70, 0x07, 0xF0, 0x03, 0xE0, 0x03, + 0xC0, 0x07, 0xC0, 0x0F, 0xE0, 0x1E, 0xE0, 0x3C, 0xF0, 0x3C, 0xF0, 0x78, + 0x78, 0x3C, 0x1C, 0x78, 0x78, 0xF0, 0xE1, 0xE3, 0xC1, 0xC7, 0x03, 0x9E, + 0x07, 0x38, 0x0E, 0xE0, 0x1D, 0xC0, 0x3F, 0x00, 0x7E, 0x00, 0x78, 0x00, + 0xF0, 0x01, 0xC0, 0x07, 0x00, 0x7E, 0x00, 0xF8, 0x01, 0xE0, 0x00, 0x1F, + 0xF9, 0xFF, 0xCF, 0xFC, 0x01, 0xE0, 0x3E, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xC0, 0x3F, 0xF9, 0xFF, 0xCF, 0xFC, 0x00, 0x07, 0x87, 0xC3, + 0xE3, 0xC1, 0xC0, 0xE0, 0x70, 0x38, 0x3C, 0x1C, 0x0E, 0x1E, 0x0F, 0x03, + 0x81, 0xC0, 0xE0, 0x70, 0x78, 0x38, 0x1C, 0x0F, 0x87, 0xC1, 0xC0, 0x0C, + 0x30, 0x86, 0x18, 0x61, 0x8C, 0x30, 0xC3, 0x0C, 0x61, 0x86, 0x18, 0x63, + 0x0C, 0x30, 0xC2, 0x00, 0x00, 0x07, 0x07, 0xC3, 0xE0, 0x70, 0x38, 0x3C, + 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xE0, 0xF0, 0xE0, 0x70, 0x78, 0x38, 0x1C, + 0x0E, 0x07, 0x07, 0x8F, 0x87, 0xC3, 0xC0, 0x3C, 0x07, 0xE0, 0xC7, 0x30, + 0x7E, 0x01, 0xC0}; + +const GFXglyph FreeSansBoldOblique12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 7, 0, 1}, // 0x20 ' ' + {0, 7, 17, 8, 3, -16}, // 0x21 '!' + {15, 10, 6, 11, 4, -17}, // 0x22 '"' + {23, 15, 16, 13, 1, -15}, // 0x23 '#' + {53, 15, 21, 13, 1, -17}, // 0x24 '$' + {93, 18, 18, 21, 3, -17}, // 0x25 '%' + {134, 15, 17, 17, 2, -16}, // 0x26 '&' + {166, 4, 6, 6, 4, -17}, // 0x27 ''' + {169, 9, 22, 8, 2, -17}, // 0x28 '(' + {194, 9, 22, 8, -1, -16}, // 0x29 ')' + {219, 8, 8, 9, 3, -17}, // 0x2A '*' + {227, 12, 11, 14, 2, -10}, // 0x2B '+' + {244, 5, 7, 7, 1, -2}, // 0x2C ',' + {249, 7, 3, 8, 2, -7}, // 0x2D '-' + {252, 4, 3, 7, 2, -2}, // 0x2E '.' + {254, 10, 17, 7, 0, -16}, // 0x2F '/' + {276, 13, 17, 13, 2, -16}, // 0x30 '0' + {304, 9, 17, 13, 4, -16}, // 0x31 '1' + {324, 15, 17, 13, 1, -16}, // 0x32 '2' + {356, 13, 17, 13, 2, -16}, // 0x33 '3' + {384, 13, 17, 13, 1, -16}, // 0x34 '4' + {412, 14, 17, 13, 1, -16}, // 0x35 '5' + {442, 13, 17, 13, 2, -16}, // 0x36 '6' + {470, 13, 17, 13, 3, -16}, // 0x37 '7' + {498, 14, 17, 13, 1, -16}, // 0x38 '8' + {528, 14, 17, 13, 2, -16}, // 0x39 '9' + {558, 6, 12, 8, 3, -11}, // 0x3A ':' + {567, 7, 16, 8, 2, -11}, // 0x3B ';' + {581, 13, 12, 14, 2, -11}, // 0x3C '<' + {601, 14, 9, 14, 1, -9}, // 0x3D '=' + {617, 13, 12, 14, 1, -10}, // 0x3E '>' + {637, 13, 18, 15, 4, -17}, // 0x3F '?' + {667, 22, 21, 23, 2, -17}, // 0x40 '@' + {725, 17, 18, 17, 0, -17}, // 0x41 'A' + {764, 17, 18, 17, 2, -17}, // 0x42 'B' + {803, 17, 18, 17, 3, -17}, // 0x43 'C' + {842, 17, 18, 17, 2, -17}, // 0x44 'D' + {881, 16, 18, 16, 2, -17}, // 0x45 'E' + {917, 16, 18, 15, 2, -17}, // 0x46 'F' + {953, 17, 18, 19, 3, -17}, // 0x47 'G' + {992, 17, 18, 17, 2, -17}, // 0x48 'H' + {1031, 7, 18, 7, 2, -17}, // 0x49 'I' + {1047, 14, 18, 13, 1, -17}, // 0x4A 'J' + {1079, 18, 18, 17, 2, -17}, // 0x4B 'K' + {1120, 13, 18, 15, 2, -17}, // 0x4C 'L' + {1150, 20, 18, 20, 2, -17}, // 0x4D 'M' + {1195, 18, 18, 17, 2, -17}, // 0x4E 'N' + {1236, 17, 18, 19, 3, -17}, // 0x4F 'O' + {1275, 16, 18, 16, 2, -17}, // 0x50 'P' + {1311, 17, 19, 19, 3, -17}, // 0x51 'Q' + {1352, 17, 18, 17, 2, -17}, // 0x52 'R' + {1391, 16, 18, 16, 2, -17}, // 0x53 'S' + {1427, 15, 18, 15, 3, -17}, // 0x54 'T' + {1461, 16, 18, 17, 3, -17}, // 0x55 'U' + {1497, 15, 18, 16, 4, -17}, // 0x56 'V' + {1531, 21, 18, 23, 4, -17}, // 0x57 'W' + {1579, 18, 18, 16, 1, -17}, // 0x58 'X' + {1620, 15, 18, 16, 4, -17}, // 0x59 'Y' + {1654, 17, 18, 15, 1, -17}, // 0x5A 'Z' + {1693, 10, 23, 8, 1, -17}, // 0x5B '[' + {1722, 4, 23, 7, 3, -22}, // 0x5C '\' + {1734, 10, 23, 8, 0, -17}, // 0x5D ']' + {1763, 11, 11, 14, 3, -16}, // 0x5E '^' + {1779, 15, 2, 13, -2, 4}, // 0x5F '_' + {1783, 4, 3, 8, 4, -17}, // 0x60 '`' + {1785, 13, 13, 13, 1, -12}, // 0x61 'a' + {1807, 15, 18, 15, 1, -17}, // 0x62 'b' + {1841, 13, 13, 13, 2, -12}, // 0x63 'c' + {1863, 15, 18, 15, 2, -17}, // 0x64 'd' + {1897, 13, 13, 13, 2, -12}, // 0x65 'e' + {1919, 9, 18, 8, 2, -17}, // 0x66 'f' + {1940, 15, 18, 15, 1, -12}, // 0x67 'g' + {1974, 14, 18, 15, 2, -17}, // 0x68 'h' + {2006, 7, 18, 7, 2, -17}, // 0x69 'i' + {2022, 10, 23, 7, -1, -17}, // 0x6A 'j' + {2051, 15, 18, 13, 1, -17}, // 0x6B 'k' + {2085, 7, 18, 7, 2, -17}, // 0x6C 'l' + {2101, 21, 13, 21, 1, -12}, // 0x6D 'm' + {2136, 14, 13, 15, 2, -12}, // 0x6E 'n' + {2159, 14, 13, 15, 2, -12}, // 0x6F 'o' + {2182, 16, 18, 15, 0, -12}, // 0x70 'p' + {2218, 14, 18, 15, 2, -12}, // 0x71 'q' + {2250, 10, 13, 9, 2, -12}, // 0x72 'r' + {2267, 12, 13, 13, 3, -12}, // 0x73 's' + {2287, 8, 15, 8, 2, -14}, // 0x74 't' + {2302, 14, 13, 15, 2, -12}, // 0x75 'u' + {2325, 13, 13, 13, 3, -12}, // 0x76 'v' + {2347, 17, 13, 19, 3, -12}, // 0x77 'w' + {2375, 16, 13, 13, 0, -12}, // 0x78 'x' + {2401, 15, 18, 13, 1, -12}, // 0x79 'y' + {2435, 13, 13, 12, 1, -12}, // 0x7A 'z' + {2457, 9, 23, 9, 3, -17}, // 0x7B '{' + {2483, 6, 23, 7, 1, -17}, // 0x7C '|' + {2501, 9, 23, 9, 0, -17}, // 0x7D '}' + {2527, 12, 5, 14, 2, -7}}; // 0x7E '~' + +const GFXfont FreeSansBoldOblique12pt7b PROGMEM = { + (uint8_t *)FreeSansBoldOblique12pt7bBitmaps, + (GFXglyph *)FreeSansBoldOblique12pt7bGlyphs, 0x20, 0x7E, 29}; + +// Approx. 3207 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique18pt7b.h new file mode 100644 index 0000000..c947f66 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique18pt7b.h @@ -0,0 +1,544 @@ +const uint8_t FreeSansBoldOblique18pt7bBitmaps[] PROGMEM = { + 0x06, 0x01, 0xC0, 0x7C, 0x1F, 0x0F, 0xC3, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, + 0xC0, 0xF0, 0x7C, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1C, 0x07, 0x01, 0xC0, + 0x60, 0x7C, 0x1F, 0x07, 0xC3, 0xF0, 0xF8, 0x00, 0x78, 0x7B, 0xC3, 0xFE, + 0x3F, 0xE1, 0xEF, 0x0F, 0x78, 0x7B, 0x83, 0x9C, 0x1C, 0xC0, 0xC0, 0x00, + 0x3C, 0x38, 0x00, 0xF1, 0xE0, 0x07, 0x87, 0x00, 0x1E, 0x3C, 0x00, 0xF0, + 0xE0, 0x3F, 0xFF, 0xF0, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0x1F, 0xFF, 0xF8, + 0x0F, 0x0E, 0x00, 0x3C, 0x78, 0x00, 0xE1, 0xE0, 0x07, 0x8F, 0x00, 0x1C, + 0x3C, 0x07, 0xFF, 0xFE, 0x1F, 0xFF, 0xF8, 0x7F, 0xFF, 0xE3, 0xFF, 0xFF, + 0x01, 0xE3, 0xC0, 0x0F, 0x0E, 0x00, 0x3C, 0x78, 0x01, 0xE1, 0xC0, 0x07, + 0x8F, 0x00, 0x3C, 0x38, 0x00, 0x00, 0x0C, 0x00, 0x01, 0x80, 0x00, 0xFC, + 0x00, 0xFF, 0xC0, 0x3F, 0xFC, 0x0F, 0xFF, 0xC3, 0xE6, 0x78, 0x78, 0xCF, + 0x1E, 0x39, 0xE3, 0xC7, 0x3C, 0x78, 0xC0, 0x0F, 0x98, 0x01, 0xFF, 0x00, + 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x00, 0x7F, 0x80, 0x0F, 0xF0, + 0x03, 0xBE, 0x00, 0x67, 0xCF, 0x8C, 0xF9, 0xF1, 0x9F, 0x3E, 0x77, 0xC7, + 0xEF, 0xF8, 0x7F, 0xFE, 0x0F, 0xFF, 0x80, 0xFF, 0xE0, 0x03, 0xE0, 0x00, + 0x38, 0x00, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x07, 0x01, 0xE0, + 0x03, 0x81, 0xFE, 0x00, 0xC0, 0xFF, 0x80, 0x70, 0x7F, 0xF0, 0x38, 0x1E, + 0x3C, 0x1C, 0x0F, 0x07, 0x06, 0x03, 0x81, 0xC3, 0x80, 0xE0, 0xF1, 0xC0, + 0x3C, 0x78, 0xE0, 0x0F, 0xFE, 0x30, 0x01, 0xFF, 0x1C, 0x00, 0x7F, 0x8E, + 0x00, 0x07, 0x83, 0x00, 0x00, 0x01, 0x83, 0xE0, 0x00, 0xE3, 0xFE, 0x00, + 0x71, 0xFF, 0x80, 0x18, 0xFF, 0xF0, 0x0C, 0x3C, 0x3C, 0x07, 0x1C, 0x07, + 0x03, 0x87, 0x01, 0xC0, 0xC1, 0xE1, 0xE0, 0x60, 0x7F, 0xF8, 0x38, 0x0F, + 0xFC, 0x1C, 0x03, 0xFE, 0x06, 0x00, 0x3E, 0x00, 0x00, 0x1F, 0x00, 0x03, + 0xFC, 0x00, 0x3F, 0xF0, 0x03, 0xFF, 0x80, 0x3F, 0x3C, 0x01, 0xF1, 0xE0, + 0x0F, 0x8F, 0x00, 0x7C, 0xF0, 0x03, 0xFF, 0x80, 0x0F, 0xF8, 0x00, 0x3F, + 0x00, 0x03, 0xF0, 0x00, 0x7F, 0xC7, 0x8F, 0xFE, 0x3C, 0xFC, 0xFB, 0xCF, + 0x83, 0xFE, 0xF8, 0x1F, 0xE7, 0xC0, 0x7E, 0x3E, 0x03, 0xE1, 0xF0, 0x1F, + 0x0F, 0xE3, 0xFC, 0x7F, 0xFF, 0xE1, 0xFF, 0xFF, 0x87, 0xFE, 0x7C, 0x0F, + 0xE1, 0xF0, 0x7B, 0xFF, 0xEF, 0x7B, 0x9C, 0xC0, 0x00, 0x78, 0x07, 0x80, + 0x78, 0x03, 0x80, 0x3C, 0x03, 0xC0, 0x1E, 0x01, 0xE0, 0x1E, 0x00, 0xF0, + 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0xF0, 0x07, + 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, + 0x07, 0x80, 0x1C, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x00, 0xE0, 0x07, 0x80, + 0x1C, 0x00, 0x01, 0xC0, 0x0F, 0x00, 0x38, 0x01, 0xE0, 0x0F, 0x00, 0x78, + 0x01, 0xC0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, + 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0xF8, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x1E, + 0x00, 0xF0, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x1E, 0x01, + 0xE0, 0x1E, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x00, 0x03, 0x00, 0x70, 0x07, + 0x04, 0x63, 0xFF, 0xF7, 0xFF, 0x1F, 0x83, 0xF0, 0x3B, 0x87, 0x38, 0x21, + 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, + 0x00, 0x7C, 0x07, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xE0, + 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x7C, 0x00, 0x1E, + 0x00, 0x3E, 0x7C, 0xF3, 0xE7, 0xC1, 0x87, 0x0C, 0x39, 0xE3, 0x00, 0x7F, + 0xDF, 0xFF, 0xFB, 0xFE, 0x7D, 0xF7, 0xBE, 0xF8, 0x00, 0x0E, 0x00, 0x18, + 0x00, 0x70, 0x00, 0xC0, 0x03, 0x80, 0x06, 0x00, 0x1C, 0x00, 0x30, 0x00, + 0xE0, 0x01, 0x80, 0x07, 0x00, 0x0C, 0x00, 0x38, 0x00, 0x60, 0x01, 0xC0, + 0x03, 0x00, 0x0E, 0x00, 0x18, 0x00, 0x70, 0x00, 0xC0, 0x03, 0x80, 0x06, + 0x00, 0x1C, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x00, 0xFC, 0x00, 0x7F, 0xC0, + 0x7F, 0xF8, 0x3F, 0xFE, 0x0F, 0x8F, 0xC7, 0xC1, 0xF1, 0xE0, 0x7C, 0xF8, + 0x1F, 0x3E, 0x07, 0xDF, 0x01, 0xF7, 0xC0, 0x7D, 0xF0, 0x3F, 0x7C, 0x0F, + 0xBF, 0x03, 0xEF, 0x80, 0xFB, 0xE0, 0x3E, 0xF8, 0x1F, 0x3E, 0x07, 0xCF, + 0x81, 0xE3, 0xE0, 0xF8, 0xFC, 0x7C, 0x1F, 0xFF, 0x07, 0xFF, 0x80, 0xFF, + 0xC0, 0x0F, 0x80, 0x00, 0x00, 0x70, 0x03, 0x80, 0x3C, 0x03, 0xE0, 0xFF, + 0x3F, 0xF3, 0xFF, 0x9F, 0xFC, 0x03, 0xE0, 0x1F, 0x01, 0xF0, 0x0F, 0x80, + 0x7C, 0x03, 0xE0, 0x1E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x3E, + 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x3E, 0x00, 0x00, 0x1F, 0x80, + 0x07, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xE0, 0x7E, 0x1F, 0x83, 0xE0, + 0x7C, 0x1F, 0x03, 0xE1, 0xF0, 0x1F, 0x0F, 0x80, 0xF8, 0x00, 0x0F, 0x80, + 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xE0, 0x00, 0xFC, + 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x80, 0x03, 0xF8, 0x00, 0x3F, + 0x00, 0x03, 0xF0, 0x00, 0x1F, 0xFF, 0xE1, 0xFF, 0xFF, 0x0F, 0xFF, 0xF0, + 0x7F, 0xFF, 0x80, 0x00, 0x7F, 0x00, 0x1F, 0xFC, 0x03, 0xFF, 0xE0, 0x7F, + 0xFF, 0x0F, 0x83, 0xF0, 0xF0, 0x1F, 0x1F, 0x01, 0xF1, 0xE0, 0x1F, 0x00, + 0x03, 0xE0, 0x00, 0xFC, 0x00, 0xFF, 0x80, 0x0F, 0xF0, 0x00, 0xFF, 0x80, + 0x0F, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xCF, 0x80, 0x7C, + 0xF8, 0x07, 0xCF, 0x80, 0xF8, 0xFC, 0x3F, 0x8F, 0xFF, 0xF0, 0x7F, 0xFE, + 0x03, 0xFF, 0xC0, 0x0F, 0xE0, 0x00, 0x00, 0x07, 0xE0, 0x01, 0xFC, 0x00, + 0x7F, 0x00, 0x1F, 0xE0, 0x03, 0xFC, 0x00, 0xEF, 0x80, 0x3D, 0xF0, 0x0F, + 0x7C, 0x03, 0xCF, 0x80, 0xF1, 0xF0, 0x1C, 0x3E, 0x07, 0x07, 0xC1, 0xE1, + 0xF0, 0x78, 0x3E, 0x1E, 0x07, 0xC3, 0xFF, 0xFE, 0x7F, 0xFF, 0xDF, 0xFF, + 0xFB, 0xFF, 0xFF, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x78, 0x00, 0x1F, + 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0x03, + 0xFF, 0xF0, 0x3F, 0xFF, 0x07, 0x80, 0x00, 0x78, 0x00, 0x0F, 0x00, 0x00, + 0xF7, 0xE0, 0x0F, 0xFF, 0x01, 0xFF, 0xF8, 0x1F, 0xFF, 0x83, 0xF0, 0xFC, + 0x3E, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, + 0x8F, 0x80, 0xF8, 0xF8, 0x1F, 0x8F, 0xC3, 0xF0, 0xFF, 0xFE, 0x07, 0xFF, + 0xC0, 0x3F, 0xF8, 0x00, 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x3F, 0xF0, 0x0F, + 0xFF, 0x03, 0xFF, 0xE0, 0xF8, 0x7E, 0x3E, 0x07, 0xC7, 0x80, 0x01, 0xF0, + 0x00, 0x3C, 0xFC, 0x07, 0xFF, 0xC1, 0xFF, 0xFC, 0x3F, 0xFF, 0xC7, 0xE1, + 0xF8, 0xF8, 0x1F, 0x3E, 0x03, 0xE7, 0x80, 0x7C, 0xF0, 0x0F, 0x9E, 0x01, + 0xE3, 0xC0, 0x7C, 0x78, 0x1F, 0x0F, 0x87, 0xE0, 0xFF, 0xF8, 0x1F, 0xFE, + 0x01, 0xFF, 0x80, 0x0F, 0xC0, 0x00, 0x7F, 0xFF, 0xEF, 0xFF, 0xF9, 0xFF, + 0xFF, 0x7F, 0xFF, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, + 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x07, 0x80, 0x01, 0xF0, 0x00, 0x7C, + 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x3E, 0x00, + 0x07, 0xC0, 0x00, 0x00, 0x7F, 0x00, 0x1F, 0xFC, 0x07, 0xFF, 0xE0, 0xFF, + 0xFF, 0x0F, 0x81, 0xF1, 0xF0, 0x0F, 0x1E, 0x00, 0xF1, 0xE0, 0x1E, 0x1F, + 0x07, 0xE0, 0xFF, 0xFC, 0x07, 0xFF, 0x00, 0xFF, 0xF8, 0x1F, 0xFF, 0x83, + 0xF0, 0xFC, 0x7C, 0x07, 0xC7, 0xC0, 0x7C, 0xF8, 0x07, 0xCF, 0x80, 0x7C, + 0xF8, 0x0F, 0x8F, 0x80, 0xF8, 0xFC, 0x3F, 0x0F, 0xFF, 0xF0, 0x7F, 0xFE, + 0x03, 0xFF, 0x80, 0x0F, 0xE0, 0x00, 0x00, 0x7E, 0x00, 0x3F, 0xF0, 0x0F, + 0xFF, 0x03, 0xFF, 0xE0, 0xFC, 0x3E, 0x3F, 0x03, 0xC7, 0xC0, 0x79, 0xF0, + 0x0F, 0x3E, 0x01, 0xE7, 0xC0, 0x3C, 0xF8, 0x0F, 0x9F, 0x03, 0xE3, 0xF0, + 0xFC, 0x7F, 0xFF, 0x87, 0xFF, 0xF0, 0x7F, 0xFE, 0x07, 0xE7, 0x80, 0x01, + 0xF0, 0x00, 0x3C, 0x7C, 0x0F, 0x8F, 0xC3, 0xE1, 0xFF, 0xF8, 0x1F, 0xFE, + 0x01, 0xFF, 0x80, 0x0F, 0xC0, 0x00, 0x0F, 0x87, 0xC3, 0xC3, 0xE1, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE1, 0xF0, 0xF0, + 0xF8, 0x7C, 0x00, 0x07, 0xC1, 0xF0, 0x78, 0x3E, 0x0F, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x83, 0xE0, 0xF0, 0x7C, + 0x1F, 0x00, 0xC0, 0x70, 0x18, 0x0E, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x3C, 0x00, 0x3F, 0x80, 0x3F, 0xE0, 0x3F, 0xFC, 0x3F, 0xFC, 0x1F, + 0xFC, 0x07, 0xFC, 0x00, 0xFC, 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0x80, 0x1F, + 0xFE, 0x00, 0xFF, 0xF0, 0x03, 0xFE, 0x00, 0x1F, 0xC0, 0x00, 0x78, 0x00, + 0x03, 0x00, 0x1F, 0xFF, 0xF3, 0xFF, 0xFE, 0x3F, 0xFF, 0xE3, 0xFF, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, + 0xC7, 0xFF, 0xFC, 0xFF, 0xFF, 0x8F, 0xFF, 0xF8, 0x18, 0x00, 0x03, 0xC0, + 0x00, 0x7F, 0x00, 0x0F, 0xF8, 0x01, 0xFF, 0xE0, 0x0F, 0xFF, 0x00, 0x3F, + 0xF8, 0x01, 0xFF, 0x00, 0x07, 0xE0, 0x07, 0xFC, 0x07, 0xFF, 0x07, 0xFF, + 0x87, 0xFF, 0x80, 0xFF, 0x80, 0x3F, 0x80, 0x07, 0x80, 0x00, 0x80, 0x00, + 0x00, 0x03, 0xF8, 0x03, 0xFF, 0xC1, 0xFF, 0xF8, 0xFF, 0xFE, 0x7E, 0x1F, + 0xDF, 0x03, 0xFF, 0x80, 0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xC0, 0x03, 0xE0, + 0x01, 0xF8, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x3F, 0x80, 0x1F, + 0x80, 0x07, 0x80, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xC0, 0x01, 0xF0, 0x00, 0xFC, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x00, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFE, + 0x00, 0x07, 0xF0, 0x1F, 0xC0, 0x0F, 0xC0, 0x03, 0xE0, 0x0F, 0x80, 0x00, + 0xF8, 0x0F, 0x00, 0x00, 0x3C, 0x0F, 0x01, 0xF1, 0xCF, 0x0F, 0x03, 0xFD, + 0xC7, 0x8F, 0x03, 0xFF, 0xE1, 0xC7, 0x03, 0xE3, 0xE0, 0xE7, 0x03, 0xC0, + 0xF0, 0x73, 0x83, 0xC0, 0x78, 0x3B, 0x81, 0xE0, 0x38, 0x1D, 0xC1, 0xE0, + 0x1C, 0x1C, 0xC0, 0xF0, 0x1C, 0x0E, 0xE0, 0x70, 0x0E, 0x0F, 0x70, 0x78, + 0x0E, 0x07, 0x38, 0x3C, 0x0F, 0x07, 0x1C, 0x1E, 0x0F, 0x87, 0x8E, 0x0F, + 0x8F, 0xCF, 0x87, 0x07, 0xFF, 0xFF, 0x83, 0xC1, 0xFE, 0x7F, 0x00, 0xE0, + 0x3C, 0x1F, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0F, + 0xC0, 0x01, 0x00, 0x03, 0xF8, 0x07, 0x80, 0x00, 0xFF, 0xFF, 0xC0, 0x00, + 0x1F, 0xFF, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x0F, 0xE0, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0xC0, 0x00, 0xFF, 0x80, 0x03, + 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x1F, 0x7C, 0x00, 0x7E, 0xF8, 0x00, 0xF9, + 0xF0, 0x03, 0xF3, 0xE0, 0x07, 0xC3, 0xE0, 0x1F, 0x87, 0xC0, 0x3E, 0x0F, + 0x80, 0xF8, 0x1F, 0x01, 0xF0, 0x3E, 0x07, 0xFF, 0xFC, 0x1F, 0xFF, 0xF8, + 0x3F, 0xFF, 0xF0, 0xFF, 0xFF, 0xF1, 0xF0, 0x03, 0xE7, 0xC0, 0x07, 0xCF, + 0x80, 0x0F, 0xBE, 0x00, 0x1F, 0x7C, 0x00, 0x3F, 0xF0, 0x00, 0x7C, 0x07, + 0xFF, 0xF0, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0x0F, + 0xC0, 0x3F, 0x0F, 0x80, 0x1F, 0x0F, 0x80, 0x1F, 0x0F, 0x80, 0x1F, 0x1F, + 0x80, 0x1E, 0x1F, 0x80, 0x3E, 0x1F, 0x00, 0x7C, 0x1F, 0xFF, 0xF8, 0x1F, + 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, 0x3F, 0xFF, 0xF8, 0x3E, 0x00, 0xFC, 0x3E, + 0x00, 0x7C, 0x3E, 0x00, 0x7C, 0x7E, 0x00, 0x7C, 0x7C, 0x00, 0x7C, 0x7C, + 0x00, 0xF8, 0x7C, 0x01, 0xF8, 0x7F, 0xFF, 0xF0, 0xFF, 0xFF, 0xE0, 0xFF, + 0xFF, 0xC0, 0xFF, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0xF8, 0x01, + 0xFF, 0xFC, 0x03, 0xFF, 0xFE, 0x07, 0xE0, 0x7F, 0x0F, 0xC0, 0x3F, 0x1F, + 0x80, 0x1F, 0x3F, 0x00, 0x1F, 0x3E, 0x00, 0x1F, 0x7E, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x7C, 0xF8, + 0x00, 0x7C, 0xFC, 0x00, 0xF8, 0xFC, 0x01, 0xF8, 0x7F, 0x07, 0xF0, 0x7F, + 0xFF, 0xE0, 0x3F, 0xFF, 0xC0, 0x0F, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x07, + 0xFF, 0xE0, 0x07, 0xFF, 0xF8, 0x07, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x0F, + 0x80, 0x7E, 0x0F, 0x80, 0x3F, 0x0F, 0x80, 0x1F, 0x1F, 0x80, 0x1F, 0x1F, + 0x80, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x1F, 0x3F, + 0x00, 0x1F, 0x3E, 0x00, 0x3E, 0x3E, 0x00, 0x3E, 0x3E, 0x00, 0x3E, 0x3E, + 0x00, 0x3E, 0x7E, 0x00, 0x7C, 0x7C, 0x00, 0x7C, 0x7C, 0x00, 0xF8, 0x7C, + 0x01, 0xF8, 0x7C, 0x07, 0xF0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xC0, 0xFF, + 0xFF, 0x00, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x07, + 0xFF, 0xFE, 0x0F, 0xFF, 0xFE, 0x0F, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x0F, + 0x80, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x3F, + 0xFF, 0xF0, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xFF, + 0xFF, 0xF0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xE0, 0x07, + 0xFF, 0xFE, 0x0F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF0, 0x7F, 0xFF, 0xE0, 0xF8, + 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0x1F, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, + 0x07, 0xFF, 0xFC, 0x0F, 0xFF, 0xF0, 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0xFC, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, + 0x80, 0x00, 0x3F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0xF8, 0x01, 0xFF, 0xFC, 0x03, + 0xFF, 0xFE, 0x07, 0xE0, 0x7E, 0x0F, 0x80, 0x3F, 0x1F, 0x00, 0x1F, 0x3E, + 0x00, 0x1F, 0x3E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0xF8, 0x03, 0xFF, 0xF8, 0x07, 0xFF, 0xF8, 0x07, 0xFE, 0xF8, + 0x07, 0xFE, 0xF8, 0x00, 0x3E, 0xF8, 0x00, 0x3E, 0xFC, 0x00, 0x7E, 0xFC, + 0x00, 0x7C, 0x7E, 0x00, 0xFC, 0x7F, 0x83, 0xFC, 0x3F, 0xFF, 0xFC, 0x1F, + 0xFF, 0xBC, 0x0F, 0xFF, 0x38, 0x03, 0xFC, 0x38, 0x03, 0xE0, 0x07, 0xC0, + 0xF8, 0x01, 0xF0, 0x7E, 0x00, 0x7C, 0x1F, 0x00, 0x3F, 0x07, 0xC0, 0x0F, + 0x81, 0xF0, 0x03, 0xE0, 0xFC, 0x00, 0xF8, 0x3E, 0x00, 0x3E, 0x0F, 0x80, + 0x1F, 0x83, 0xE0, 0x07, 0xC0, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFC, 0x1F, + 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC1, 0xF0, 0x03, 0xE0, 0x7C, 0x00, 0xF8, + 0x3F, 0x00, 0x3E, 0x0F, 0x80, 0x0F, 0x83, 0xE0, 0x07, 0xE0, 0xF8, 0x01, + 0xF0, 0x3E, 0x00, 0x7C, 0x1F, 0x80, 0x1F, 0x07, 0xC0, 0x0F, 0xC1, 0xF0, + 0x03, 0xF0, 0x7C, 0x00, 0xF8, 0x3F, 0x00, 0x3E, 0x00, 0x07, 0xC3, 0xF0, + 0xFC, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x7E, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, + 0x3F, 0x0F, 0xC3, 0xE0, 0xF8, 0x3E, 0x0F, 0x87, 0xE1, 0xF0, 0x7C, 0x1F, + 0x07, 0xC3, 0xF0, 0xFC, 0x3E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, + 0x01, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xE0, + 0x00, 0x3E, 0x00, 0x07, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, + 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x7C, 0x0F, + 0x8F, 0x81, 0xF8, 0xF8, 0x1F, 0x0F, 0x81, 0xF0, 0xF8, 0x1F, 0x0F, 0xC3, + 0xF0, 0xFF, 0xFE, 0x07, 0xFF, 0xC0, 0x3F, 0xF8, 0x01, 0xFC, 0x00, 0x07, + 0xC0, 0x0F, 0xC1, 0xF0, 0x07, 0xE0, 0x7C, 0x03, 0xF0, 0x3F, 0x03, 0xF8, + 0x0F, 0x81, 0xF8, 0x03, 0xE0, 0xFC, 0x00, 0xF8, 0x7E, 0x00, 0x7E, 0x3F, + 0x00, 0x1F, 0x1F, 0x80, 0x07, 0xCF, 0xC0, 0x01, 0xF7, 0xE0, 0x00, 0x7F, + 0xF0, 0x00, 0x3F, 0xFC, 0x00, 0x0F, 0xFF, 0x80, 0x03, 0xFF, 0xF0, 0x00, + 0xFE, 0xFC, 0x00, 0x3F, 0x1F, 0x80, 0x1F, 0x87, 0xE0, 0x07, 0xC0, 0xFC, + 0x01, 0xF0, 0x3F, 0x00, 0x7C, 0x07, 0xE0, 0x1F, 0x01, 0xFC, 0x0F, 0xC0, + 0x3F, 0x03, 0xE0, 0x0F, 0xE0, 0xF8, 0x01, 0xF8, 0x3E, 0x00, 0x3F, 0x00, + 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x0F, 0xC0, 0x03, + 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x1F, 0x80, 0x07, 0xC0, 0x01, 0xF0, + 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x0F, 0xC0, 0x03, 0xE0, 0x00, 0xF8, 0x00, + 0x3E, 0x00, 0x0F, 0x80, 0x07, 0xE0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1F, + 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xE0, 0x03, + 0xF8, 0x01, 0xFC, 0x07, 0xF0, 0x07, 0xF8, 0x1F, 0xE0, 0x0F, 0xF0, 0x3F, + 0xC0, 0x3F, 0xE0, 0x7F, 0x80, 0x7F, 0xC0, 0xFF, 0x01, 0xFF, 0x01, 0xFE, + 0x03, 0xFE, 0x07, 0xDC, 0x07, 0x7C, 0x0F, 0xB8, 0x1E, 0xF8, 0x1F, 0x70, + 0x3D, 0xF0, 0x3E, 0xF0, 0xF7, 0xC0, 0xF9, 0xE1, 0xEF, 0x81, 0xF3, 0xC7, + 0x9F, 0x03, 0xE7, 0x8F, 0x3E, 0x07, 0xCF, 0x3C, 0x7C, 0x0F, 0x9E, 0x79, + 0xF0, 0x3E, 0x3C, 0xE3, 0xE0, 0x7C, 0x7B, 0xC7, 0xC0, 0xF8, 0xF7, 0x8F, + 0x81, 0xF1, 0xFE, 0x1E, 0x07, 0xE3, 0xFC, 0x7C, 0x0F, 0x87, 0xF0, 0xF8, + 0x1F, 0x0F, 0xE1, 0xF0, 0x3E, 0x1F, 0x83, 0xE0, 0x7C, 0x3F, 0x0F, 0x81, + 0xF0, 0x7E, 0x1F, 0x00, 0x03, 0xE0, 0x07, 0xC0, 0x7E, 0x00, 0xF8, 0x1F, + 0xC0, 0x1F, 0x03, 0xF8, 0x03, 0xE0, 0x7F, 0x80, 0x7C, 0x0F, 0xF0, 0x1F, + 0x01, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, 0x7C, 0x0F, 0xBC, 0x0F, 0x81, 0xF7, + 0xC1, 0xF0, 0x3E, 0xF8, 0x7C, 0x0F, 0x8F, 0x0F, 0x81, 0xF1, 0xF1, 0xF0, + 0x3E, 0x3E, 0x3E, 0x07, 0xC3, 0xC7, 0xC0, 0xF8, 0x7D, 0xF0, 0x3E, 0x0F, + 0xBE, 0x07, 0xC0, 0xF7, 0xC0, 0xF8, 0x1F, 0xF8, 0x1F, 0x01, 0xFE, 0x03, + 0xC0, 0x3F, 0xC0, 0xF8, 0x07, 0xF8, 0x1F, 0x00, 0x7F, 0x03, 0xE0, 0x0F, + 0xE0, 0x7C, 0x01, 0xF8, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0xE0, 0x00, + 0x3F, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x7F, 0xFF, 0xC0, 0x7E, 0x07, 0xF0, + 0x7E, 0x01, 0xF8, 0x7C, 0x00, 0x7E, 0x3E, 0x00, 0x1F, 0x3E, 0x00, 0x0F, + 0x9E, 0x00, 0x07, 0xDF, 0x00, 0x03, 0xEF, 0x80, 0x01, 0xFF, 0x80, 0x00, + 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7D, 0xF0, 0x00, 0x3E, 0xF8, 0x00, + 0x1F, 0x7C, 0x00, 0x1F, 0x3E, 0x00, 0x1F, 0x9F, 0x80, 0x0F, 0x87, 0xE0, + 0x0F, 0x83, 0xF8, 0x1F, 0x80, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0x80, 0x0F, + 0xFF, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x0F, 0xFF, 0xF0, + 0x3F, 0xFF, 0xF0, 0x7F, 0xFF, 0xF0, 0xF8, 0x07, 0xE1, 0xF0, 0x07, 0xC3, + 0xE0, 0x0F, 0x8F, 0xC0, 0x1F, 0x1F, 0x00, 0x3E, 0x3E, 0x00, 0xF8, 0x7C, + 0x01, 0xF0, 0xF8, 0x07, 0xC3, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x0F, 0xFF, + 0xF8, 0x1F, 0xFF, 0x80, 0x3E, 0x00, 0x00, 0xFC, 0x00, 0x01, 0xF0, 0x00, + 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x3F, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x3F, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x7F, 0xFF, 0xC0, 0x7F, 0x07, + 0xF0, 0x7E, 0x01, 0xF8, 0x7E, 0x00, 0x7E, 0x3E, 0x00, 0x1F, 0x3E, 0x00, + 0x0F, 0x9E, 0x00, 0x07, 0xDF, 0x00, 0x03, 0xEF, 0x80, 0x01, 0xF7, 0x80, + 0x00, 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7D, 0xF0, 0x00, 0x3E, 0xF8, + 0x02, 0x1F, 0x7C, 0x03, 0x9F, 0x3E, 0x03, 0xFF, 0x9F, 0x81, 0xFF, 0x87, + 0xE0, 0x7F, 0x83, 0xF8, 0x3F, 0xC0, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xF0, + 0x0F, 0xFF, 0xFC, 0x01, 0xFE, 0x1C, 0x00, 0x00, 0x0C, 0x00, 0x07, 0xFF, + 0xF8, 0x07, 0xFF, 0xFE, 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0x0F, 0x80, + 0x3F, 0x0F, 0x80, 0x1F, 0x0F, 0x80, 0x1F, 0x0F, 0x80, 0x1F, 0x1F, 0x80, + 0x1E, 0x1F, 0x00, 0x3E, 0x1F, 0x00, 0x7C, 0x1F, 0xFF, 0xF8, 0x1F, 0xFF, + 0xE0, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, 0x3E, 0x01, 0xF8, 0x3E, 0x00, + 0xF8, 0x3E, 0x00, 0xF8, 0x7E, 0x00, 0xF8, 0x7C, 0x00, 0xF8, 0x7C, 0x01, + 0xF0, 0x7C, 0x01, 0xF0, 0x7C, 0x01, 0xF0, 0xFC, 0x01, 0xF0, 0xF8, 0x01, + 0xF0, 0xF8, 0x01, 0xF0, 0x00, 0x3F, 0xC0, 0x07, 0xFF, 0xC0, 0x3F, 0xFF, + 0x81, 0xFF, 0xFF, 0x0F, 0xC0, 0xFC, 0x3E, 0x01, 0xF1, 0xF0, 0x07, 0xC7, + 0xC0, 0x1F, 0x1F, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xFF, + 0x80, 0x0F, 0xFF, 0xC0, 0x1F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, 0x03, 0xFE, + 0x00, 0x01, 0xF8, 0x00, 0x03, 0xEF, 0x80, 0x0F, 0xBE, 0x00, 0x3C, 0xFC, + 0x01, 0xF3, 0xF8, 0x1F, 0x87, 0xFF, 0xFE, 0x0F, 0xFF, 0xF0, 0x1F, 0xFF, + 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x0F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xC0, 0x00, 0x3E, + 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xC0, 0x00, + 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xC0, + 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, 0x07, + 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, + 0x00, 0x0F, 0x80, 0x1F, 0x1F, 0x80, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x00, + 0x3F, 0x1F, 0x00, 0x3E, 0x1F, 0x00, 0x3E, 0x3E, 0x00, 0x3E, 0x3E, 0x00, + 0x7E, 0x3E, 0x00, 0x7C, 0x3E, 0x00, 0x7C, 0x3E, 0x00, 0x7C, 0x7C, 0x00, + 0x7C, 0x7C, 0x00, 0xFC, 0x7C, 0x00, 0xF8, 0x7C, 0x00, 0xF8, 0x7C, 0x00, + 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x01, 0xF8, 0xF8, 0x01, 0xF0, 0xF8, 0x01, + 0xF0, 0xF8, 0x03, 0xE0, 0xFE, 0x0F, 0xE0, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, + 0x80, 0x1F, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0xFC, 0x00, 0x7F, 0xF0, 0x03, + 0xE7, 0xC0, 0x0F, 0x9F, 0x00, 0x7C, 0x7C, 0x01, 0xF1, 0xF0, 0x0F, 0x87, + 0xC0, 0x3E, 0x1F, 0x01, 0xF0, 0x7C, 0x07, 0x81, 0xF0, 0x3E, 0x03, 0xC0, + 0xF0, 0x0F, 0x07, 0xC0, 0x3E, 0x1E, 0x00, 0xF8, 0xF8, 0x03, 0xE3, 0xC0, + 0x0F, 0x9F, 0x00, 0x3E, 0x78, 0x00, 0xFB, 0xE0, 0x01, 0xEF, 0x00, 0x07, + 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xE0, + 0x00, 0x1F, 0x80, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x07, 0xE0, 0x1F, 0xF8, + 0x07, 0xE0, 0x3F, 0xF8, 0x0F, 0xE0, 0x3E, 0xF8, 0x0F, 0xE0, 0x7E, 0xF8, + 0x1F, 0xE0, 0x7C, 0xF8, 0x1F, 0xE0, 0x7C, 0xF8, 0x3F, 0xE0, 0xF8, 0xF8, + 0x3D, 0xE0, 0xF8, 0x78, 0x3D, 0xE1, 0xF0, 0x78, 0x79, 0xE1, 0xF0, 0x78, + 0x79, 0xE1, 0xE0, 0x78, 0xF9, 0xE3, 0xE0, 0x78, 0xF1, 0xE3, 0xC0, 0x79, + 0xF1, 0xE7, 0xC0, 0x79, 0xE1, 0xE7, 0x80, 0x79, 0xE1, 0xE7, 0x80, 0x7B, + 0xC1, 0xEF, 0x80, 0x7B, 0xC1, 0xEF, 0x00, 0x7F, 0x81, 0xFF, 0x00, 0x7F, + 0x81, 0xFE, 0x00, 0x7F, 0x01, 0xFE, 0x00, 0x7F, 0x01, 0xFC, 0x00, 0x7F, + 0x01, 0xFC, 0x00, 0x7E, 0x01, 0xF8, 0x00, 0x3E, 0x01, 0xF8, 0x00, 0x3C, + 0x01, 0xF0, 0x00, 0x03, 0xF0, 0x07, 0xE0, 0x7E, 0x01, 0xF8, 0x07, 0xE0, + 0x7E, 0x00, 0xFC, 0x1F, 0x80, 0x1F, 0x83, 0xE0, 0x01, 0xF8, 0xF8, 0x00, + 0x3F, 0x3F, 0x00, 0x03, 0xEF, 0xC0, 0x00, 0x7F, 0xF0, 0x00, 0x0F, 0xFC, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0xFF, 0xC0, + 0x00, 0x3E, 0xF8, 0x00, 0x0F, 0xDF, 0x80, 0x03, 0xF3, 0xF0, 0x00, 0xFC, + 0x3F, 0x00, 0x3F, 0x07, 0xE0, 0x07, 0xE0, 0xFC, 0x01, 0xF8, 0x0F, 0xC0, + 0x7E, 0x01, 0xF8, 0x1F, 0x80, 0x3F, 0x80, 0x7C, 0x00, 0xFD, 0xF8, 0x07, + 0xE7, 0xE0, 0x1F, 0x1F, 0x80, 0xFC, 0x3E, 0x07, 0xE0, 0xFC, 0x1F, 0x03, + 0xF0, 0xFC, 0x07, 0xC7, 0xE0, 0x1F, 0x1F, 0x00, 0x7E, 0xFC, 0x00, 0xFB, + 0xE0, 0x03, 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x00, + 0x01, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, 0x01, + 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, + 0x00, 0x0F, 0x80, 0x00, 0x3E, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0x81, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xE0, 0x00, + 0x07, 0xE0, 0x00, 0x07, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF0, 0x00, + 0x07, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, + 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, + 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF8, 0x00, + 0x03, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, + 0xF0, 0x7F, 0xFF, 0xF0, 0x00, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x1F, + 0xF0, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x07, 0x80, 0x0F, 0x00, + 0x1E, 0x00, 0x3C, 0x00, 0xF8, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, + 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x07, 0x80, 0x0F, 0x00, + 0x1E, 0x00, 0x3C, 0x00, 0xF8, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, + 0xF0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x00, 0xE7, 0x39, 0xCE, 0x31, 0x8C, + 0x63, 0x1C, 0xE7, 0x39, 0xCE, 0x31, 0x8C, 0x63, 0x9C, 0xE7, 0x38, 0x01, + 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x1F, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, + 0x00, 0x3E, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x0F, 0x00, + 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, + 0x00, 0x3E, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x0F, 0x00, + 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x1F, 0xF0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, + 0x00, 0x00, 0x7C, 0x00, 0xFC, 0x01, 0xFC, 0x01, 0xFC, 0x03, 0xFC, 0x03, + 0x9E, 0x07, 0x9E, 0x0F, 0x1E, 0x0F, 0x1E, 0x1E, 0x1E, 0x1C, 0x0F, 0x3C, + 0x0F, 0x78, 0x0F, 0x78, 0x0F, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xF3, 0x8C, 0x71, 0x80, 0x01, 0xFE, 0x01, 0xFF, 0xE0, + 0xFF, 0xF8, 0x7F, 0xFF, 0x1F, 0x0F, 0xC7, 0x81, 0xF0, 0x00, 0x7C, 0x00, + 0xFE, 0x07, 0xFF, 0x87, 0xFF, 0xE3, 0xFE, 0xF9, 0xF0, 0x7C, 0xF8, 0x1F, + 0x3E, 0x0F, 0xCF, 0x87, 0xF3, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, 0xEF, 0x81, + 0xE3, 0xF0, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, + 0x07, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF9, 0xF8, 0x0F, 0xFF, + 0xC1, 0xFF, 0xFE, 0x1F, 0xFF, 0xE1, 0xFC, 0x3F, 0x1F, 0x83, 0xF1, 0xF0, + 0x1F, 0x3E, 0x01, 0xF3, 0xE0, 0x1F, 0x3C, 0x01, 0xF3, 0xC0, 0x1F, 0x3C, + 0x03, 0xE7, 0xC0, 0x3E, 0x7E, 0x07, 0xC7, 0xF1, 0xFC, 0x7F, 0xFF, 0x87, + 0xFF, 0xF0, 0xFB, 0xFE, 0x0F, 0x9F, 0x80, 0x00, 0xFC, 0x01, 0xFF, 0xC0, + 0xFF, 0xF8, 0x7F, 0xFF, 0x3F, 0x0F, 0xCF, 0x81, 0xF7, 0xC0, 0x7D, 0xF0, + 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF8, 0x0F, + 0xBE, 0x07, 0xCF, 0xC3, 0xF1, 0xFF, 0xF8, 0x7F, 0xFC, 0x0F, 0xFE, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x80, 0x00, + 0x3E, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x1F, 0x00, 0x7E, 0x7C, + 0x07, 0xFD, 0xF0, 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xE3, 0xF8, 0x3E, + 0x07, 0xE1, 0xF0, 0x1F, 0x87, 0xC0, 0x3C, 0x3E, 0x00, 0xF0, 0xF8, 0x07, + 0xC3, 0xE0, 0x1F, 0x0F, 0x80, 0x7C, 0x3E, 0x03, 0xE0, 0xF8, 0x1F, 0x83, + 0xF0, 0xFE, 0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0x00, 0x7E, + 0x7C, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0xE0, 0x3F, 0xFE, 0x0F, 0xFF, 0xE3, + 0xF0, 0x7E, 0x7C, 0x07, 0xDF, 0x00, 0xFB, 0xE0, 0x1F, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x1F, 0x3F, + 0x07, 0xE3, 0xFF, 0xF8, 0x7F, 0xFE, 0x03, 0xFF, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0xF8, 0x1F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7C, 0x07, 0xC0, 0x3E, 0x0F, + 0xFE, 0x7F, 0xF3, 0xFF, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, + 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x78, 0x07, 0xC0, 0x3E, 0x01, + 0xF0, 0x0F, 0x80, 0xF8, 0x07, 0xC0, 0x00, 0x00, 0x7C, 0x7C, 0x07, 0xFD, + 0xF0, 0x3F, 0xF7, 0x81, 0xFF, 0xFE, 0x0F, 0xE3, 0xF8, 0x3E, 0x07, 0xE1, + 0xF8, 0x0F, 0x87, 0xC0, 0x3C, 0x1E, 0x00, 0xF0, 0xF8, 0x03, 0xC3, 0xE0, + 0x1F, 0x0F, 0x80, 0x78, 0x3E, 0x03, 0xE0, 0xF8, 0x1F, 0x83, 0xF0, 0xFE, + 0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xC0, 0x3F, 0xEF, 0x00, 0x3E, 0x7C, 0x00, + 0x01, 0xF0, 0x00, 0x07, 0xC3, 0xE0, 0x3E, 0x0F, 0x80, 0xF8, 0x3F, 0x0F, + 0xC0, 0x7F, 0xFE, 0x00, 0xFF, 0xF0, 0x00, 0xFE, 0x00, 0x00, 0x03, 0xE0, + 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, + 0x00, 0x0F, 0x80, 0x00, 0xF8, 0xF8, 0x0F, 0xBF, 0xE0, 0xFF, 0xFF, 0x0F, + 0xFF, 0xF1, 0xFC, 0x3F, 0x1F, 0x81, 0xF1, 0xF0, 0x1F, 0x1F, 0x01, 0xF1, + 0xE0, 0x1F, 0x3E, 0x03, 0xE3, 0xE0, 0x3E, 0x3E, 0x03, 0xE3, 0xE0, 0x3E, + 0x7C, 0x03, 0xE7, 0xC0, 0x7C, 0x7C, 0x07, 0xC7, 0xC0, 0x7C, 0x7C, 0x07, + 0xCF, 0x80, 0x78, 0x07, 0xC1, 0xF0, 0x7C, 0x3E, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, + 0x0F, 0x87, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC3, 0xE0, 0xF8, 0x3E, 0x00, + 0x00, 0x3E, 0x00, 0x78, 0x01, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0x80, 0x1F, + 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, + 0x1F, 0x00, 0x3C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x1F, + 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x03, 0xF0, 0x1F, 0xC0, 0x3F, 0x80, + 0x7E, 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, + 0x0F, 0x80, 0x00, 0x78, 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, + 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x07, 0xC7, 0xE0, 0x3E, 0x7E, 0x01, + 0xF7, 0xE0, 0x0F, 0xFE, 0x00, 0xFF, 0xE0, 0x07, 0xFF, 0x00, 0x3F, 0xFC, + 0x01, 0xFF, 0xE0, 0x0F, 0xDF, 0x00, 0xFC, 0xFC, 0x07, 0xC3, 0xE0, 0x3E, + 0x1F, 0x01, 0xF0, 0xFC, 0x0F, 0x83, 0xE0, 0xF8, 0x1F, 0x87, 0xC0, 0xFC, + 0x00, 0x07, 0xC1, 0xF0, 0x7C, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x1F, + 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x87, + 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC3, 0xE0, 0xF8, 0x3E, 0x00, 0x0F, 0x8F, + 0x83, 0xF0, 0x3E, 0xFF, 0x3F, 0xE0, 0xF7, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, + 0xFF, 0x1F, 0xC7, 0xF8, 0x7C, 0x7C, 0x0F, 0x81, 0xF1, 0xF0, 0x3E, 0x07, + 0xCF, 0x81, 0xF0, 0x3E, 0x3E, 0x07, 0xC0, 0xF8, 0xF8, 0x1F, 0x03, 0xE3, + 0xE0, 0x7C, 0x0F, 0x8F, 0x81, 0xF0, 0x3E, 0x7C, 0x0F, 0x81, 0xF1, 0xF0, + 0x3E, 0x07, 0xC7, 0xC0, 0xF8, 0x1F, 0x1F, 0x03, 0xE0, 0x7C, 0x7C, 0x0F, + 0x81, 0xE3, 0xE0, 0x7C, 0x0F, 0x8F, 0x81, 0xF0, 0x3E, 0x00, 0x0F, 0x8F, + 0x80, 0xFB, 0xFE, 0x0F, 0xFF, 0xF1, 0xFF, 0xFF, 0x1F, 0xC3, 0xF1, 0xF8, + 0x1F, 0x1F, 0x01, 0xF1, 0xF0, 0x1F, 0x3E, 0x01, 0xF3, 0xE0, 0x3E, 0x3E, + 0x03, 0xE3, 0xE0, 0x3E, 0x3C, 0x03, 0xE7, 0xC0, 0x3E, 0x7C, 0x07, 0xC7, + 0xC0, 0x7C, 0x7C, 0x07, 0xC7, 0x80, 0x7C, 0xF8, 0x07, 0x80, 0x00, 0xFE, + 0x00, 0x7F, 0xF0, 0x3F, 0xFF, 0x0F, 0xFF, 0xE3, 0xF8, 0xFE, 0x7C, 0x0F, + 0xDF, 0x00, 0xFB, 0xE0, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x1F, + 0xFC, 0x03, 0xEF, 0x80, 0x7D, 0xF8, 0x1F, 0x3F, 0x07, 0xE3, 0xFF, 0xF8, + 0x7F, 0xFE, 0x07, 0xFF, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xE7, 0xE0, 0x0F, + 0xBF, 0xC0, 0x7D, 0xFF, 0x81, 0xFF, 0xFE, 0x07, 0xF0, 0xFC, 0x1F, 0x81, + 0xF0, 0x7C, 0x07, 0xC3, 0xE0, 0x1F, 0x0F, 0x80, 0x7C, 0x3E, 0x01, 0xF0, + 0xF0, 0x07, 0xC3, 0xC0, 0x3E, 0x1F, 0x00, 0xF8, 0x7E, 0x07, 0xC1, 0xFC, + 0x7F, 0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xC0, 0xFB, 0xFE, 0x03, 0xE7, 0xE0, + 0x0F, 0x80, 0x00, 0x3E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x1F, + 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x01, + 0xF1, 0xF0, 0x7F, 0xDF, 0x0F, 0xFD, 0xF1, 0xFF, 0xFE, 0x3F, 0x8F, 0xE3, + 0xE0, 0x7E, 0x7C, 0x03, 0xE7, 0xC0, 0x3E, 0xF8, 0x03, 0xCF, 0x80, 0x3C, + 0xF8, 0x07, 0xCF, 0x80, 0x7C, 0xF8, 0x0F, 0x8F, 0x81, 0xF8, 0xFC, 0x3F, + 0x87, 0xFF, 0xF8, 0x7F, 0xFF, 0x83, 0xFF, 0xF0, 0x1F, 0x9F, 0x00, 0x01, + 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, + 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x0F, 0x8E, 0x1F, 0x7C, 0x3F, + 0xF0, 0xFF, 0xE1, 0xFC, 0x03, 0xF0, 0x07, 0xC0, 0x0F, 0x80, 0x3E, 0x00, + 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, + 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xE0, 0x00, 0x01, 0xFC, 0x01, 0xFF, 0xC0, + 0xFF, 0xF8, 0x7F, 0xFF, 0x3F, 0x0F, 0xCF, 0x81, 0xF3, 0xF0, 0x00, 0xFF, + 0x80, 0x3F, 0xFC, 0x07, 0xFF, 0xC0, 0x7F, 0xF8, 0x03, 0xFE, 0x00, 0x1F, + 0xBE, 0x03, 0xEF, 0xC1, 0xFB, 0xFF, 0xFC, 0x7F, 0xFE, 0x0F, 0xFF, 0x00, + 0xFE, 0x00, 0x0F, 0x81, 0xF0, 0x7C, 0x0F, 0x81, 0xF0, 0xFF, 0xBF, 0xF7, + 0xFE, 0x3E, 0x07, 0xC0, 0xF8, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, + 0xF8, 0x1F, 0x03, 0xE0, 0x7F, 0x0F, 0xE1, 0xFC, 0x1F, 0x80, 0x1F, 0x01, + 0xF1, 0xF0, 0x1F, 0x3E, 0x03, 0xE3, 0xE0, 0x3E, 0x3E, 0x03, 0xE3, 0xE0, + 0x3E, 0x3E, 0x03, 0xE7, 0xC0, 0x7C, 0x7C, 0x07, 0xC7, 0xC0, 0x7C, 0x7C, + 0x07, 0xC7, 0xC0, 0x7C, 0xF8, 0x0F, 0x8F, 0x81, 0xF8, 0xF8, 0x3F, 0x8F, + 0xFF, 0xF8, 0xFF, 0xFF, 0x07, 0xFD, 0xF0, 0x3F, 0x1F, 0x00, 0xF8, 0x0F, + 0xFE, 0x03, 0xEF, 0x81, 0xF3, 0xE0, 0x7C, 0xF8, 0x3E, 0x3E, 0x0F, 0x8F, + 0x87, 0xC1, 0xE1, 0xF0, 0x78, 0xF8, 0x1E, 0x3E, 0x07, 0x9F, 0x01, 0xF7, + 0x80, 0x7F, 0xE0, 0x1F, 0xF0, 0x03, 0xFC, 0x00, 0xFE, 0x00, 0x3F, 0x80, + 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x1F, 0x07, 0xFF, 0x03, 0xE0, 0xFB, + 0xE0, 0xFC, 0x1F, 0x7C, 0x1F, 0x87, 0xCF, 0x87, 0xF0, 0xF9, 0xF0, 0xFE, + 0x3E, 0x3E, 0x3D, 0xC7, 0xC3, 0xC7, 0xB9, 0xF0, 0x79, 0xE7, 0x3E, 0x0F, + 0x3C, 0xE7, 0x81, 0xEF, 0x1D, 0xF0, 0x3D, 0xE3, 0xBC, 0x07, 0xBC, 0x7F, + 0x80, 0xFF, 0x0F, 0xE0, 0x1F, 0xE1, 0xFC, 0x03, 0xF8, 0x3F, 0x00, 0x7F, + 0x07, 0xE0, 0x0F, 0xC0, 0xF8, 0x01, 0xF8, 0x1F, 0x00, 0x00, 0x0F, 0xC1, + 0xF8, 0x3F, 0x07, 0xC0, 0x7C, 0x3E, 0x01, 0xF9, 0xF8, 0x03, 0xEF, 0xC0, + 0x0F, 0xBE, 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0x80, 0x01, 0xFC, 0x00, 0x03, + 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x3E, 0xF8, + 0x01, 0xFB, 0xF0, 0x07, 0xC7, 0xC0, 0x3E, 0x1F, 0x81, 0xF8, 0x7E, 0x0F, + 0xC0, 0xF8, 0x00, 0x1F, 0x80, 0x7C, 0x3E, 0x03, 0xE0, 0xF8, 0x0F, 0x03, + 0xE0, 0x7C, 0x0F, 0x81, 0xE0, 0x3E, 0x0F, 0x80, 0xF8, 0x3C, 0x03, 0xE1, + 0xF0, 0x07, 0x87, 0x80, 0x1F, 0x3E, 0x00, 0x7C, 0xF0, 0x01, 0xF7, 0xC0, + 0x07, 0xDE, 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0xC0, 0x01, 0xFE, 0x00, 0x03, + 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, + 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x00, 0x01, + 0xF8, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x0F, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, + 0xFF, 0x87, 0xFF, 0xE0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, + 0xF0, 0x01, 0xFC, 0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x01, 0xF8, + 0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xFF, 0xF8, 0xFF, 0xFF, 0x1F, 0xFF, + 0xE3, 0xFF, 0xFC, 0x00, 0x00, 0x7C, 0x03, 0xF0, 0x1F, 0xC0, 0xFE, 0x03, + 0xE0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, + 0xE0, 0x0F, 0x80, 0x3C, 0x01, 0xF0, 0x1F, 0x80, 0x70, 0x01, 0xF8, 0x01, + 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, + 0xF0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xFC, 0x07, 0xE0, 0x0F, 0x80, + 0x1E, 0x00, 0x03, 0x81, 0xC0, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0C, 0x0E, + 0x07, 0x03, 0x81, 0xC0, 0xC0, 0xE0, 0x70, 0x38, 0x18, 0x1C, 0x0E, 0x07, + 0x03, 0x81, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x18, 0x1C, 0x0E, 0x07, 0x01, + 0x80, 0x80, 0x00, 0x00, 0x01, 0xE0, 0x07, 0xC0, 0x1F, 0x80, 0xFE, 0x00, + 0x78, 0x01, 0xE0, 0x07, 0x80, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, + 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x7E, 0x00, 0x38, 0x07, 0xE0, + 0x3E, 0x00, 0xF0, 0x07, 0xC0, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, + 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x01, 0xF8, 0x0F, 0xE0, 0x3F, 0x00, + 0xF8, 0x00, 0x0F, 0x00, 0x1F, 0xC1, 0xDF, 0xF0, 0xEE, 0x3F, 0xE6, 0x07, + 0xF0, 0x01, 0xE0}; + +const GFXglyph FreeSansBoldOblique18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 10, 0, 1}, // 0x20 ' ' + {0, 10, 25, 12, 4, -24}, // 0x21 '!' + {32, 13, 9, 17, 6, -25}, // 0x22 '"' + {47, 22, 24, 19, 1, -23}, // 0x23 '#' + {113, 19, 31, 19, 2, -26}, // 0x24 '$' + {187, 26, 26, 31, 5, -25}, // 0x25 '%' + {272, 21, 25, 25, 3, -24}, // 0x26 '&' + {338, 5, 9, 8, 6, -25}, // 0x27 ''' + {344, 13, 33, 12, 3, -25}, // 0x28 '(' + {398, 13, 33, 12, -1, -25}, // 0x29 ')' + {452, 12, 11, 14, 5, -25}, // 0x2A '*' + {469, 18, 16, 20, 3, -15}, // 0x2B '+' + {505, 7, 11, 10, 1, -4}, // 0x2C ',' + {515, 10, 4, 12, 2, -10}, // 0x2D '-' + {520, 6, 5, 10, 2, -4}, // 0x2E '.' + {524, 15, 25, 10, 0, -24}, // 0x2F '/' + {571, 18, 25, 19, 3, -24}, // 0x30 '0' + {628, 13, 25, 19, 6, -24}, // 0x31 '1' + {669, 21, 25, 19, 1, -24}, // 0x32 '2' + {735, 20, 25, 19, 2, -24}, // 0x33 '3' + {798, 19, 25, 19, 2, -24}, // 0x34 '4' + {858, 20, 24, 19, 2, -23}, // 0x35 '5' + {918, 19, 25, 19, 3, -24}, // 0x36 '6' + {978, 19, 24, 19, 5, -23}, // 0x37 '7' + {1035, 20, 25, 19, 2, -24}, // 0x38 '8' + {1098, 19, 25, 19, 2, -24}, // 0x39 '9' + {1158, 9, 18, 12, 4, -17}, // 0x3A ':' + {1179, 10, 24, 12, 3, -17}, // 0x3B ';' + {1209, 19, 17, 20, 3, -16}, // 0x3C '<' + {1250, 20, 12, 20, 2, -13}, // 0x3D '=' + {1280, 19, 17, 20, 1, -15}, // 0x3E '>' + {1321, 18, 26, 21, 6, -25}, // 0x3F '?' + {1380, 33, 31, 34, 3, -25}, // 0x40 '@' + {1508, 23, 26, 25, 1, -25}, // 0x41 'A' + {1583, 24, 26, 25, 3, -25}, // 0x42 'B' + {1661, 24, 26, 25, 4, -25}, // 0x43 'C' + {1739, 24, 26, 25, 3, -25}, // 0x44 'D' + {1817, 24, 26, 23, 3, -25}, // 0x45 'E' + {1895, 23, 26, 21, 3, -25}, // 0x46 'F' + {1970, 24, 26, 27, 4, -25}, // 0x47 'G' + {2048, 26, 26, 25, 2, -25}, // 0x48 'H' + {2133, 10, 26, 10, 2, -25}, // 0x49 'I' + {2166, 20, 26, 19, 2, -25}, // 0x4A 'J' + {2231, 26, 26, 25, 3, -25}, // 0x4B 'K' + {2316, 18, 26, 21, 3, -25}, // 0x4C 'L' + {2375, 31, 26, 29, 2, -25}, // 0x4D 'M' + {2476, 27, 26, 25, 2, -25}, // 0x4E 'N' + {2564, 25, 26, 27, 4, -25}, // 0x4F 'O' + {2646, 23, 26, 23, 3, -25}, // 0x50 'P' + {2721, 25, 27, 27, 4, -25}, // 0x51 'Q' + {2806, 24, 26, 25, 3, -25}, // 0x52 'R' + {2884, 22, 26, 23, 3, -25}, // 0x53 'S' + {2956, 21, 26, 21, 5, -25}, // 0x54 'T' + {3025, 24, 26, 25, 4, -25}, // 0x55 'U' + {3103, 22, 26, 23, 6, -25}, // 0x56 'V' + {3175, 32, 26, 33, 6, -25}, // 0x57 'W' + {3279, 27, 26, 23, 1, -25}, // 0x58 'X' + {3367, 22, 26, 23, 6, -25}, // 0x59 'Y' + {3439, 25, 26, 21, 1, -25}, // 0x5A 'Z' + {3521, 15, 33, 12, 1, -25}, // 0x5B '[' + {3583, 5, 25, 10, 5, -24}, // 0x5C '\' + {3599, 15, 33, 12, -1, -25}, // 0x5D ']' + {3661, 16, 15, 20, 4, -23}, // 0x5E '^' + {3691, 21, 3, 19, -2, 5}, // 0x5F '_' + {3699, 5, 5, 12, 6, -25}, // 0x60 '`' + {3703, 18, 19, 19, 2, -18}, // 0x61 'a' + {3746, 20, 26, 21, 2, -25}, // 0x62 'b' + {3811, 18, 19, 19, 3, -18}, // 0x63 'c' + {3854, 22, 26, 21, 3, -25}, // 0x64 'd' + {3926, 19, 19, 19, 2, -18}, // 0x65 'e' + {3972, 13, 26, 12, 3, -25}, // 0x66 'f' + {4015, 22, 27, 21, 1, -18}, // 0x67 'g' + {4090, 20, 26, 21, 2, -25}, // 0x68 'h' + {4155, 10, 26, 10, 2, -25}, // 0x69 'i' + {4188, 15, 34, 10, -2, -25}, // 0x6A 'j' + {4252, 21, 26, 19, 2, -25}, // 0x6B 'k' + {4321, 10, 26, 10, 2, -25}, // 0x6C 'l' + {4354, 30, 19, 31, 2, -18}, // 0x6D 'm' + {4426, 20, 19, 21, 2, -18}, // 0x6E 'n' + {4474, 19, 19, 21, 3, -18}, // 0x6F 'o' + {4520, 22, 27, 21, 0, -18}, // 0x70 'p' + {4595, 20, 27, 21, 3, -18}, // 0x71 'q' + {4663, 15, 19, 14, 2, -18}, // 0x72 'r' + {4699, 18, 19, 19, 2, -18}, // 0x73 's' + {4742, 11, 23, 12, 4, -22}, // 0x74 't' + {4774, 20, 19, 21, 3, -18}, // 0x75 'u' + {4822, 18, 19, 19, 5, -18}, // 0x76 'v' + {4865, 27, 19, 27, 4, -18}, // 0x77 'w' + {4930, 22, 19, 19, 1, -18}, // 0x78 'x' + {4983, 22, 27, 19, 1, -18}, // 0x79 'y' + {5058, 19, 19, 17, 1, -18}, // 0x7A 'z' + {5104, 14, 33, 14, 2, -25}, // 0x7B '{' + {5162, 9, 33, 10, 2, -25}, // 0x7C '|' + {5200, 14, 33, 14, 2, -25}, // 0x7D '}' + {5258, 17, 6, 20, 3, -10}}; // 0x7E '~' + +const GFXfont FreeSansBoldOblique18pt7b PROGMEM = { + (uint8_t *)FreeSansBoldOblique18pt7bBitmaps, + (GFXglyph *)FreeSansBoldOblique18pt7bGlyphs, 0x20, 0x7E, 42}; + +// Approx. 5943 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique24pt7b.h new file mode 100644 index 0000000..69ba7af --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique24pt7b.h @@ -0,0 +1,892 @@ +const uint8_t FreeSansBoldOblique24pt7bBitmaps[] PROGMEM = { + 0x01, 0xE0, 0x07, 0xF0, 0x1F, 0xC0, 0xFF, 0x03, 0xF8, 0x0F, 0xE0, 0x3F, + 0x80, 0xFE, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xE0, 0x1F, + 0x80, 0x7E, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0xF8, 0x03, 0xE0, 0x0F, + 0x80, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0xFE, 0x03, 0xF8, 0x00, + 0x7E, 0x0F, 0xDF, 0x83, 0xF7, 0xE0, 0xFF, 0xF0, 0x7E, 0xFC, 0x1F, 0xBF, + 0x07, 0xEF, 0xC1, 0xFB, 0xE0, 0x7C, 0xF8, 0x1F, 0x3C, 0x07, 0x8F, 0x01, + 0xE3, 0x80, 0x70, 0x00, 0x07, 0xC1, 0xF0, 0x00, 0x3E, 0x0F, 0x80, 0x03, + 0xE0, 0xF8, 0x00, 0x1F, 0x07, 0xC0, 0x01, 0xF0, 0x7C, 0x00, 0x0F, 0x83, + 0xE0, 0x00, 0xF8, 0x3E, 0x00, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF8, + 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0x0F, + 0x83, 0xE0, 0x00, 0x7C, 0x3E, 0x00, 0x07, 0xC1, 0xF0, 0x00, 0x3E, 0x0F, + 0x80, 0x03, 0xE0, 0xF8, 0x00, 0x1F, 0x07, 0xC0, 0x00, 0xF8, 0x7C, 0x00, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0x1F, 0x07, 0xC0, 0x00, 0xF8, 0x3E, + 0x00, 0x0F, 0x83, 0xE0, 0x00, 0x7C, 0x1F, 0x00, 0x07, 0xC1, 0xF0, 0x00, + 0x3E, 0x0F, 0x80, 0x01, 0xF0, 0xF8, 0x00, 0x1F, 0x07, 0xC0, 0x00, 0xF8, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x7F, 0xFF, 0x00, 0x3F, 0xFF, 0xE0, 0x1F, + 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0x07, 0xF3, 0x9F, 0xC1, 0xF8, 0xE3, 0xF0, + 0x7C, 0x38, 0xFC, 0x3F, 0x0E, 0x3F, 0x0F, 0xC7, 0x8F, 0xC3, 0xF1, 0xC0, + 0x00, 0xFE, 0x70, 0x00, 0x3F, 0xDC, 0x00, 0x0F, 0xFF, 0x00, 0x01, 0xFF, + 0xE0, 0x00, 0x3F, 0xFE, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, 0x00, + 0x0F, 0xFF, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x77, 0xF8, 0x00, 0x1C, 0xFE, + 0x00, 0x07, 0x3F, 0x8F, 0xE3, 0xCF, 0xE3, 0xF8, 0xE3, 0xF8, 0xFE, 0x38, + 0xFC, 0x3F, 0x8E, 0x7F, 0x0F, 0xF3, 0x9F, 0xC3, 0xFD, 0xFF, 0xE0, 0x7F, + 0xFF, 0xF0, 0x1F, 0xFF, 0xFC, 0x03, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x03, 0x80, + 0x00, 0x01, 0xE0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x07, + 0x80, 0x7F, 0xE0, 0x00, 0xF0, 0x0F, 0xFF, 0x00, 0x1E, 0x01, 0xFF, 0xF0, + 0x01, 0xC0, 0x3F, 0xFF, 0x80, 0x3C, 0x07, 0xE1, 0xF8, 0x07, 0x80, 0x78, + 0x07, 0x80, 0xF0, 0x0F, 0x80, 0x78, 0x0E, 0x00, 0xF0, 0x07, 0x81, 0xC0, + 0x0F, 0x00, 0xF8, 0x3C, 0x00, 0xF0, 0x0F, 0x07, 0x80, 0x0F, 0xC3, 0xF0, + 0xF0, 0x00, 0xFF, 0xFE, 0x0E, 0x00, 0x07, 0xFF, 0xC1, 0xE0, 0x00, 0x7F, + 0xF8, 0x3C, 0x00, 0x03, 0xFF, 0x07, 0x80, 0x00, 0x0F, 0xC0, 0x70, 0x00, + 0x00, 0x00, 0x0E, 0x03, 0xF0, 0x00, 0x01, 0xE0, 0xFF, 0xC0, 0x00, 0x3C, + 0x1F, 0xFE, 0x00, 0x03, 0x83, 0xFF, 0xE0, 0x00, 0x70, 0x7F, 0xFF, 0x00, + 0x0F, 0x0F, 0xC3, 0xF0, 0x01, 0xE0, 0xF0, 0x0F, 0x00, 0x3C, 0x1F, 0x00, + 0xF0, 0x03, 0x81, 0xE0, 0x0F, 0x00, 0x78, 0x1E, 0x01, 0xF0, 0x0F, 0x01, + 0xE0, 0x1E, 0x01, 0xE0, 0x1F, 0x87, 0xE0, 0x1C, 0x01, 0xFF, 0xFC, 0x03, + 0x80, 0x0F, 0xFF, 0x80, 0x78, 0x00, 0xFF, 0xF0, 0x0F, 0x00, 0x07, 0xFE, + 0x01, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x3F, 0xF0, + 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0x00, 0x03, 0xFF, 0xF8, 0x00, + 0x3F, 0x9F, 0xC0, 0x03, 0xF8, 0x7E, 0x00, 0x1F, 0xC3, 0xF0, 0x00, 0xFE, + 0x1F, 0x00, 0x07, 0xF1, 0xF8, 0x00, 0x3F, 0xCF, 0xC0, 0x01, 0xFE, 0xFC, + 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0xFF, 0xC0, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0x07, 0xC1, 0xFF, + 0xF8, 0x3E, 0x3F, 0xFF, 0xE3, 0xE3, 0xFE, 0x3F, 0x1F, 0x1F, 0xC1, 0xFD, + 0xF1, 0xFC, 0x07, 0xFF, 0x8F, 0xC0, 0x3F, 0xF8, 0xFE, 0x00, 0xFF, 0xC7, + 0xF0, 0x07, 0xFC, 0x3F, 0x80, 0x1F, 0xC1, 0xFC, 0x00, 0xFE, 0x0F, 0xF0, + 0x1F, 0xF8, 0x7F, 0xC1, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, + 0xFC, 0x3F, 0xFF, 0xCF, 0xE0, 0x7F, 0xF8, 0x7F, 0x80, 0xFF, 0x00, 0x00, + 0x7E, 0xFD, 0xFF, 0xEF, 0xDF, 0xBF, 0x7C, 0xF9, 0xE3, 0xC7, 0x00, 0x00, + 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0xC0, 0x07, 0xC0, 0x07, 0xC0, + 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xF0, 0x01, 0xF0, 0x01, 0xF8, + 0x00, 0xF8, 0x00, 0xFC, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x3E, 0x00, 0x1F, + 0x00, 0x1F, 0x80, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x03, 0xF0, 0x01, + 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, + 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x1E, + 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x3E, 0x00, 0x1F, 0x00, 0x07, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, + 0x1E, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x7C, + 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x07, 0x80, 0x03, 0xE0, 0x01, 0xF0, 0x00, + 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, + 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, + 0x00, 0x1F, 0x00, 0x1F, 0x80, 0x0F, 0x80, 0x07, 0xC0, 0x07, 0xE0, 0x03, + 0xE0, 0x03, 0xF0, 0x01, 0xF0, 0x01, 0xF8, 0x00, 0xF8, 0x00, 0xFC, 0x00, + 0x7C, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x01, 0xE0, 0x03, 0x80, 0x07, + 0x00, 0x0E, 0x07, 0x3C, 0x6F, 0xFF, 0xFF, 0xFF, 0xBF, 0xFE, 0x0F, 0xE0, + 0x1F, 0xC0, 0x7F, 0x81, 0xEF, 0x87, 0x8F, 0x0E, 0x1E, 0x08, 0x10, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, + 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0xFC, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF8, 0x00, + 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0xC7, 0xF1, 0xF8, 0xFE, 0x3F, + 0x8F, 0xE0, 0x38, 0x1C, 0x07, 0x03, 0xC0, 0xE0, 0xF0, 0xFC, 0x3C, 0x0C, + 0x00, 0x7F, 0xFD, 0xFF, 0xF7, 0xFF, 0x9F, 0xFE, 0xFF, 0xFB, 0xFF, 0xE0, + 0x7F, 0x7F, 0x7F, 0x7E, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x70, 0x00, 0x0E, + 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x03, + 0x80, 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x01, + 0xC0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x07, 0x00, 0x00, + 0x70, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, + 0x38, 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, + 0x0E, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, + 0x07, 0x00, 0x00, 0x70, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x1F, 0xFC, 0x00, 0x3F, 0xFF, 0x80, 0x3F, 0xFF, 0xC0, 0x3F, 0xFF, 0xF0, + 0x1F, 0xC7, 0xF8, 0x1F, 0xC1, 0xFE, 0x1F, 0xC0, 0x7F, 0x0F, 0xC0, 0x3F, + 0x8F, 0xE0, 0x1F, 0xC7, 0xF0, 0x0F, 0xE3, 0xF0, 0x07, 0xF3, 0xF8, 0x03, + 0xF9, 0xFC, 0x01, 0xFC, 0xFC, 0x01, 0xFE, 0xFE, 0x00, 0xFE, 0x7F, 0x00, + 0x7F, 0x3F, 0x80, 0x3F, 0x9F, 0xC0, 0x1F, 0xCF, 0xE0, 0x1F, 0xEF, 0xE0, + 0x0F, 0xE7, 0xF0, 0x07, 0xF3, 0xF8, 0x03, 0xF9, 0xFC, 0x03, 0xF8, 0xFE, + 0x01, 0xFC, 0x7F, 0x00, 0xFE, 0x3F, 0x80, 0xFE, 0x1F, 0xE0, 0x7F, 0x0F, + 0xF8, 0xFF, 0x03, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0x80, + 0x1F, 0xFF, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x0F, + 0x80, 0x0F, 0x80, 0x07, 0xC0, 0x07, 0xE0, 0x0F, 0xF0, 0x3F, 0xF9, 0xFF, + 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0x7F, 0xFF, 0x00, 0x3F, 0x80, 0x1F, 0x80, + 0x0F, 0xC0, 0x0F, 0xE0, 0x07, 0xF0, 0x03, 0xF8, 0x01, 0xF8, 0x01, 0xFC, + 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x1F, 0xC0, 0x0F, + 0xE0, 0x07, 0xF0, 0x03, 0xF0, 0x01, 0xF8, 0x01, 0xFC, 0x00, 0xFE, 0x00, + 0x7F, 0x00, 0x3F, 0x00, 0x3F, 0x80, 0x1F, 0xC0, 0x00, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xE0, + 0x0F, 0xFF, 0xFF, 0x00, 0xFF, 0x07, 0xFC, 0x07, 0xF0, 0x1F, 0xE0, 0x7F, + 0x00, 0x7F, 0x03, 0xF0, 0x03, 0xF8, 0x1F, 0x80, 0x1F, 0xC1, 0xF8, 0x00, + 0xFE, 0x0F, 0xC0, 0x0F, 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x07, 0xF0, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x7F, 0x80, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x1F, 0xF8, 0x00, 0x01, 0xFF, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xC0, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x03, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x0F, 0xFF, 0x80, 0x0F, 0xFF, 0xF0, 0x07, + 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xC0, 0xFE, 0x1F, 0xF0, 0x7F, 0x01, 0xFC, + 0x1F, 0x80, 0x7F, 0x07, 0xE0, 0x1F, 0xC3, 0xF0, 0x07, 0xF0, 0xFC, 0x01, + 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x80, 0x01, + 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x1F, 0xFC, 0x00, 0x07, 0xFF, 0x80, + 0x01, 0xFF, 0xE0, 0x00, 0x07, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x07, 0xF0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x7F, 0x3F, 0x80, + 0x3F, 0xCF, 0xE0, 0x0F, 0xE3, 0xF8, 0x07, 0xF8, 0xFF, 0x83, 0xFC, 0x3F, + 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xE0, + 0x03, 0xFF, 0xE0, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, + 0x7F, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, + 0x3F, 0xF8, 0x00, 0x3D, 0xFC, 0x00, 0x3C, 0xFE, 0x00, 0x3E, 0x7E, 0x00, + 0x3E, 0x7F, 0x00, 0x1E, 0x3F, 0x80, 0x1E, 0x1F, 0xC0, 0x1E, 0x0F, 0xC0, + 0x1F, 0x07, 0xE0, 0x1F, 0x07, 0xF0, 0x1F, 0x03, 0xF8, 0x1F, 0x01, 0xFC, + 0x0F, 0x80, 0xFC, 0x0F, 0x80, 0xFE, 0x0F, 0x80, 0x7F, 0x07, 0xFF, 0xFF, + 0xF7, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, + 0xFE, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, + 0xF0, 0x0F, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x80, 0x7C, 0x00, 0x00, 0x0F, + 0x80, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x03, 0xE3, 0xF0, 0x00, 0x7F, 0xFF, 0x80, 0x1F, 0xFF, 0xF8, 0x03, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xF0, 0x1F, 0xE1, 0xFF, 0x03, 0xF0, 0x1F, 0xE0, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xF0, 0xFE, 0x00, 0xFE, 0x1F, + 0xC0, 0x3F, 0x83, 0xF8, 0x07, 0xF0, 0x7F, 0x83, 0xFC, 0x0F, 0xFF, 0xFF, + 0x80, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0xFE, 0x00, 0x0F, + 0xFF, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xFE, + 0x00, 0x1F, 0xFF, 0x80, 0x1F, 0xFF, 0xE0, 0x1F, 0xFF, 0xF8, 0x1F, 0xC3, + 0xFC, 0x1F, 0x80, 0xFE, 0x0F, 0xC0, 0x3F, 0x0F, 0xC0, 0x00, 0x07, 0xE0, + 0x00, 0x07, 0xE0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xF8, 0xFC, 0x01, 0xF9, + 0xFF, 0x80, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFC, 0x3F, + 0xE1, 0xFF, 0x1F, 0xE0, 0x7F, 0x8F, 0xE0, 0x1F, 0xCF, 0xE0, 0x0F, 0xE7, + 0xF0, 0x07, 0xF3, 0xF0, 0x03, 0xF9, 0xF8, 0x01, 0xF8, 0xFC, 0x01, 0xFC, + 0x7E, 0x00, 0xFE, 0x3F, 0x00, 0xFE, 0x1F, 0xC0, 0xFF, 0x0F, 0xF0, 0xFF, + 0x03, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x1F, 0xFF, + 0x80, 0x07, 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xCF, + 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, + 0x9F, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x1F, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x03, 0xF0, 0x00, 0x01, 0xF8, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xE0, + 0x00, 0x03, 0xF0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xF0, 0x00, + 0x01, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x0F, 0xFF, 0x80, 0x07, 0xFF, 0xF0, 0x03, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, + 0xC0, 0xFE, 0x0F, 0xF0, 0x3E, 0x01, 0xFC, 0x1F, 0x80, 0x3F, 0x07, 0xC0, + 0x0F, 0xC1, 0xF0, 0x03, 0xF0, 0x7C, 0x01, 0xF8, 0x1F, 0x00, 0xFC, 0x03, + 0xF0, 0x7F, 0x00, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0x80, 0x07, 0xFF, 0xE0, + 0x07, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x81, 0xFE, 0x1F, 0xE0, 0xFE, 0x03, + 0xFC, 0x3F, 0x00, 0x7F, 0x1F, 0xC0, 0x1F, 0xC7, 0xE0, 0x07, 0xF3, 0xF8, + 0x01, 0xFC, 0xFE, 0x00, 0x7F, 0x3F, 0x80, 0x3F, 0x8F, 0xE0, 0x0F, 0xE3, + 0xFC, 0x07, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xC0, 0x03, 0xFF, 0xE0, 0x00, 0x3F, + 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xFC, 0x00, 0x3F, 0xFF, 0x00, + 0x3F, 0xFF, 0xC0, 0x3F, 0xFF, 0xF0, 0x3F, 0xC3, 0xF8, 0x3F, 0xC0, 0xFE, + 0x1F, 0xC0, 0x3F, 0x1F, 0xC0, 0x1F, 0x8F, 0xE0, 0x0F, 0xC7, 0xE0, 0x07, + 0xE7, 0xF0, 0x03, 0xF3, 0xF8, 0x01, 0xF9, 0xFC, 0x01, 0xFC, 0xFE, 0x00, + 0xFE, 0x7F, 0x00, 0xFE, 0x3F, 0xC0, 0xFF, 0x1F, 0xF0, 0xFF, 0x87, 0xFF, + 0xFF, 0xC3, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xE0, 0x3F, 0xF3, 0xF0, 0x07, + 0xE3, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x7E, 0x1F, 0xC0, 0x7E, 0x0F, 0xF0, 0xFF, 0x07, 0xFF, 0xFF, 0x01, + 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0x00, 0x0F, 0xFF, 0x00, + 0x01, 0xFC, 0x00, 0x00, 0x07, 0xF0, 0x7F, 0x07, 0xF0, 0x7E, 0x0F, 0xE0, + 0xFE, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x07, 0xF0, 0x7F, 0x07, + 0xE0, 0xFE, 0x0F, 0xE0, 0xFE, 0x00, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, + 0x7E, 0x03, 0xF8, 0x0F, 0xE0, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0x80, 0xFE, 0x03, 0xF8, 0x0F, 0xE0, + 0x03, 0x80, 0x1C, 0x00, 0x70, 0x03, 0xC0, 0x0E, 0x00, 0xF0, 0x0F, 0xC0, + 0x3C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xE0, 0x00, + 0x01, 0xF8, 0x00, 0x03, 0xFE, 0x00, 0x07, 0xFF, 0x80, 0x0F, 0xFF, 0xE0, + 0x1F, 0xFF, 0xF0, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0xC0, 0x1F, 0xFF, 0x80, + 0x0F, 0xFF, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x3F, 0xFE, + 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xF8, 0x00, + 0x7F, 0xFF, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0x00, 0x01, 0xF8, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x80, 0x1F, 0xFF, 0xFF, 0xC7, 0xFF, + 0xFF, 0xF1, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x8F, + 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, + 0xFF, 0x1F, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xF8, 0xFF, + 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0x80, 0x04, 0x00, 0x00, 0x01, 0xE0, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x0F, 0xFF, 0x00, 0x03, 0xFF, + 0xF8, 0x00, 0x7F, 0xFF, 0x80, 0x07, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xC0, + 0x01, 0xFF, 0xF0, 0x00, 0x1F, 0xFC, 0x00, 0x01, 0xFF, 0x00, 0x03, 0xFF, + 0xC0, 0x07, 0xFF, 0xE0, 0x0F, 0xFF, 0xF0, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, + 0xE0, 0x1F, 0xFF, 0xC0, 0x07, 0xFF, 0x80, 0x01, 0xFF, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, + 0x01, 0xFF, 0xF0, 0x07, 0xFF, 0xF8, 0x0F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFE, + 0x1F, 0xFF, 0xFE, 0x3F, 0xC1, 0xFF, 0x3F, 0x80, 0xFF, 0x7F, 0x00, 0x7F, + 0x7E, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xFC, 0x00, 0x0F, 0xF8, + 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0x7F, 0x80, 0x00, 0xFE, 0x00, + 0x01, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x0F, 0xE0, 0x00, 0x0F, 0xE0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, + 0x1F, 0xC0, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, + 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x00, + 0x01, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xE0, 0x1F, 0xF8, 0x00, 0x07, + 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0x80, 0x07, 0xE0, + 0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0x00, 0x03, 0xF0, 0x0F, 0xC0, 0x00, + 0x00, 0x0F, 0x80, 0xFC, 0x00, 0x00, 0x00, 0x3E, 0x07, 0xC0, 0x03, 0xF1, + 0xF1, 0xF0, 0x7C, 0x00, 0xFF, 0xCF, 0x07, 0x87, 0xE0, 0x1F, 0xFF, 0xF8, + 0x3C, 0x7E, 0x01, 0xF8, 0x7F, 0x81, 0xE3, 0xE0, 0x1F, 0x01, 0xF8, 0x0F, + 0x3E, 0x01, 0xF0, 0x0F, 0xC0, 0x79, 0xF0, 0x1F, 0x00, 0x7C, 0x03, 0xDF, + 0x00, 0xF0, 0x03, 0xE0, 0x1C, 0xF8, 0x0F, 0x80, 0x1E, 0x01, 0xE7, 0xC0, + 0x78, 0x00, 0xF0, 0x0F, 0x3C, 0x07, 0xC0, 0x0F, 0x00, 0xF3, 0xE0, 0x3C, + 0x00, 0x78, 0x07, 0x9F, 0x03, 0xE0, 0x07, 0x80, 0x78, 0xF8, 0x1F, 0x00, + 0x7C, 0x07, 0xC7, 0xC0, 0xF8, 0x07, 0xC0, 0x7C, 0x3E, 0x07, 0xC0, 0x7E, + 0x07, 0xC1, 0xF0, 0x3F, 0x07, 0xF8, 0xFC, 0x0F, 0x81, 0xFF, 0xFF, 0xFF, + 0xC0, 0x7E, 0x07, 0xFF, 0xBF, 0xFC, 0x01, 0xF0, 0x1F, 0xF8, 0xFF, 0x80, + 0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x03, 0x80, 0x00, 0x01, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, + 0xFE, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xF0, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x7F, + 0xF0, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0xFF, + 0xF0, 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x03, 0xFB, + 0xF8, 0x00, 0x07, 0xF3, 0xF8, 0x00, 0x07, 0xE3, 0xF8, 0x00, 0x0F, 0xE3, + 0xF8, 0x00, 0x0F, 0xC3, 0xF8, 0x00, 0x1F, 0xC3, 0xF8, 0x00, 0x1F, 0x83, + 0xF8, 0x00, 0x3F, 0x81, 0xFC, 0x00, 0x7F, 0x01, 0xFC, 0x00, 0x7F, 0x01, + 0xFC, 0x00, 0xFE, 0x01, 0xFC, 0x00, 0xFC, 0x01, 0xFC, 0x01, 0xFF, 0xFF, + 0xFC, 0x01, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, + 0xFE, 0x07, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFE, 0x0F, 0xE0, 0x00, + 0xFE, 0x1F, 0xC0, 0x00, 0xFE, 0x1F, 0xC0, 0x00, 0xFE, 0x3F, 0x80, 0x00, + 0xFE, 0x3F, 0x80, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, + 0x7F, 0x01, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, + 0xFC, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, + 0xFF, 0x03, 0xF8, 0x00, 0xFF, 0x03, 0xF8, 0x00, 0x7F, 0x07, 0xF0, 0x00, + 0x7F, 0x07, 0xF0, 0x00, 0x7F, 0x07, 0xF0, 0x00, 0x7E, 0x07, 0xF0, 0x00, + 0xFE, 0x0F, 0xF0, 0x03, 0xFC, 0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, + 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, + 0xF8, 0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xC0, 0x07, 0xFC, 0x1F, 0xC0, 0x01, + 0xFC, 0x1F, 0xC0, 0x01, 0xFC, 0x3F, 0x80, 0x01, 0xFC, 0x3F, 0x80, 0x01, + 0xFC, 0x3F, 0x80, 0x01, 0xFC, 0x3F, 0x80, 0x03, 0xF8, 0x7F, 0x00, 0x07, + 0xF8, 0x7F, 0x00, 0x0F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFE, + 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, + 0xFC, 0x01, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0x03, 0xFE, 0x07, 0xFC, 0x01, + 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, 0xE0, 0x00, 0x7F, 0x1F, 0xE0, 0x00, + 0x7F, 0x1F, 0xC0, 0x00, 0x7F, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xF8, 0xFE, 0x00, 0x03, + 0xF8, 0xFF, 0x00, 0x07, 0xF8, 0xFF, 0x00, 0x07, 0xF0, 0x7F, 0x80, 0x1F, + 0xF0, 0x7F, 0xE0, 0x7F, 0xE0, 0x3F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, + 0x80, 0x1F, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xF8, + 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, + 0xE0, 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, + 0xFC, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xF8, 0x03, 0xFE, 0x07, 0xF0, 0x01, + 0xFF, 0x07, 0xF0, 0x00, 0xFF, 0x07, 0xF0, 0x00, 0x7F, 0x07, 0xF0, 0x00, + 0x7F, 0x0F, 0xF0, 0x00, 0x7F, 0x0F, 0xE0, 0x00, 0x7F, 0x0F, 0xE0, 0x00, + 0x7F, 0x0F, 0xE0, 0x00, 0x7F, 0x0F, 0xE0, 0x00, 0x7F, 0x1F, 0xC0, 0x00, + 0x7F, 0x1F, 0xC0, 0x00, 0xFE, 0x1F, 0xC0, 0x00, 0xFE, 0x1F, 0xC0, 0x00, + 0xFE, 0x1F, 0xC0, 0x01, 0xFE, 0x3F, 0x80, 0x01, 0xFC, 0x3F, 0x80, 0x01, + 0xFC, 0x3F, 0x80, 0x03, 0xF8, 0x3F, 0x80, 0x07, 0xF8, 0x7F, 0x00, 0x0F, + 0xF0, 0x7F, 0x00, 0x1F, 0xF0, 0x7F, 0x00, 0x7F, 0xE0, 0x7F, 0xFF, 0xFF, + 0xC0, 0x7F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFE, + 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, + 0xFE, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xF8, 0x00, + 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, + 0xE0, 0x1F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, + 0xE0, 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, + 0xFE, 0x01, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, + 0xFE, 0x03, 0xF8, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x03, 0xF8, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, + 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, + 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x00, 0x00, 0x1F, 0xC0, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFF, + 0xF8, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, + 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x0F, 0xF8, + 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0x3F, 0x87, 0xF0, 0x00, 0x1F, 0xC7, 0xF0, + 0x00, 0x0F, 0xE3, 0xF8, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x01, 0xFC, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x3F, 0xFF, 0x3F, 0x00, 0x1F, 0xFF, 0xBF, 0x80, 0x0F, 0xFF, 0x9F, + 0xC0, 0x07, 0xFF, 0xCF, 0xE0, 0x03, 0xFF, 0xE7, 0xF0, 0x03, 0xFF, 0xF3, + 0xF8, 0x00, 0x01, 0xF9, 0xFC, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x00, 0xFC, + 0x7F, 0x80, 0x00, 0xFE, 0x3F, 0xC0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, + 0x87, 0xFC, 0x00, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, + 0xE0, 0x3F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFE, 0xF8, 0x03, 0xFF, 0xFC, + 0x78, 0x00, 0x7F, 0xFC, 0x3C, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x01, 0xFC, + 0x00, 0x0F, 0xE0, 0x3F, 0x80, 0x01, 0xFC, 0x07, 0xF0, 0x00, 0x3F, 0x80, + 0xFE, 0x00, 0x0F, 0xE0, 0x1F, 0xC0, 0x01, 0xFC, 0x07, 0xF0, 0x00, 0x3F, + 0x80, 0xFE, 0x00, 0x07, 0xF0, 0x1F, 0xC0, 0x00, 0xFE, 0x03, 0xF8, 0x00, + 0x3F, 0x80, 0xFF, 0x00, 0x07, 0xF0, 0x1F, 0xC0, 0x00, 0xFE, 0x03, 0xF8, + 0x00, 0x1F, 0xC0, 0x7F, 0x00, 0x07, 0xF0, 0x0F, 0xFF, 0xFF, 0xFE, 0x03, + 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, + 0x01, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF8, 0x0F, 0xE0, 0x00, + 0x7F, 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x3F, 0x80, 0x01, 0xFC, 0x07, 0xF0, + 0x00, 0x7F, 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x3F, 0x80, 0x01, 0xFC, 0x07, + 0xF0, 0x00, 0x3F, 0x80, 0xFE, 0x00, 0x0F, 0xE0, 0x1F, 0xC0, 0x01, 0xFC, + 0x07, 0xF0, 0x00, 0x3F, 0x80, 0xFE, 0x00, 0x07, 0xF0, 0x1F, 0xC0, 0x00, + 0xFE, 0x03, 0xF8, 0x00, 0x3F, 0x80, 0x7F, 0x00, 0x07, 0xF0, 0x1F, 0xC0, + 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x07, 0xF0, 0x3F, 0x80, 0xFE, 0x03, 0xF8, + 0x0F, 0xE0, 0x3F, 0x81, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, + 0x0F, 0xE0, 0x3F, 0x80, 0xFE, 0x03, 0xF8, 0x0F, 0xE0, 0x7F, 0x01, 0xFC, + 0x07, 0xF0, 0x1F, 0xC0, 0x7F, 0x03, 0xF8, 0x0F, 0xE0, 0x3F, 0x80, 0xFE, + 0x03, 0xF8, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0xFE, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x07, 0xF0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x0F, 0xE0, 0xFE, 0x03, 0xFC, 0x1F, 0xC0, 0x7F, 0x03, 0xF8, 0x0F, 0xE0, + 0xFE, 0x01, 0xFC, 0x1F, 0xC0, 0x3F, 0x83, 0xF8, 0x0F, 0xE0, 0x7F, 0x01, + 0xFC, 0x0F, 0xF0, 0xFF, 0x81, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xF8, 0x03, + 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0x80, 0x03, 0xFF, 0xE0, 0x00, 0x1F, 0xE0, + 0x00, 0x00, 0x00, 0xFE, 0x00, 0x0F, 0xF0, 0x0F, 0xF0, 0x00, 0xFF, 0x00, + 0x7F, 0x00, 0x1F, 0xF0, 0x03, 0xF8, 0x01, 0xFF, 0x00, 0x1F, 0xC0, 0x1F, + 0xE0, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x0F, 0xE0, 0x1F, 0xE0, 0x00, 0x7F, + 0x01, 0xFE, 0x00, 0x03, 0xF8, 0x1F, 0xE0, 0x00, 0x1F, 0xC1, 0xFE, 0x00, + 0x00, 0xFE, 0x1F, 0xE0, 0x00, 0x0F, 0xE3, 0xFE, 0x00, 0x00, 0x7F, 0x3F, + 0xC0, 0x00, 0x03, 0xFB, 0xFC, 0x00, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x01, + 0xFF, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x00, 0x7F, 0xFF, 0xC0, + 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x01, 0xFF, + 0x9F, 0xE0, 0x00, 0x0F, 0xF8, 0xFF, 0x00, 0x00, 0x7F, 0x83, 0xFC, 0x00, + 0x03, 0xF8, 0x1F, 0xF0, 0x00, 0x1F, 0xC0, 0x7F, 0x80, 0x01, 0xFC, 0x01, + 0xFE, 0x00, 0x0F, 0xE0, 0x0F, 0xF0, 0x00, 0x7F, 0x00, 0x3F, 0xC0, 0x03, + 0xF8, 0x01, 0xFF, 0x00, 0x3F, 0x80, 0x07, 0xF8, 0x01, 0xFC, 0x00, 0x3F, + 0xE0, 0x0F, 0xE0, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x07, 0xFC, 0x03, 0xF8, + 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x01, 0xFC, 0x00, 0x01, 0xFC, + 0x00, 0x03, 0xF8, 0x00, 0x03, 0xF8, 0x00, 0x03, 0xF8, 0x00, 0x03, 0xF8, + 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF0, + 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x0F, 0xE0, 0x00, 0x0F, 0xE0, + 0x00, 0x0F, 0xE0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x1F, 0xC0, + 0x00, 0x1F, 0xC0, 0x00, 0x1F, 0xC0, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0x80, + 0x00, 0x3F, 0x80, 0x00, 0x3F, 0x80, 0x00, 0x3F, 0x80, 0x00, 0x7F, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, + 0x80, 0x03, 0xFF, 0x80, 0xFF, 0xC0, 0x01, 0xFF, 0x80, 0x7F, 0xE0, 0x01, + 0xFF, 0xC0, 0x3F, 0xF0, 0x00, 0xFF, 0xE0, 0x3F, 0xF8, 0x00, 0xFF, 0xF0, + 0x1F, 0xFC, 0x00, 0x7F, 0xF8, 0x0F, 0xFE, 0x00, 0x7D, 0xF8, 0x07, 0xEF, + 0x00, 0x3E, 0xFC, 0x03, 0xF7, 0x80, 0x3F, 0xFE, 0x03, 0xFB, 0xC0, 0x1F, + 0x7F, 0x01, 0xFD, 0xE0, 0x1F, 0xBF, 0x00, 0xFE, 0xF0, 0x0F, 0x9F, 0x80, + 0x7E, 0x78, 0x0F, 0xDF, 0xC0, 0x7F, 0x3E, 0x07, 0xCF, 0xE0, 0x3F, 0x9F, + 0x07, 0xE7, 0xF0, 0x1F, 0xCF, 0x83, 0xE3, 0xF0, 0x0F, 0xE7, 0xC3, 0xF1, + 0xF8, 0x07, 0xE3, 0xE1, 0xF9, 0xFC, 0x07, 0xF1, 0xF0, 0xF8, 0xFE, 0x03, + 0xF8, 0xF8, 0xFC, 0x7F, 0x01, 0xFC, 0x7C, 0x7C, 0x3F, 0x00, 0xFC, 0x3E, + 0x7E, 0x1F, 0x80, 0x7E, 0x1F, 0x3E, 0x1F, 0xC0, 0x7F, 0x0F, 0xBF, 0x0F, + 0xE0, 0x3F, 0x87, 0xDF, 0x07, 0xE0, 0x1F, 0xC3, 0xFF, 0x83, 0xF0, 0x0F, + 0xC1, 0xFF, 0xC3, 0xF8, 0x0F, 0xE0, 0xFF, 0xC1, 0xFC, 0x07, 0xF0, 0x7F, + 0xE0, 0xFE, 0x03, 0xF8, 0x3F, 0xE0, 0x7E, 0x01, 0xFC, 0x1F, 0xF0, 0x3F, + 0x00, 0xFC, 0x0F, 0xF0, 0x3F, 0x80, 0xFE, 0x07, 0xF8, 0x1F, 0xC0, 0x7F, + 0x03, 0xF8, 0x0F, 0xC0, 0x00, 0x01, 0xFE, 0x00, 0x07, 0xE0, 0x3F, 0xC0, + 0x01, 0xFC, 0x07, 0xFC, 0x00, 0x3F, 0x80, 0xFF, 0x80, 0x07, 0xF0, 0x1F, + 0xF0, 0x00, 0xFC, 0x07, 0xFF, 0x00, 0x3F, 0x80, 0xFF, 0xE0, 0x07, 0xF0, + 0x1F, 0xFC, 0x00, 0xFE, 0x03, 0xFF, 0xC0, 0x1F, 0x80, 0xFF, 0xF8, 0x03, + 0xF0, 0x1F, 0xFF, 0x80, 0xFE, 0x03, 0xFB, 0xF0, 0x1F, 0xC0, 0x7E, 0x7E, + 0x03, 0xF8, 0x0F, 0xC7, 0xE0, 0x7E, 0x03, 0xF8, 0xFC, 0x0F, 0xC0, 0x7F, + 0x1F, 0x83, 0xF8, 0x0F, 0xE1, 0xF8, 0x7F, 0x01, 0xF8, 0x3F, 0x0F, 0xE0, + 0x3F, 0x07, 0xF1, 0xF8, 0x0F, 0xE0, 0x7E, 0x3F, 0x01, 0xFC, 0x0F, 0xCF, + 0xE0, 0x3F, 0x00, 0xFD, 0xFC, 0x07, 0xE0, 0x1F, 0xBF, 0x81, 0xFC, 0x03, + 0xF7, 0xE0, 0x3F, 0x80, 0x3F, 0xFC, 0x07, 0xF0, 0x07, 0xFF, 0x80, 0xFC, + 0x00, 0xFF, 0xF0, 0x1F, 0x80, 0x0F, 0xFC, 0x07, 0xF0, 0x01, 0xFF, 0x80, + 0xFE, 0x00, 0x3F, 0xF0, 0x1F, 0xC0, 0x03, 0xFE, 0x03, 0xF0, 0x00, 0x7F, + 0xC0, 0x7E, 0x00, 0x07, 0xF0, 0x1F, 0xC0, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x01, + 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xF0, 0x3F, 0xF8, 0x1F, 0xF0, 0x03, 0xFE, 0x07, 0xF0, 0x00, 0x7F, + 0x83, 0xF8, 0x00, 0x0F, 0xF1, 0xFE, 0x00, 0x03, 0xFC, 0x7F, 0x00, 0x00, + 0x7F, 0x3F, 0x80, 0x00, 0x1F, 0xCF, 0xE0, 0x00, 0x07, 0xF7, 0xF0, 0x00, + 0x01, 0xFD, 0xFC, 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x1F, 0xDF, 0xC0, + 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x03, 0xFB, 0xF8, 0x00, 0x00, 0xFE, 0xFE, + 0x00, 0x00, 0x3F, 0xBF, 0x80, 0x00, 0x0F, 0xEF, 0xE0, 0x00, 0x07, 0xF3, + 0xF8, 0x00, 0x01, 0xFC, 0xFE, 0x00, 0x00, 0xFE, 0x3F, 0xC0, 0x00, 0x7F, + 0x8F, 0xF0, 0x00, 0x1F, 0xC1, 0xFE, 0x00, 0x0F, 0xE0, 0x7F, 0xC0, 0x0F, + 0xF8, 0x1F, 0xFC, 0x0F, 0xFC, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, + 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x03, + 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, + 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xC1, 0xFE, 0x00, 0xFF, 0x83, 0xF8, + 0x00, 0xFF, 0x07, 0xF0, 0x00, 0xFE, 0x0F, 0xE0, 0x01, 0xFC, 0x1F, 0xC0, + 0x03, 0xF8, 0x7F, 0x00, 0x07, 0xF0, 0xFE, 0x00, 0x1F, 0xC1, 0xFC, 0x00, + 0x3F, 0x83, 0xF8, 0x00, 0xFE, 0x07, 0xF0, 0x07, 0xFC, 0x1F, 0xFF, 0xFF, + 0xF0, 0x3F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFE, + 0x03, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0x80, 0x0F, 0xE0, 0x00, 0x00, + 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x0F, + 0xE0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x01, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xF0, + 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xC0, 0x3F, 0xF0, 0x3F, + 0xF0, 0x1F, 0xF0, 0x03, 0xFE, 0x07, 0xF8, 0x00, 0x7F, 0x83, 0xFC, 0x00, + 0x0F, 0xF1, 0xFE, 0x00, 0x03, 0xFC, 0x7F, 0x00, 0x00, 0x7F, 0x3F, 0x80, + 0x00, 0x1F, 0xCF, 0xE0, 0x00, 0x07, 0xF3, 0xF0, 0x00, 0x01, 0xFD, 0xFC, + 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x1F, 0xDF, 0x80, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0x03, 0xFB, 0xF8, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x3F, + 0xBF, 0x80, 0x00, 0x0F, 0xEF, 0xE0, 0x01, 0x87, 0xF3, 0xF8, 0x00, 0xF1, + 0xFC, 0xFE, 0x00, 0x7C, 0xFE, 0x3F, 0xC0, 0x3F, 0xFF, 0x8F, 0xF0, 0x07, + 0xFF, 0xC1, 0xFE, 0x01, 0xFF, 0xE0, 0x7F, 0xC0, 0x3F, 0xF8, 0x1F, 0xFC, + 0x0F, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xC0, 0x0F, + 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0x9F, 0x80, + 0x01, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xFF, 0xFF, + 0xF0, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, + 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, 0xE0, + 0x03, 0xFE, 0x0F, 0xF0, 0x00, 0xFF, 0x07, 0xF0, 0x00, 0x3F, 0x83, 0xF8, + 0x00, 0x1F, 0xC1, 0xFC, 0x00, 0x0F, 0xC0, 0xFE, 0x00, 0x07, 0xE0, 0xFE, + 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x07, 0xF0, 0x3F, 0x80, 0x0F, 0xF0, 0x1F, + 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x07, + 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0x01, + 0xFC, 0x00, 0x7F, 0x80, 0xFE, 0x00, 0x1F, 0xC0, 0x7F, 0x00, 0x0F, 0xE0, + 0x3F, 0x80, 0x07, 0xF0, 0x1F, 0xC0, 0x03, 0xF8, 0x1F, 0xC0, 0x01, 0xFC, + 0x0F, 0xE0, 0x01, 0xFC, 0x07, 0xF0, 0x00, 0xFE, 0x03, 0xF8, 0x00, 0x7F, + 0x01, 0xFC, 0x00, 0x3F, 0x81, 0xFC, 0x00, 0x1F, 0xC0, 0xFE, 0x00, 0x0F, + 0xE0, 0x7F, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x7F, + 0xFF, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, + 0xF8, 0x0F, 0xFF, 0xFF, 0xF0, 0x3F, 0xC0, 0x7F, 0xC1, 0xFE, 0x00, 0xFF, + 0x07, 0xF0, 0x01, 0xFC, 0x3F, 0x80, 0x07, 0xF0, 0xFE, 0x00, 0x1F, 0xC3, + 0xF8, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, + 0xFE, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x0F, 0xFF, 0xF8, 0x00, 0x03, 0xFF, + 0xF0, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0xFC, + 0x00, 0x00, 0x07, 0xF3, 0xF8, 0x00, 0x1F, 0xCF, 0xE0, 0x00, 0x7E, 0x3F, + 0x80, 0x03, 0xF8, 0xFF, 0x00, 0x1F, 0xE3, 0xFF, 0x01, 0xFF, 0x07, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, + 0xF0, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x3F, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, + 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x01, 0xFC, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x7F, + 0x07, 0xF0, 0x00, 0x7F, 0x07, 0xF0, 0x00, 0xFE, 0x0F, 0xE0, 0x00, 0xFE, + 0x0F, 0xE0, 0x00, 0xFE, 0x0F, 0xE0, 0x00, 0xFE, 0x0F, 0xE0, 0x00, 0xFE, + 0x0F, 0xE0, 0x01, 0xFC, 0x1F, 0xC0, 0x01, 0xFC, 0x1F, 0xC0, 0x01, 0xFC, + 0x1F, 0xC0, 0x01, 0xFC, 0x1F, 0xC0, 0x01, 0xFC, 0x3F, 0x80, 0x03, 0xF8, + 0x3F, 0x80, 0x03, 0xF8, 0x3F, 0x80, 0x03, 0xF8, 0x3F, 0x80, 0x03, 0xF8, + 0x3F, 0x80, 0x07, 0xF0, 0x7F, 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x07, 0xF0, + 0x7F, 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x0F, 0xE0, + 0xFE, 0x00, 0x0F, 0xE0, 0xFE, 0x00, 0x0F, 0xE0, 0xFE, 0x00, 0x0F, 0xE0, + 0xFE, 0x00, 0x1F, 0xC0, 0xFE, 0x00, 0x1F, 0xC0, 0xFF, 0x00, 0x3F, 0x80, + 0xFF, 0xC0, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFE, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xE0, 0x00, + 0x01, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x03, 0xF9, 0xFC, 0x00, 0x0F, 0xE7, + 0xF0, 0x00, 0x7F, 0x1F, 0xC0, 0x01, 0xFC, 0x7F, 0x00, 0x0F, 0xE1, 0xFC, + 0x00, 0x3F, 0x87, 0xF0, 0x01, 0xFC, 0x1F, 0xC0, 0x07, 0xF0, 0x3F, 0x00, + 0x3F, 0x80, 0xFC, 0x00, 0xFC, 0x03, 0xF0, 0x07, 0xF0, 0x0F, 0xC0, 0x1F, + 0x80, 0x3F, 0x80, 0xFE, 0x00, 0xFE, 0x03, 0xF0, 0x03, 0xF8, 0x1F, 0xC0, + 0x0F, 0xE0, 0x7E, 0x00, 0x1F, 0x83, 0xF8, 0x00, 0x7E, 0x0F, 0xC0, 0x01, + 0xF8, 0x7E, 0x00, 0x07, 0xE1, 0xF8, 0x00, 0x1F, 0x8F, 0xC0, 0x00, 0x7E, + 0x3F, 0x00, 0x01, 0xF9, 0xF8, 0x00, 0x07, 0xE7, 0xE0, 0x00, 0x0F, 0xFF, + 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0x00, + 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x01, 0xFC, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, + 0xFE, 0x00, 0x7F, 0x80, 0x1F, 0xFF, 0xC0, 0x0F, 0xF0, 0x03, 0xFB, 0xF8, + 0x01, 0xFE, 0x00, 0x7F, 0x7F, 0x00, 0x7F, 0xC0, 0x1F, 0xCF, 0xE0, 0x0F, + 0xF8, 0x03, 0xF9, 0xFC, 0x03, 0xFF, 0x00, 0xFE, 0x3F, 0x80, 0x7F, 0xE0, + 0x1F, 0xC7, 0xF0, 0x1F, 0xFC, 0x07, 0xF0, 0x7E, 0x03, 0xFF, 0x80, 0xFE, + 0x0F, 0xC0, 0x7D, 0xF0, 0x1F, 0x81, 0xF8, 0x1F, 0xBE, 0x07, 0xF0, 0x3F, + 0x03, 0xE7, 0xC0, 0xFC, 0x07, 0xE0, 0xFC, 0xF8, 0x3F, 0x80, 0xFC, 0x1F, + 0x1F, 0x07, 0xE0, 0x1F, 0x83, 0xE3, 0xE0, 0xFC, 0x03, 0xF0, 0xFC, 0x7C, + 0x3F, 0x00, 0x7E, 0x1F, 0x0F, 0x87, 0xE0, 0x0F, 0xC7, 0xE1, 0xF1, 0xF8, + 0x01, 0xF8, 0xF8, 0x3E, 0x3F, 0x00, 0x3F, 0x3F, 0x07, 0xCF, 0xC0, 0x07, + 0xE7, 0xC0, 0xF9, 0xF8, 0x00, 0xFC, 0xF8, 0x1F, 0x3E, 0x00, 0x1F, 0xBE, + 0x03, 0xEF, 0xC0, 0x01, 0xF7, 0xC0, 0x7D, 0xF0, 0x00, 0x3F, 0xF8, 0x0F, + 0xFE, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x3F, 0xF0, + 0x00, 0x1F, 0xF0, 0x07, 0xFC, 0x00, 0x03, 0xFE, 0x00, 0xFF, 0x80, 0x00, + 0x7F, 0x80, 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x03, 0xFC, 0x00, 0x01, 0xFC, + 0x00, 0x7F, 0x80, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x07, 0xFC, 0x00, 0xFF, + 0x00, 0x1F, 0xE0, 0x07, 0xF8, 0x00, 0xFF, 0x00, 0x7F, 0x80, 0x03, 0xFC, + 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x7F, 0x80, 0x00, 0xFF, 0x07, 0xF8, 0x00, + 0x03, 0xFC, 0x3F, 0x80, 0x00, 0x1F, 0xE3, 0xF8, 0x00, 0x00, 0x7F, 0x3F, + 0xC0, 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x00, + 0x7F, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xFC, 0x00, + 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, 0x1F, + 0xF0, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, + 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0xFE, + 0x00, 0x00, 0x0F, 0xE7, 0xF0, 0x00, 0x00, 0xFF, 0x3F, 0xC0, 0x00, 0x0F, + 0xF1, 0xFE, 0x00, 0x00, 0xFF, 0x07, 0xF8, 0x00, 0x07, 0xF0, 0x3F, 0xC0, + 0x00, 0x7F, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x07, 0xF8, 0x00, 0x7F, 0x80, + 0x3F, 0xC0, 0x07, 0xF8, 0x01, 0xFF, 0x00, 0x7F, 0x80, 0x07, 0xF8, 0x07, + 0xFC, 0x00, 0x3F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF7, 0xF8, 0x00, 0x7F, + 0xBF, 0xC0, 0x07, 0xF8, 0xFE, 0x00, 0x3F, 0x87, 0xF8, 0x03, 0xFC, 0x3F, + 0xC0, 0x3F, 0xC0, 0xFE, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x3F, 0xC1, + 0xFE, 0x00, 0xFE, 0x0F, 0xE0, 0x07, 0xF0, 0xFE, 0x00, 0x3F, 0x8F, 0xE0, + 0x00, 0xFE, 0x7F, 0x00, 0x07, 0xF7, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x01, + 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x3F, 0xF8, 0x00, 0x01, 0xFF, + 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, + 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, + 0x01, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, + 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x7F, 0xF8, 0x03, 0xFF, 0x80, + 0x1F, 0xFC, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0x00, 0x7E, 0x00, 0x03, 0xF0, + 0x00, 0x1F, 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0x7E, 0x00, 0x03, + 0xF0, 0x00, 0x1F, 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0x7E, 0x00, + 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0x7E, + 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xF8, 0x00, 0x0F, 0xC0, 0x00, + 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xF8, 0x00, 0x0F, 0xC0, + 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x01, 0xF8, 0x00, 0x0F, + 0xC0, 0x00, 0x7E, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0x00, 0x01, 0xFF, 0xC0, + 0x0F, 0xFE, 0x00, 0x7F, 0xF0, 0x07, 0xFF, 0x80, 0x3F, 0xFC, 0x00, 0x81, + 0xC3, 0xC7, 0x8F, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0xC7, 0x8F, 0x1E, + 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x8F, 0x1E, 0x3C, 0x38, 0x70, 0xE1, + 0xC3, 0x87, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0x00, 0x7F, 0xF8, 0x03, 0xFF, + 0xC0, 0x1F, 0xFC, 0x00, 0xFF, 0xE0, 0x07, 0xFF, 0x00, 0x01, 0xF8, 0x00, + 0x1F, 0xC0, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF8, + 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0x00, 0x03, + 0xF8, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0x00, + 0x03, 0xF8, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x7F, + 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, + 0x7F, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, + 0x00, 0x7F, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x01, 0xFF, + 0xE0, 0x0F, 0xFE, 0x00, 0x7F, 0xF0, 0x03, 0xFF, 0x80, 0x3F, 0xFC, 0x00, + 0x00, 0x1F, 0x80, 0x00, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x07, + 0xFC, 0x00, 0x7F, 0xE0, 0x03, 0xFF, 0x80, 0x3E, 0xFC, 0x01, 0xF3, 0xE0, + 0x1F, 0x1F, 0x01, 0xF8, 0xF8, 0x0F, 0x87, 0xE0, 0xFC, 0x3F, 0x07, 0xC0, + 0xF8, 0x7C, 0x07, 0xC7, 0xE0, 0x3E, 0x3E, 0x01, 0xFB, 0xF0, 0x0F, 0xDF, + 0x00, 0x3F, 0xF0, 0x01, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xE0, 0xF8, 0xF0, 0xF1, 0xE1, + 0xC3, 0xC3, 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xF8, 0x7F, 0x03, 0xFC, 0x3F, 0x00, + 0xFE, 0x1F, 0x80, 0x7E, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0x80, 0x1F, + 0xFF, 0xC0, 0x7F, 0xFF, 0xC0, 0xFF, 0xFF, 0xE0, 0xFF, 0xF7, 0xF0, 0xFF, + 0x83, 0xF8, 0xFF, 0x01, 0xF8, 0x7F, 0x00, 0xFC, 0x7F, 0x00, 0xFE, 0x3F, + 0x80, 0x7F, 0x1F, 0xC0, 0x7F, 0x8F, 0xF0, 0xFF, 0x87, 0xFF, 0xFF, 0xC3, + 0xFF, 0xFF, 0xE0, 0xFF, 0xF7, 0xF8, 0x3F, 0xF3, 0xFC, 0x07, 0xE0, 0x00, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xE0, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x3F, 0x80, 0x1F, + 0x9F, 0xFC, 0x03, 0xF7, 0xFF, 0xC0, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, + 0x83, 0xFF, 0x0F, 0xF0, 0x7F, 0x80, 0xFF, 0x0F, 0xE0, 0x1F, 0xE3, 0xF8, + 0x01, 0xFC, 0x7F, 0x00, 0x3F, 0x8F, 0xC0, 0x07, 0xF1, 0xF8, 0x00, 0xFE, + 0x7F, 0x00, 0x1F, 0xCF, 0xC0, 0x03, 0xF9, 0xF8, 0x00, 0xFE, 0x3F, 0x00, + 0x1F, 0xC7, 0xE0, 0x03, 0xF9, 0xFC, 0x00, 0xFE, 0x3F, 0xC0, 0x3F, 0xC7, + 0xF8, 0x0F, 0xF0, 0xFF, 0x83, 0xFC, 0x1F, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xF0, 0x1F, 0x9F, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xFC, 0x00, 0x7F, 0xFF, 0x80, 0x7F, 0xFF, + 0xE0, 0x7F, 0xFF, 0xF0, 0x7F, 0x83, 0xFC, 0x7F, 0x00, 0xFE, 0x3F, 0x00, + 0x7F, 0x3F, 0x80, 0x3F, 0x9F, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x0F, 0xE0, + 0x00, 0x07, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xFC, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x7F, 0x3F, 0x80, 0x3F, 0x9F, + 0xE0, 0x3F, 0x87, 0xF8, 0x3F, 0x83, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xC0, 0x0F, 0xFF, 0x80, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x0F, 0xE0, 0x01, 0xFC, 0x7F, 0x00, 0x3F, + 0xF3, 0xF8, 0x03, 0xFF, 0xDF, 0x80, 0x7F, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, + 0xE0, 0x3F, 0xC3, 0xFF, 0x03, 0xFC, 0x0F, 0xF8, 0x3F, 0xC0, 0x3F, 0x81, + 0xFC, 0x01, 0xFC, 0x1F, 0xC0, 0x07, 0xE0, 0xFE, 0x00, 0x3F, 0x07, 0xF0, + 0x03, 0xF8, 0x7F, 0x00, 0x1F, 0x83, 0xF8, 0x00, 0xFC, 0x1F, 0xC0, 0x07, + 0xE0, 0xFE, 0x00, 0x3F, 0x07, 0xF0, 0x03, 0xF0, 0x3F, 0x80, 0x3F, 0x81, + 0xFC, 0x01, 0xFC, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC3, 0xFF, 0x01, 0xFF, + 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFC, 0x00, 0xFF, 0xCF, + 0xE0, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0xFC, 0x00, + 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x7F, 0x87, 0xF8, + 0x7F, 0x01, 0xFE, 0x7F, 0x00, 0x7F, 0x3F, 0x80, 0x3F, 0xBF, 0x80, 0x1F, + 0xDF, 0xC0, 0x0F, 0xEF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFD, 0xFC, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x3F, 0x80, 0x3F, 0x9F, 0xE0, 0x3F, 0x87, 0xF8, 0x3F, 0xC3, 0xFF, + 0xFF, 0xC0, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0x80, 0x0F, 0xFF, 0x80, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x1F, 0xF0, 0x0F, 0xF8, 0x07, 0xFE, + 0x01, 0xFF, 0x80, 0xFE, 0x00, 0x3F, 0x80, 0x0F, 0xC0, 0x03, 0xF0, 0x01, + 0xFC, 0x03, 0xFF, 0xF1, 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, 0x80, 0xFE, + 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x7F, 0x00, + 0x1F, 0xC0, 0x07, 0xE0, 0x01, 0xF8, 0x00, 0xFE, 0x00, 0x3F, 0x80, 0x0F, + 0xE0, 0x03, 0xF0, 0x00, 0xFC, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x07, 0xF0, + 0x01, 0xF8, 0x00, 0x7E, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x07, 0xC3, 0xF8, + 0x01, 0xFF, 0x9F, 0x80, 0x1F, 0xFE, 0xFC, 0x01, 0xFF, 0xFF, 0xE0, 0x1F, + 0xFF, 0xFF, 0x01, 0xFE, 0x1F, 0xF8, 0x1F, 0xE0, 0x3F, 0x80, 0xFE, 0x01, + 0xFC, 0x0F, 0xE0, 0x0F, 0xE0, 0x7F, 0x00, 0x3F, 0x07, 0xF0, 0x01, 0xF8, + 0x3F, 0x80, 0x0F, 0x81, 0xF8, 0x00, 0x7C, 0x1F, 0xC0, 0x07, 0xE0, 0xFE, + 0x00, 0x3F, 0x07, 0xF0, 0x01, 0xF0, 0x3F, 0x80, 0x1F, 0x81, 0xFC, 0x00, + 0xFC, 0x0F, 0xE0, 0x0F, 0xE0, 0x7F, 0x80, 0xFF, 0x03, 0xFE, 0x1F, 0xF0, + 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFC, 0x01, 0xFF, 0xF7, 0xE0, 0x07, + 0xFE, 0x7F, 0x00, 0x0F, 0xC3, 0xF0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x01, + 0xFC, 0x0F, 0xE0, 0x0F, 0xC0, 0x7F, 0x00, 0xFE, 0x03, 0xFC, 0x1F, 0xE0, + 0x1F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xFC, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xE0, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xFE, 0x0F, + 0xC0, 0x1F, 0xCF, 0xFE, 0x03, 0xFB, 0xFF, 0xE0, 0x7F, 0xFF, 0xFE, 0x0F, + 0xFF, 0xFF, 0xC3, 0xFF, 0x07, 0xF8, 0x7F, 0x80, 0x7F, 0x0F, 0xE0, 0x0F, + 0xE1, 0xFC, 0x01, 0xFC, 0x7F, 0x00, 0x3F, 0x0F, 0xE0, 0x07, 0xE1, 0xFC, + 0x01, 0xFC, 0x3F, 0x00, 0x3F, 0x87, 0xE0, 0x07, 0xF1, 0xFC, 0x00, 0xFC, + 0x3F, 0x80, 0x1F, 0x87, 0xF0, 0x07, 0xF0, 0xFC, 0x00, 0xFE, 0x1F, 0x80, + 0x1F, 0xC7, 0xF0, 0x03, 0xF0, 0xFE, 0x00, 0x7E, 0x1F, 0xC0, 0x1F, 0xC3, + 0xF0, 0x03, 0xF8, 0xFE, 0x00, 0x7F, 0x1F, 0xC0, 0x0F, 0xC0, 0x01, 0xFC, + 0x07, 0xF0, 0x1F, 0x80, 0x7E, 0x03, 0xF8, 0x0F, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xE0, 0x3F, 0x80, 0xFE, + 0x03, 0xF8, 0x0F, 0xC0, 0x3F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7E, + 0x03, 0xF8, 0x0F, 0xE0, 0x3F, 0x80, 0xFC, 0x03, 0xF0, 0x1F, 0xC0, 0x7F, + 0x01, 0xFC, 0x07, 0xE0, 0x1F, 0x80, 0xFE, 0x03, 0xF8, 0x00, 0x00, 0x0F, + 0xE0, 0x01, 0xFC, 0x00, 0x3F, 0x80, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0xFE, + 0x00, 0x1F, 0xC0, 0x03, 0xF8, 0x00, 0x7E, 0x00, 0x1F, 0xC0, 0x03, 0xF8, + 0x00, 0x7F, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x7F, 0x00, 0x0F, 0xE0, + 0x01, 0xFC, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x01, 0xFC, 0x00, 0x3F, 0x80, + 0x07, 0xF0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x07, 0xF0, 0x00, 0xFE, 0x00, + 0x1F, 0x80, 0x03, 0xF0, 0x00, 0xFE, 0x00, 0x1F, 0xC0, 0x03, 0xF8, 0x00, + 0x7E, 0x00, 0x0F, 0xC0, 0x03, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xC0, 0x0F, + 0xF8, 0x03, 0xFE, 0x00, 0x7E, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x7F, 0x00, 0xFE, 0x07, 0xE0, 0x3F, 0xC0, 0x7E, 0x07, + 0xF8, 0x0F, 0xE0, 0xFF, 0x00, 0xFE, 0x1F, 0xC0, 0x0F, 0xE3, 0xF8, 0x00, + 0xFC, 0x7F, 0x00, 0x0F, 0xCF, 0xE0, 0x01, 0xFD, 0xFC, 0x00, 0x1F, 0xFF, + 0x80, 0x01, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0x80, 0x03, 0xFF, 0xFC, 0x00, + 0x3F, 0xFF, 0xC0, 0x03, 0xFE, 0xFE, 0x00, 0x3F, 0xCF, 0xE0, 0x03, 0xF0, + 0xFE, 0x00, 0x7F, 0x07, 0xF0, 0x07, 0xF0, 0x7F, 0x00, 0x7F, 0x07, 0xF8, + 0x07, 0xE0, 0x3F, 0x80, 0x7E, 0x03, 0xF8, 0x0F, 0xE0, 0x3F, 0xC0, 0xFE, + 0x01, 0xFC, 0x0F, 0xC0, 0x1F, 0xE0, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0x80, + 0x7E, 0x03, 0xF8, 0x0F, 0xE0, 0x3F, 0x80, 0xFC, 0x03, 0xF0, 0x1F, 0xC0, + 0x7F, 0x01, 0xFC, 0x07, 0xE0, 0x3F, 0x80, 0xFE, 0x03, 0xF8, 0x0F, 0xC0, + 0x3F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x7E, 0x03, 0xF8, 0x0F, 0xE0, + 0x3F, 0x80, 0xFC, 0x03, 0xF0, 0x1F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xE0, + 0x1F, 0x80, 0xFE, 0x03, 0xF8, 0x00, 0x07, 0xF0, 0xFC, 0x03, 0xF0, 0x07, + 0xE3, 0xFF, 0x0F, 0xFC, 0x07, 0xEF, 0xFF, 0x3F, 0xFE, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xF8, 0x7F, 0xF0, 0xFF, + 0x0F, 0xE0, 0x3F, 0xC0, 0x7F, 0x0F, 0xE0, 0x3F, 0x80, 0x7F, 0x1F, 0xC0, + 0x3F, 0x80, 0x7E, 0x1F, 0xC0, 0x3F, 0x00, 0x7E, 0x1F, 0xC0, 0x3F, 0x00, + 0xFE, 0x1F, 0x80, 0x7F, 0x00, 0xFE, 0x3F, 0x80, 0x7F, 0x00, 0xFC, 0x3F, + 0x80, 0x7F, 0x00, 0xFC, 0x3F, 0x80, 0x7E, 0x01, 0xFC, 0x3F, 0x00, 0x7E, + 0x01, 0xFC, 0x3F, 0x00, 0xFE, 0x01, 0xFC, 0x7F, 0x00, 0xFE, 0x01, 0xF8, + 0x7F, 0x00, 0xFE, 0x01, 0xF8, 0x7F, 0x00, 0xFC, 0x03, 0xF8, 0x7E, 0x01, + 0xFC, 0x03, 0xF8, 0x7E, 0x01, 0xFC, 0x03, 0xF8, 0xFE, 0x01, 0xFC, 0x03, + 0xF0, 0xFE, 0x01, 0xF8, 0x03, 0xF0, 0xFE, 0x01, 0xF8, 0x07, 0xF0, 0x07, + 0xF0, 0xFE, 0x00, 0xFE, 0x7F, 0xF0, 0x1F, 0x9F, 0xFF, 0x03, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0xFE, 0x1F, 0xF8, 0x3F, 0xC3, 0xFC, 0x03, 0xF8, 0x7F, + 0x00, 0x7F, 0x0F, 0xE0, 0x0F, 0xE3, 0xF8, 0x01, 0xF8, 0x7F, 0x00, 0x3F, + 0x0F, 0xC0, 0x0F, 0xE1, 0xF8, 0x01, 0xFC, 0x7F, 0x00, 0x3F, 0x8F, 0xE0, + 0x07, 0xE1, 0xFC, 0x00, 0xFC, 0x3F, 0x00, 0x3F, 0x87, 0xE0, 0x07, 0xF1, + 0xFC, 0x00, 0xFE, 0x3F, 0x80, 0x1F, 0x87, 0xF0, 0x03, 0xF0, 0xFC, 0x00, + 0xFE, 0x3F, 0x80, 0x1F, 0xC7, 0xF0, 0x03, 0xF8, 0xFE, 0x00, 0x7E, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x1F, 0xFF, 0x00, 0x1F, 0xFF, 0xE0, 0x0F, 0xFF, + 0xFC, 0x07, 0xFF, 0xFF, 0x83, 0xFC, 0x1F, 0xE1, 0xFE, 0x03, 0xFC, 0xFF, + 0x00, 0xFF, 0x3F, 0x80, 0x1F, 0xDF, 0xC0, 0x07, 0xF7, 0xF0, 0x01, 0xFD, + 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0x03, + 0xFB, 0xF8, 0x00, 0xFE, 0xFE, 0x00, 0x3F, 0xBF, 0x80, 0x1F, 0xCF, 0xF0, + 0x0F, 0xF3, 0xFC, 0x07, 0xF8, 0x7F, 0x83, 0xFC, 0x1F, 0xFF, 0xFE, 0x03, + 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x80, 0x0F, 0xFF, 0x80, 0x00, 0x7F, 0x00, + 0x00, 0x01, 0xFC, 0x3F, 0x00, 0x0F, 0xCF, 0xFE, 0x00, 0x7E, 0xFF, 0xF8, + 0x07, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x01, 0xFF, 0x87, 0xF8, 0x0F, + 0xF0, 0x1F, 0xE0, 0xFF, 0x00, 0xFF, 0x07, 0xF0, 0x03, 0xF8, 0x3F, 0x80, + 0x1F, 0xC1, 0xF8, 0x00, 0xFE, 0x0F, 0xC0, 0x07, 0xF0, 0xFE, 0x00, 0x3F, + 0x87, 0xF0, 0x01, 0xFC, 0x3F, 0x00, 0x1F, 0xC1, 0xF8, 0x00, 0xFE, 0x1F, + 0xC0, 0x07, 0xF0, 0xFE, 0x00, 0x7F, 0x07, 0xF8, 0x07, 0xF8, 0x3F, 0xC0, + 0x7F, 0x81, 0xFF, 0x87, 0xF8, 0x1F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFC, + 0x07, 0xF7, 0xFF, 0xC0, 0x3F, 0x1F, 0xF8, 0x01, 0xF8, 0x7F, 0x00, 0x1F, + 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x07, 0xE0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x87, 0xF0, + 0x0F, 0xFE, 0x7F, 0x01, 0xFF, 0xF7, 0xE0, 0x3F, 0xFF, 0x7E, 0x07, 0xFF, + 0xFF, 0xE0, 0xFF, 0x07, 0xFE, 0x1F, 0xE0, 0x3F, 0xE3, 0xFC, 0x03, 0xFC, + 0x3F, 0x80, 0x1F, 0xC7, 0xF0, 0x01, 0xFC, 0x7F, 0x00, 0x1F, 0xC7, 0xF0, + 0x01, 0xF8, 0xFE, 0x00, 0x1F, 0x8F, 0xE0, 0x03, 0xF8, 0xFE, 0x00, 0x3F, + 0x8F, 0xE0, 0x03, 0xF8, 0xFE, 0x00, 0x7F, 0x0F, 0xE0, 0x07, 0xF0, 0xFE, + 0x00, 0xFF, 0x0F, 0xF0, 0x1F, 0xF0, 0x7F, 0x87, 0xFF, 0x07, 0xFF, 0xFF, + 0xE0, 0x3F, 0xFF, 0x7E, 0x03, 0xFF, 0xEF, 0xE0, 0x1F, 0xFC, 0xFE, 0x00, + 0x7F, 0x0F, 0xC0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x1F, 0x80, + 0x00, 0x03, 0xF8, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xF8, 0x00, 0x07, + 0xF0, 0xF0, 0x7F, 0x3F, 0x07, 0xE7, 0xE0, 0x7E, 0xFE, 0x0F, 0xFF, 0xE0, + 0xFF, 0xFE, 0x0F, 0xFC, 0x00, 0xFF, 0x00, 0x0F, 0xE0, 0x01, 0xFC, 0x00, + 0x1F, 0xC0, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x03, 0xF8, 0x00, 0x3F, 0x80, + 0x03, 0xF8, 0x00, 0x3F, 0x00, 0x03, 0xF0, 0x00, 0x7F, 0x00, 0x07, 0xF0, + 0x00, 0x7F, 0x00, 0x07, 0xE0, 0x00, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0xFE, + 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0xFC, 0x07, + 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0x1F, 0xC0, 0x7F, 0x1F, + 0xC0, 0x7F, 0x1F, 0xE0, 0x00, 0x1F, 0xFC, 0x00, 0x1F, 0xFF, 0xC0, 0x0F, + 0xFF, 0xF0, 0x07, 0xFF, 0xF8, 0x03, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, + 0x0F, 0xFE, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFE, 0xFC, 0x00, 0xFE, 0xFE, + 0x00, 0xFE, 0xFF, 0x03, 0xFC, 0x7F, 0xFF, 0xF8, 0x7F, 0xFF, 0xF8, 0x3F, + 0xFF, 0xE0, 0x1F, 0xFF, 0xC0, 0x03, 0xFE, 0x00, 0x03, 0xF0, 0x1F, 0xC0, + 0x7F, 0x01, 0xFC, 0x07, 0xE0, 0x3F, 0x80, 0xFE, 0x1F, 0xFF, 0x7F, 0xFD, + 0xFF, 0xFF, 0xFF, 0xC7, 0xF0, 0x1F, 0xC0, 0x7E, 0x01, 0xF8, 0x0F, 0xE0, + 0x3F, 0x80, 0xFE, 0x03, 0xF0, 0x0F, 0xC0, 0x7F, 0x01, 0xFC, 0x07, 0xE0, + 0x1F, 0x80, 0xFE, 0x03, 0xF8, 0x0F, 0xE0, 0x3F, 0xF0, 0xFF, 0xC3, 0xFF, + 0x07, 0xFC, 0x0F, 0xE0, 0x0F, 0xC0, 0x0F, 0xE1, 0xF8, 0x01, 0xFC, 0x7F, + 0x00, 0x3F, 0x0F, 0xE0, 0x0F, 0xE1, 0xFC, 0x01, 0xFC, 0x3F, 0x00, 0x3F, + 0x87, 0xE0, 0x07, 0xE1, 0xFC, 0x00, 0xFC, 0x3F, 0x80, 0x3F, 0x87, 0xF0, + 0x07, 0xF0, 0xFC, 0x00, 0xFE, 0x1F, 0x80, 0x1F, 0x87, 0xF0, 0x03, 0xF0, + 0xFE, 0x00, 0xFE, 0x1F, 0x80, 0x1F, 0xC3, 0xF0, 0x03, 0xF0, 0xFE, 0x00, + 0x7E, 0x1F, 0xC0, 0x1F, 0xC3, 0xF8, 0x07, 0xF8, 0x7F, 0x01, 0xFF, 0x0F, + 0xF0, 0x7F, 0xC1, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0x03, 0xFF, 0xEF, + 0xE0, 0x3F, 0xF9, 0xFC, 0x01, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0x7F, + 0x00, 0x3F, 0xBF, 0x80, 0x3F, 0x8F, 0xC0, 0x1F, 0xC7, 0xE0, 0x1F, 0xC3, + 0xF0, 0x0F, 0xC1, 0xFC, 0x0F, 0xE0, 0xFE, 0x07, 0xE0, 0x7F, 0x07, 0xF0, + 0x3F, 0x83, 0xF0, 0x0F, 0xC3, 0xF8, 0x07, 0xE1, 0xF8, 0x03, 0xF1, 0xFC, + 0x01, 0xF8, 0xFC, 0x00, 0xFC, 0xFC, 0x00, 0x7E, 0x7E, 0x00, 0x3F, 0x7E, + 0x00, 0x0F, 0xBF, 0x00, 0x07, 0xFF, 0x00, 0x03, 0xFF, 0x80, 0x01, 0xFF, + 0x80, 0x00, 0xFF, 0x80, 0x00, 0x7F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x1F, + 0xE0, 0x00, 0x00, 0xFE, 0x03, 0xF8, 0x0F, 0xFF, 0xC0, 0x7F, 0x01, 0xFF, + 0xF8, 0x1F, 0xE0, 0x3F, 0x7F, 0x03, 0xFC, 0x0F, 0xEF, 0xE0, 0xFF, 0x81, + 0xF9, 0xFC, 0x1F, 0xF0, 0x7F, 0x3F, 0x83, 0xFE, 0x0F, 0xC3, 0xF0, 0xFF, + 0xC3, 0xF8, 0x7E, 0x1E, 0xF8, 0x7E, 0x0F, 0xC7, 0xDF, 0x1F, 0xC1, 0xF8, + 0xFB, 0xE3, 0xF0, 0x3F, 0x1E, 0x7C, 0x7E, 0x07, 0xE7, 0xCF, 0x9F, 0x80, + 0xFC, 0xF1, 0xF3, 0xF0, 0x1F, 0xBE, 0x3E, 0xFC, 0x03, 0xF7, 0x87, 0xDF, + 0x80, 0x7E, 0xF0, 0xFF, 0xE0, 0x0F, 0xFE, 0x1F, 0xFC, 0x01, 0xFF, 0x83, + 0xFF, 0x00, 0x3F, 0xF0, 0x7F, 0xE0, 0x07, 0xFC, 0x0F, 0xF8, 0x00, 0x7F, + 0x81, 0xFF, 0x00, 0x0F, 0xF0, 0x3F, 0xC0, 0x01, 0xFC, 0x07, 0xF8, 0x00, + 0x3F, 0x80, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x07, 0xF8, 0x1F, 0xE0, 0x7F, + 0x80, 0x7F, 0x03, 0xF8, 0x03, 0xF8, 0x3F, 0x80, 0x1F, 0xE3, 0xF8, 0x00, + 0x7F, 0x3F, 0x80, 0x03, 0xF9, 0xFC, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x7F, + 0xFC, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x7F, 0xC0, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x01, 0xFF, 0x80, 0x00, + 0x1F, 0xFE, 0x00, 0x01, 0xFF, 0xF0, 0x00, 0x1F, 0xDF, 0xC0, 0x01, 0xFC, + 0xFE, 0x00, 0x1F, 0xE7, 0xF8, 0x00, 0xFE, 0x1F, 0xC0, 0x0F, 0xE0, 0xFE, + 0x00, 0xFF, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xC0, 0xFF, 0x00, 0xFF, 0x00, + 0x0F, 0xE0, 0x03, 0xF0, 0x7F, 0x00, 0x3F, 0x83, 0xF8, 0x01, 0xF8, 0x1F, + 0xC0, 0x1F, 0xC0, 0xFE, 0x00, 0xFC, 0x03, 0xF8, 0x0F, 0xE0, 0x1F, 0xC0, + 0x7E, 0x00, 0xFE, 0x07, 0xE0, 0x07, 0xF0, 0x3F, 0x00, 0x3F, 0x83, 0xF0, + 0x01, 0xFC, 0x1F, 0x80, 0x0F, 0xE1, 0xF8, 0x00, 0x3F, 0x0F, 0xC0, 0x01, + 0xF8, 0xFC, 0x00, 0x0F, 0xC7, 0xC0, 0x00, 0x7F, 0x7E, 0x00, 0x03, 0xFB, + 0xE0, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x03, 0xFF, 0x80, + 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, + 0x3F, 0xC0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x03, 0xF8, 0x00, + 0x01, 0xFF, 0x80, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x07, + 0xF8, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x80, + 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xF8, + 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x07, + 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x7F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, + 0xE0, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xC0, 0x0F, 0xF0, 0x07, 0xFC, + 0x01, 0xFE, 0x00, 0xFF, 0x80, 0x3E, 0x00, 0x0F, 0x80, 0x07, 0xE0, 0x01, + 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0xF8, + 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x07, 0xE0, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x3F, 0x00, 0x7F, 0x80, 0x1F, 0x80, 0x07, 0xE0, 0x03, 0xFC, 0x00, 0x3F, + 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xC0, + 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x01, + 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xF8, 0x01, 0xFE, 0x00, 0x7F, + 0x80, 0x0F, 0xE0, 0x01, 0xF8, 0x00, 0x00, 0x78, 0x03, 0xC0, 0x1C, 0x01, + 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1C, 0x01, 0xE0, 0x0F, 0x00, 0x78, + 0x03, 0xC0, 0x1C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3C, 0x01, + 0xE0, 0x0F, 0x00, 0x78, 0x03, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, + 0x03, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0x80, 0x3C, 0x01, + 0xE0, 0x0F, 0x00, 0x70, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x70, + 0x07, 0x80, 0x3C, 0x00, 0x00, 0x7E, 0x00, 0x1F, 0xC0, 0x07, 0xF0, 0x01, + 0xFE, 0x00, 0x7F, 0x80, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x1F, + 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x0F, 0x80, + 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xF0, 0x00, + 0xFF, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x07, 0xF8, 0x03, 0xF0, 0x00, 0xF8, + 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x1F, + 0x80, 0x07, 0xC0, 0x01, 0xF0, 0x07, 0xFC, 0x01, 0xFE, 0x00, 0xFF, 0x80, + 0x3F, 0xC0, 0x0F, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0xFF, 0x80, 0x07, 0xFF, + 0x03, 0xDF, 0xFE, 0x0F, 0xF0, 0x7F, 0xFB, 0x80, 0xFF, 0xE0, 0x01, 0xFF, + 0x00, 0x03, 0xF0}; + +const GFXglyph FreeSansBoldOblique24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 13, 0, 1}, // 0x20 ' ' + {0, 14, 34, 16, 5, -33}, // 0x21 '!' + {60, 18, 12, 22, 8, -33}, // 0x22 '"' + {87, 29, 33, 26, 2, -31}, // 0x23 '#' + {207, 26, 42, 26, 3, -35}, // 0x24 '$' + {344, 36, 34, 42, 6, -32}, // 0x25 '%' + {497, 29, 35, 34, 4, -33}, // 0x26 '&' + {624, 7, 12, 11, 8, -33}, // 0x27 ''' + {635, 17, 44, 16, 4, -33}, // 0x28 '(' + {729, 17, 44, 16, 0, -34}, // 0x29 ')' + {823, 15, 15, 18, 7, -33}, // 0x2A '*' + {852, 24, 22, 27, 4, -21}, // 0x2B '+' + {918, 10, 15, 13, 1, -6}, // 0x2C ',' + {937, 14, 6, 16, 3, -15}, // 0x2D '-' + {948, 8, 7, 13, 3, -6}, // 0x2E '.' + {955, 20, 34, 13, 0, -32}, // 0x2F '/' + {1040, 25, 35, 26, 4, -33}, // 0x30 '0' + {1150, 17, 33, 26, 8, -32}, // 0x31 '1' + {1221, 29, 34, 26, 1, -33}, // 0x32 '2' + {1345, 26, 35, 26, 3, -33}, // 0x33 '3' + {1459, 25, 32, 26, 3, -31}, // 0x34 '4' + {1559, 27, 34, 26, 3, -32}, // 0x35 '5' + {1674, 25, 35, 26, 4, -33}, // 0x36 '6' + {1784, 26, 33, 26, 6, -32}, // 0x37 '7' + {1892, 26, 35, 26, 3, -33}, // 0x38 '8' + {2006, 25, 35, 26, 4, -33}, // 0x39 '9' + {2116, 12, 25, 16, 5, -24}, // 0x3A ':' + {2154, 14, 33, 16, 3, -24}, // 0x3B ';' + {2212, 26, 23, 27, 4, -22}, // 0x3C '<' + {2287, 26, 18, 27, 3, -19}, // 0x3D '=' + {2346, 26, 23, 27, 1, -21}, // 0x3E '>' + {2421, 24, 35, 29, 8, -34}, // 0x3F '?' + {2526, 45, 41, 46, 3, -34}, // 0x40 '@' + {2757, 32, 34, 34, 1, -33}, // 0x41 'A' + {2893, 32, 34, 34, 4, -33}, // 0x42 'B' + {3029, 32, 36, 34, 5, -34}, // 0x43 'C' + {3173, 32, 34, 34, 4, -33}, // 0x44 'D' + {3309, 32, 34, 31, 4, -33}, // 0x45 'E' + {3445, 32, 34, 29, 3, -33}, // 0x46 'F' + {3581, 33, 36, 37, 5, -34}, // 0x47 'G' + {3730, 35, 34, 34, 3, -33}, // 0x48 'H' + {3879, 14, 34, 13, 3, -33}, // 0x49 'I' + {3939, 27, 35, 26, 3, -33}, // 0x4A 'J' + {4058, 37, 34, 34, 3, -33}, // 0x4B 'K' + {4216, 24, 34, 29, 4, -33}, // 0x4C 'L' + {4318, 41, 34, 39, 3, -33}, // 0x4D 'M' + {4493, 35, 34, 34, 3, -33}, // 0x4E 'N' + {4642, 34, 36, 37, 5, -34}, // 0x4F 'O' + {4795, 31, 34, 31, 4, -33}, // 0x50 'P' + {4927, 34, 37, 37, 5, -34}, // 0x51 'Q' + {5085, 33, 34, 34, 4, -33}, // 0x52 'R' + {5226, 30, 36, 31, 4, -34}, // 0x53 'S' + {5361, 28, 34, 29, 7, -33}, // 0x54 'T' + {5480, 32, 35, 34, 6, -33}, // 0x55 'U' + {5620, 30, 34, 31, 8, -33}, // 0x56 'V' + {5748, 43, 34, 44, 8, -33}, // 0x57 'W' + {5931, 37, 34, 31, 1, -33}, // 0x58 'X' + {6089, 29, 34, 31, 9, -33}, // 0x59 'Y' + {6213, 33, 34, 29, 1, -33}, // 0x5A 'Z' + {6354, 21, 43, 16, 1, -33}, // 0x5B '[' + {6467, 7, 36, 13, 6, -34}, // 0x5C '\' + {6499, 21, 43, 16, -1, -33}, // 0x5D ']' + {6612, 21, 20, 27, 6, -32}, // 0x5E '^' + {6665, 29, 4, 26, -3, 6}, // 0x5F '_' + {6680, 7, 7, 16, 8, -35}, // 0x60 '`' + {6687, 25, 26, 26, 2, -24}, // 0x61 'a' + {6769, 27, 35, 29, 3, -33}, // 0x62 'b' + {6888, 25, 26, 26, 4, -24}, // 0x63 'c' + {6970, 29, 35, 29, 4, -33}, // 0x64 'd' + {7097, 25, 26, 26, 3, -24}, // 0x65 'e' + {7179, 18, 34, 16, 4, -33}, // 0x66 'f' + {7256, 29, 35, 29, 2, -24}, // 0x67 'g' + {7383, 27, 34, 29, 3, -33}, // 0x68 'h' + {7498, 14, 34, 13, 3, -33}, // 0x69 'i' + {7558, 19, 44, 13, -2, -33}, // 0x6A 'j' + {7663, 28, 34, 26, 3, -33}, // 0x6B 'k' + {7782, 14, 34, 13, 3, -33}, // 0x6C 'l' + {7842, 40, 25, 42, 3, -24}, // 0x6D 'm' + {7967, 27, 25, 29, 3, -24}, // 0x6E 'n' + {8052, 26, 26, 29, 4, -24}, // 0x6F 'o' + {8137, 29, 35, 29, 1, -24}, // 0x70 'p' + {8264, 28, 35, 29, 3, -24}, // 0x71 'q' + {8387, 20, 25, 18, 3, -24}, // 0x72 'r' + {8450, 24, 26, 26, 3, -24}, // 0x73 's' + {8528, 14, 32, 16, 5, -30}, // 0x74 't' + {8584, 27, 26, 29, 4, -24}, // 0x75 'u' + {8672, 25, 25, 26, 6, -24}, // 0x76 'v' + {8751, 35, 25, 37, 6, -24}, // 0x77 'w' + {8861, 29, 25, 26, 1, -24}, // 0x78 'x' + {8952, 29, 35, 26, 2, -24}, // 0x79 'y' + {9079, 26, 25, 23, 1, -24}, // 0x7A 'z' + {9161, 18, 43, 18, 4, -33}, // 0x7B '{' + {9258, 13, 43, 13, 3, -33}, // 0x7C '|' + {9328, 18, 43, 18, 2, -33}, // 0x7D '}' + {9425, 22, 8, 27, 5, -14}}; // 0x7E '~' + +const GFXfont FreeSansBoldOblique24pt7b PROGMEM = { + (uint8_t *)FreeSansBoldOblique24pt7bBitmaps, + (GFXglyph *)FreeSansBoldOblique24pt7bGlyphs, 0x20, 0x7E, 56}; + +// Approx. 10119 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique9pt7b.h new file mode 100644 index 0000000..263e615 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansBoldOblique9pt7b.h @@ -0,0 +1,226 @@ +const uint8_t FreeSansBoldOblique9pt7bBitmaps[] PROGMEM = { + 0x21, 0x8E, 0x73, 0x18, 0xC6, 0x21, 0x19, 0xCE, 0x00, 0xEF, 0xDF, 0xBE, + 0x68, 0x80, 0x06, 0xC1, 0x99, 0xFF, 0xBF, 0xF1, 0xB0, 0x66, 0x0C, 0xC7, + 0xFC, 0xFF, 0x8C, 0x83, 0x30, 0x64, 0x00, 0x02, 0x00, 0xF0, 0x7F, 0x1D, + 0x73, 0xEE, 0x78, 0x0F, 0x00, 0xF8, 0x0F, 0xC1, 0xBB, 0xA7, 0x74, 0xEF, + 0xF8, 0xFE, 0x04, 0x00, 0x80, 0x3C, 0x11, 0xF8, 0x8E, 0x66, 0x31, 0x90, + 0xCE, 0x83, 0xF4, 0x07, 0xB0, 0x00, 0x9E, 0x04, 0xFC, 0x26, 0x31, 0x98, + 0xC4, 0x7E, 0x20, 0xF0, 0x07, 0x80, 0xFC, 0x1D, 0xC1, 0xDC, 0x1F, 0x80, + 0xE0, 0x3E, 0x37, 0x77, 0xE3, 0xEE, 0x3C, 0xE3, 0xCF, 0xFE, 0x3C, 0xE0, + 0xFF, 0xE8, 0x06, 0x06, 0x0C, 0x18, 0x38, 0x30, 0x70, 0x60, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0x60, 0x70, 0x30, 0x0C, 0x0E, 0x06, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x06, 0x0E, 0x0C, 0x1C, 0x18, 0x30, 0x60, 0x60, + 0x32, 0xBF, 0x9C, 0xD2, 0x40, 0x0C, 0x06, 0x07, 0x1F, 0xFF, 0xF0, 0xC0, + 0xE0, 0x60, 0x77, 0x72, 0x6C, 0xFF, 0xC0, 0xFC, 0x02, 0x02, 0x04, 0x04, + 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x0F, 0x07, 0xE3, + 0x9D, 0xC7, 0x71, 0xDC, 0x7E, 0x1F, 0x8E, 0xE3, 0xB8, 0xEE, 0x73, 0xF8, + 0x3C, 0x00, 0x04, 0x3B, 0xF7, 0xE1, 0xC3, 0x06, 0x1C, 0x38, 0x70, 0xC1, + 0x87, 0x00, 0x0F, 0x87, 0xFC, 0xE3, 0xB8, 0x70, 0x0E, 0x03, 0x80, 0xF0, + 0x38, 0x1E, 0x07, 0x01, 0xC0, 0x7F, 0xCF, 0xF8, 0x0F, 0xC7, 0xFC, 0xE3, + 0xB8, 0x70, 0x1C, 0x0F, 0x03, 0xF0, 0x0E, 0x01, 0xDC, 0x3B, 0x8E, 0x7F, + 0x83, 0xE0, 0x03, 0xC0, 0xE0, 0x58, 0x2E, 0x13, 0x8C, 0xE6, 0x33, 0xFE, + 0xFF, 0x81, 0xC0, 0x60, 0x18, 0x0F, 0xE3, 0xFC, 0x60, 0x0C, 0x03, 0x78, + 0x7F, 0x9C, 0x70, 0x0E, 0x01, 0xDC, 0x33, 0x8E, 0x7F, 0x83, 0xE0, 0x0F, + 0x07, 0xE3, 0x9D, 0xC0, 0x7F, 0x1F, 0xEF, 0x3B, 0x8E, 0xE3, 0xB8, 0xCE, + 0x71, 0xF8, 0x3C, 0x00, 0x7F, 0xDF, 0xF0, 0x18, 0x0C, 0x06, 0x03, 0x81, + 0xC0, 0x60, 0x38, 0x0C, 0x07, 0x01, 0x80, 0x60, 0x00, 0x0F, 0x83, 0xFC, + 0xE3, 0x9C, 0x73, 0x9C, 0x3F, 0x0F, 0xE3, 0x8E, 0xE1, 0xDC, 0x3B, 0x8E, + 0x7F, 0xC3, 0xE0, 0x0F, 0x83, 0xF8, 0xE3, 0xB8, 0x77, 0x0E, 0xE1, 0xDC, + 0x7B, 0xFE, 0x3D, 0xC0, 0x33, 0x8E, 0x7F, 0x87, 0xC0, 0x77, 0x00, 0x00, + 0x0E, 0xE0, 0x39, 0xC0, 0x00, 0x01, 0xCE, 0x71, 0x19, 0x80, 0x00, 0x00, + 0x70, 0xFD, 0xF8, 0x70, 0x3F, 0x03, 0xF8, 0x1E, 0x01, 0x80, 0x7F, 0xDF, + 0xF0, 0x00, 0x00, 0xFF, 0xBF, 0xE0, 0x60, 0x1E, 0x07, 0xF0, 0x3F, 0x03, + 0x87, 0xEF, 0xC3, 0x80, 0x00, 0x00, 0x1F, 0x1F, 0xFE, 0x1F, 0x87, 0x01, + 0xC0, 0xE0, 0x70, 0x78, 0x3C, 0x0E, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, + 0xFC, 0x00, 0xFF, 0xC0, 0xF0, 0x78, 0x70, 0x07, 0x38, 0x01, 0xCC, 0x3F, + 0x36, 0x31, 0x8D, 0x98, 0x63, 0xC4, 0x11, 0xF3, 0x0C, 0x6C, 0xC6, 0x73, + 0x3E, 0xF8, 0xE7, 0x3C, 0x1E, 0x00, 0x03, 0xFE, 0x00, 0x3F, 0x00, 0x01, + 0xE0, 0x0F, 0x00, 0xF8, 0x07, 0xC0, 0x6F, 0x03, 0x38, 0x31, 0xC3, 0x8E, + 0x1F, 0xF1, 0xFF, 0x8C, 0x1E, 0xE0, 0x76, 0x03, 0x80, 0x1F, 0xF0, 0xFF, + 0xC6, 0x0E, 0x70, 0x73, 0x87, 0x1F, 0xF0, 0xFF, 0x86, 0x0E, 0x70, 0x73, + 0x83, 0x9C, 0x38, 0xFF, 0xC7, 0xF8, 0x00, 0x07, 0xE0, 0xFF, 0x8F, 0x1E, + 0x70, 0x77, 0x00, 0x30, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x03, 0xBC, + 0x38, 0xFF, 0x83, 0xF0, 0x00, 0x1F, 0xE0, 0xFF, 0x86, 0x1E, 0x70, 0x73, + 0x83, 0x9C, 0x1C, 0xC0, 0xE6, 0x07, 0x70, 0x73, 0x83, 0x9C, 0x38, 0xFF, + 0x8F, 0xF0, 0x00, 0x1F, 0xF8, 0xFF, 0x86, 0x00, 0x70, 0x03, 0x80, 0x1F, + 0xF0, 0xFF, 0x86, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xFF, 0xC7, 0xFC, + 0x00, 0x1F, 0xF1, 0xFF, 0x18, 0x03, 0x80, 0x38, 0x03, 0xFC, 0x3F, 0xC7, + 0x00, 0x70, 0x07, 0x00, 0x70, 0x06, 0x00, 0xE0, 0x00, 0x07, 0xC1, 0xFE, + 0x38, 0x77, 0x03, 0x70, 0x0E, 0x00, 0xE1, 0xEE, 0x1E, 0xE0, 0x6E, 0x0E, + 0x70, 0xE7, 0xFC, 0x1F, 0x40, 0x1C, 0x1C, 0x60, 0x63, 0x83, 0x8E, 0x0E, + 0x38, 0x38, 0xFF, 0xC3, 0xFF, 0x1C, 0x1C, 0x70, 0x71, 0xC1, 0xC6, 0x06, + 0x18, 0x38, 0xE0, 0xE0, 0x39, 0xCE, 0x63, 0x39, 0xCE, 0x63, 0x39, 0xCE, + 0x00, 0x00, 0xC0, 0x18, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x00, 0xE0, 0x1C, + 0xE3, 0x9C, 0x73, 0x9C, 0x7F, 0x87, 0xC0, 0x1C, 0x3C, 0x71, 0xC1, 0x8E, + 0x0E, 0x70, 0x3B, 0x80, 0xFC, 0x03, 0xF0, 0x0E, 0xE0, 0x73, 0x81, 0xC7, + 0x07, 0x1C, 0x18, 0x38, 0xE0, 0xF0, 0x1C, 0x07, 0x01, 0x80, 0xE0, 0x38, + 0x0E, 0x03, 0x80, 0xC0, 0x70, 0x1C, 0x07, 0x01, 0xFF, 0x7F, 0x80, 0x1E, + 0x1F, 0x1E, 0x1E, 0x3E, 0x1E, 0x3E, 0x3E, 0x36, 0x3E, 0x36, 0x6E, 0x36, + 0x6C, 0x76, 0xCC, 0x76, 0xDC, 0x67, 0x9C, 0x67, 0x98, 0xE7, 0x18, 0xE7, + 0x18, 0x1C, 0x1C, 0x70, 0x63, 0xE1, 0x8F, 0x8E, 0x3E, 0x38, 0xDC, 0xC3, + 0x33, 0x1C, 0xEC, 0x71, 0xF1, 0xC7, 0xC6, 0x1E, 0x18, 0x38, 0xE0, 0xE0, + 0x07, 0xC0, 0xFF, 0x8E, 0x1E, 0xE0, 0x77, 0x03, 0xF0, 0x1F, 0x80, 0xFC, + 0x07, 0xE0, 0x77, 0x03, 0xBC, 0x38, 0xFF, 0x81, 0xF0, 0x00, 0x1F, 0xF0, + 0xFF, 0xC6, 0x0E, 0x70, 0x73, 0x83, 0x9C, 0x38, 0xFF, 0x87, 0xF8, 0x70, + 0x03, 0x80, 0x1C, 0x00, 0xC0, 0x0E, 0x00, 0x00, 0x07, 0xC0, 0xFF, 0x8F, + 0x1C, 0xE0, 0x77, 0x03, 0xB0, 0x1F, 0x80, 0xFC, 0x06, 0xE1, 0x77, 0x1F, + 0x3C, 0x78, 0xFF, 0xC1, 0xF6, 0x00, 0x20, 0x1F, 0xF0, 0xFF, 0xC6, 0x0E, + 0x70, 0x73, 0x83, 0x9C, 0x38, 0xFF, 0x87, 0xFC, 0x70, 0x73, 0x83, 0x9C, + 0x38, 0xC1, 0xC6, 0x0F, 0x00, 0x07, 0xE0, 0xFF, 0xC7, 0x0E, 0x70, 0x73, + 0x80, 0x1F, 0x80, 0x7F, 0x80, 0x7E, 0x00, 0x77, 0x03, 0xBC, 0x38, 0xFF, + 0xC3, 0xF8, 0x00, 0xFF, 0xDF, 0xF8, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x06, + 0x01, 0xC0, 0x38, 0x07, 0x00, 0xC0, 0x18, 0x07, 0x00, 0x38, 0x31, 0xC1, + 0x8C, 0x1C, 0xE0, 0xE7, 0x07, 0x38, 0x31, 0xC3, 0x9C, 0x1C, 0xE0, 0xE7, + 0x06, 0x38, 0x70, 0xFF, 0x03, 0xE0, 0x00, 0xE0, 0xFC, 0x1D, 0x87, 0x30, + 0xC6, 0x38, 0xC6, 0x19, 0xC3, 0xB0, 0x7E, 0x0F, 0x80, 0xF0, 0x1C, 0x03, + 0x00, 0xE1, 0xC3, 0xF1, 0xE3, 0xB8, 0xF1, 0xDC, 0x78, 0xCE, 0x6C, 0xE7, + 0x36, 0x63, 0xB3, 0x70, 0xD9, 0xB0, 0x7C, 0xD8, 0x3C, 0x78, 0x1E, 0x3C, + 0x0E, 0x1C, 0x07, 0x0E, 0x00, 0x0E, 0x1C, 0x38, 0xE0, 0xE7, 0x01, 0xD8, + 0x07, 0xE0, 0x0F, 0x00, 0x38, 0x01, 0xE0, 0x0F, 0xC0, 0x77, 0x01, 0x8E, + 0x0E, 0x38, 0x70, 0xF0, 0xE0, 0xEE, 0x39, 0xC7, 0x39, 0xC3, 0x70, 0x7C, + 0x0F, 0x80, 0xE0, 0x1C, 0x03, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x3F, 0xF3, + 0xFF, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x3C, 0x07, + 0x80, 0x70, 0x0F, 0xFC, 0xFF, 0xC0, 0x0F, 0x0F, 0x0C, 0x1C, 0x18, 0x18, + 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x60, 0x60, 0x60, 0x78, 0x78, 0x12, + 0x4C, 0x92, 0x49, 0x26, 0xD9, 0x20, 0x1E, 0x1E, 0x06, 0x06, 0x06, 0x0C, + 0x0C, 0x0C, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x38, 0x30, 0xF0, 0xF0, 0x06, + 0x0E, 0x0E, 0x1B, 0x33, 0x33, 0x63, 0x63, 0xFF, 0xE0, 0xCC, 0x1F, 0x8F, + 0xF3, 0x1C, 0x06, 0x1F, 0x9F, 0xEE, 0x3B, 0x9C, 0xFF, 0x1D, 0xC0, 0x18, + 0x03, 0x00, 0xE0, 0x1D, 0xC3, 0xFC, 0x71, 0xDC, 0x3B, 0x87, 0x70, 0xEE, + 0x39, 0xCF, 0x7F, 0xCF, 0xE0, 0x0F, 0x0F, 0xF7, 0x1D, 0xC0, 0xE0, 0x38, + 0x0E, 0x03, 0x8E, 0x7F, 0x0F, 0x80, 0x00, 0x60, 0x06, 0x00, 0x61, 0xEE, + 0x3F, 0xE7, 0x9C, 0x71, 0xCE, 0x1C, 0xE1, 0xCE, 0x1C, 0xE3, 0x87, 0xF8, + 0x7F, 0x80, 0x1F, 0x0F, 0xE7, 0x1D, 0xC7, 0xFF, 0xFF, 0xFE, 0x03, 0x8E, + 0x7F, 0x0F, 0x80, 0x1C, 0xF3, 0x3F, 0xFD, 0xC7, 0x18, 0x63, 0x8E, 0x30, + 0xC0, 0x0F, 0x71, 0xFE, 0x3C, 0xE3, 0x8E, 0x70, 0xE7, 0x0E, 0x70, 0xC7, + 0x1C, 0x3F, 0xC3, 0xFC, 0x01, 0xCE, 0x38, 0x7F, 0x03, 0xE0, 0x18, 0x03, + 0x00, 0xE0, 0x1D, 0xE3, 0xFE, 0x71, 0xCC, 0x3B, 0x86, 0x70, 0xCC, 0x39, + 0x87, 0x30, 0xEE, 0x18, 0x39, 0xC0, 0x63, 0x39, 0xCE, 0x63, 0x39, 0xCE, + 0x00, 0x06, 0x06, 0x00, 0x0E, 0x0E, 0x0C, 0x0C, 0x1C, 0x1C, 0x1C, 0x18, + 0x18, 0x38, 0x38, 0x30, 0x70, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x1C, 0xE3, + 0x38, 0x6E, 0x1F, 0x83, 0xF0, 0x7E, 0x0E, 0xE1, 0x9C, 0x73, 0x8E, 0x38, + 0x39, 0xCE, 0x63, 0x39, 0xCE, 0x63, 0x39, 0xCE, 0x00, 0x3B, 0x9E, 0x3F, + 0xFF, 0x39, 0xC7, 0x71, 0xC6, 0x71, 0x86, 0x71, 0x8E, 0x63, 0x8E, 0x63, + 0x8C, 0xE3, 0x8C, 0xE3, 0x1C, 0x3B, 0xC7, 0xFC, 0xE3, 0xB8, 0x77, 0x0C, + 0xE1, 0x98, 0x73, 0x0E, 0xE1, 0xDC, 0x30, 0x0F, 0x87, 0xF9, 0xE7, 0xB8, + 0x7E, 0x0F, 0xC1, 0xF8, 0x77, 0x9E, 0x7F, 0x87, 0xC0, 0x1D, 0xE1, 0xFE, + 0x1C, 0x73, 0x87, 0x38, 0x73, 0x87, 0x38, 0xE3, 0x8E, 0x7F, 0xC7, 0xF8, + 0x60, 0x06, 0x00, 0x60, 0x0E, 0x00, 0x1E, 0xE7, 0xFD, 0xE7, 0x38, 0xEE, + 0x1D, 0xC3, 0xB8, 0x77, 0x1C, 0x7F, 0x8F, 0xF0, 0x0E, 0x01, 0x80, 0x30, + 0x06, 0x00, 0x3B, 0x36, 0x38, 0x70, 0x70, 0x70, 0x60, 0x60, 0xE0, 0xE0, + 0x3E, 0x3F, 0xF8, 0xFC, 0x0F, 0xC3, 0xF8, 0x3D, 0x8E, 0xFE, 0x3E, 0x00, + 0x38, 0xCF, 0xFE, 0x71, 0x86, 0x38, 0xE3, 0x8F, 0x3C, 0x31, 0xDC, 0x77, + 0x19, 0x86, 0x63, 0xB8, 0xEE, 0x33, 0x9C, 0xFF, 0x1F, 0xC0, 0xE1, 0x98, + 0xE6, 0x31, 0x9C, 0x66, 0x1B, 0x86, 0xC1, 0xF0, 0x78, 0x0E, 0x00, 0xE7, + 0x1B, 0x9C, 0xEE, 0x73, 0x3B, 0xDC, 0xEB, 0x63, 0xAD, 0x8F, 0xBC, 0x1C, + 0xF0, 0x73, 0xC1, 0xCE, 0x00, 0x1C, 0xE1, 0xCC, 0x0D, 0x80, 0xF8, 0x0F, + 0x00, 0xF0, 0x1F, 0x03, 0xB8, 0x33, 0x87, 0x38, 0x70, 0xCE, 0x38, 0xC6, + 0x19, 0xC3, 0x30, 0x66, 0x0F, 0x81, 0xF0, 0x3C, 0x03, 0x80, 0x60, 0x18, + 0x0F, 0x01, 0xC0, 0x00, 0x1F, 0xCF, 0xF0, 0x38, 0x1C, 0x0E, 0x07, 0x03, + 0x81, 0xC0, 0x7F, 0xBF, 0xE0, 0x0E, 0x38, 0x61, 0x83, 0x06, 0x0C, 0x78, + 0xF0, 0xC1, 0x83, 0x0E, 0x1C, 0x38, 0x78, 0x70, 0x18, 0xC4, 0x21, 0x18, + 0xC4, 0x21, 0x18, 0xC4, 0x23, 0x18, 0x80, 0x1C, 0x3C, 0x38, 0x70, 0xE1, + 0x83, 0x06, 0x1E, 0x5C, 0x60, 0xC1, 0x83, 0x0C, 0x38, 0xE0, 0x71, 0x8E}; + +const GFXglyph FreeSansBoldOblique9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 5, 13, 6, 2, -12}, // 0x21 '!' + {9, 7, 5, 9, 3, -12}, // 0x22 '"' + {14, 11, 12, 10, 1, -11}, // 0x23 '#' + {31, 11, 16, 10, 1, -13}, // 0x24 '$' + {53, 14, 13, 16, 2, -12}, // 0x25 '%' + {76, 12, 13, 13, 2, -12}, // 0x26 '&' + {96, 3, 5, 4, 3, -12}, // 0x27 ''' + {98, 8, 17, 6, 2, -12}, // 0x28 '(' + {115, 8, 17, 6, -2, -13}, // 0x29 ')' + {132, 6, 6, 7, 3, -12}, // 0x2A '*' + {137, 9, 8, 11, 2, -7}, // 0x2B '+' + {146, 4, 6, 5, 0, -2}, // 0x2C ',' + {149, 5, 2, 6, 1, -5}, // 0x2D '-' + {151, 3, 2, 5, 1, -1}, // 0x2E '.' + {152, 8, 13, 5, 0, -12}, // 0x2F '/' + {165, 10, 13, 10, 1, -12}, // 0x30 '0' + {182, 7, 13, 10, 3, -12}, // 0x31 '1' + {194, 11, 13, 10, 1, -12}, // 0x32 '2' + {212, 11, 13, 10, 1, -12}, // 0x33 '3' + {230, 10, 12, 10, 1, -11}, // 0x34 '4' + {245, 11, 13, 10, 1, -12}, // 0x35 '5' + {263, 10, 13, 10, 2, -12}, // 0x36 '6' + {280, 10, 13, 10, 2, -12}, // 0x37 '7' + {297, 11, 13, 10, 1, -12}, // 0x38 '8' + {315, 11, 13, 10, 1, -12}, // 0x39 '9' + {333, 4, 9, 6, 2, -8}, // 0x3A ':' + {338, 5, 12, 6, 1, -8}, // 0x3B ';' + {346, 10, 9, 11, 1, -8}, // 0x3C '<' + {358, 10, 6, 11, 1, -6}, // 0x3D '=' + {366, 10, 9, 11, 1, -7}, // 0x3E '>' + {378, 10, 13, 11, 3, -12}, // 0x3F '?' + {395, 18, 16, 18, 1, -13}, // 0x40 '@' + {431, 13, 13, 13, 0, -12}, // 0x41 'A' + {453, 13, 13, 13, 1, -12}, // 0x42 'B' + {475, 13, 13, 13, 2, -12}, // 0x43 'C' + {497, 13, 13, 13, 1, -12}, // 0x44 'D' + {519, 13, 13, 12, 1, -12}, // 0x45 'E' + {541, 12, 13, 11, 1, -12}, // 0x46 'F' + {561, 12, 13, 14, 2, -12}, // 0x47 'G' + {581, 14, 13, 13, 1, -12}, // 0x48 'H' + {604, 5, 13, 5, 1, -12}, // 0x49 'I' + {613, 11, 13, 10, 1, -12}, // 0x4A 'J' + {631, 14, 13, 13, 1, -12}, // 0x4B 'K' + {654, 10, 13, 11, 1, -12}, // 0x4C 'L' + {671, 16, 13, 15, 1, -12}, // 0x4D 'M' + {697, 14, 13, 13, 1, -12}, // 0x4E 'N' + {720, 13, 13, 14, 2, -12}, // 0x4F 'O' + {742, 13, 13, 12, 1, -12}, // 0x50 'P' + {764, 13, 14, 14, 2, -12}, // 0x51 'Q' + {787, 13, 13, 13, 1, -12}, // 0x52 'R' + {809, 13, 13, 12, 1, -12}, // 0x53 'S' + {831, 11, 13, 11, 3, -12}, // 0x54 'T' + {849, 13, 13, 13, 2, -12}, // 0x55 'U' + {871, 11, 13, 12, 3, -12}, // 0x56 'V' + {889, 17, 13, 17, 3, -12}, // 0x57 'W' + {917, 14, 13, 12, 0, -12}, // 0x58 'X' + {940, 11, 13, 12, 3, -12}, // 0x59 'Y' + {958, 12, 13, 11, 1, -12}, // 0x5A 'Z' + {978, 8, 17, 6, 0, -12}, // 0x5B '[' + {995, 3, 17, 5, 2, -16}, // 0x5C '\' + {1002, 8, 17, 6, 0, -13}, // 0x5D ']' + {1019, 8, 8, 11, 2, -12}, // 0x5E '^' + {1027, 11, 1, 10, -1, 4}, // 0x5F '_' + {1029, 3, 2, 6, 3, -12}, // 0x60 '`' + {1030, 10, 10, 10, 1, -9}, // 0x61 'a' + {1043, 11, 13, 11, 1, -12}, // 0x62 'b' + {1061, 10, 10, 10, 1, -9}, // 0x63 'c' + {1074, 12, 13, 11, 1, -12}, // 0x64 'd' + {1094, 10, 10, 10, 1, -9}, // 0x65 'e' + {1107, 6, 13, 6, 2, -12}, // 0x66 'f' + {1117, 12, 14, 11, 0, -9}, // 0x67 'g' + {1138, 11, 13, 11, 1, -12}, // 0x68 'h' + {1156, 5, 13, 5, 1, -12}, // 0x69 'i' + {1165, 8, 17, 5, -1, -12}, // 0x6A 'j' + {1182, 11, 13, 10, 1, -12}, // 0x6B 'k' + {1200, 5, 13, 5, 1, -12}, // 0x6C 'l' + {1209, 16, 10, 16, 1, -9}, // 0x6D 'm' + {1229, 11, 10, 11, 1, -9}, // 0x6E 'n' + {1243, 11, 10, 11, 1, -9}, // 0x6F 'o' + {1257, 12, 14, 11, 0, -9}, // 0x70 'p' + {1278, 11, 14, 11, 1, -9}, // 0x71 'q' + {1298, 8, 10, 7, 1, -9}, // 0x72 'r' + {1308, 9, 10, 10, 2, -9}, // 0x73 's' + {1320, 6, 12, 6, 2, -11}, // 0x74 't' + {1329, 10, 10, 11, 2, -9}, // 0x75 'u' + {1342, 10, 10, 10, 2, -9}, // 0x76 'v' + {1355, 14, 10, 14, 2, -9}, // 0x77 'w' + {1373, 12, 10, 10, 0, -9}, // 0x78 'x' + {1388, 11, 14, 10, 1, -9}, // 0x79 'y' + {1408, 10, 10, 9, 0, -9}, // 0x7A 'z' + {1421, 7, 17, 7, 2, -12}, // 0x7B '{' + {1436, 5, 17, 5, 1, -12}, // 0x7C '|' + {1447, 7, 17, 7, 0, -13}, // 0x7D '}' + {1462, 8, 2, 11, 2, -4}}; // 0x7E '~' + +const GFXfont FreeSansBoldOblique9pt7b PROGMEM = { + (uint8_t *)FreeSansBoldOblique9pt7bBitmaps, + (GFXglyph *)FreeSansBoldOblique9pt7bGlyphs, 0x20, 0x7E, 22}; + +// Approx. 2136 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique12pt7b.h new file mode 100644 index 0000000..b9fbbfc --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique12pt7b.h @@ -0,0 +1,301 @@ +const uint8_t FreeSansOblique12pt7bBitmaps[] PROGMEM = { + 0x0C, 0x61, 0x86, 0x18, 0x63, 0x0C, 0x30, 0xC2, 0x18, 0x61, 0x00, 0x00, + 0xC3, 0x00, 0xCF, 0x3C, 0xE2, 0x8A, 0x20, 0x01, 0x8C, 0x03, 0x18, 0x06, + 0x60, 0x18, 0xC0, 0x31, 0x83, 0xFF, 0x87, 0xFF, 0x03, 0x18, 0x0C, 0x60, + 0x18, 0xC0, 0x23, 0x03, 0xFF, 0x8F, 0xFF, 0x02, 0x30, 0x0C, 0x60, 0x18, + 0x80, 0x63, 0x00, 0xC6, 0x00, 0x00, 0x80, 0x3F, 0x03, 0xFC, 0x32, 0x73, + 0x91, 0x99, 0x8C, 0xCC, 0x06, 0x60, 0x3E, 0x00, 0x7E, 0x01, 0xFC, 0x0C, + 0xEC, 0x43, 0x62, 0x1B, 0x11, 0x9D, 0x9C, 0x7F, 0xC1, 0xF8, 0x02, 0x00, + 0x10, 0x01, 0x80, 0x00, 0x00, 0x01, 0x83, 0xC0, 0x60, 0xFC, 0x18, 0x30, + 0xC2, 0x0C, 0x18, 0xC1, 0x83, 0x30, 0x38, 0xCC, 0x03, 0xF1, 0x00, 0x3C, + 0x40, 0x00, 0x18, 0xF0, 0x06, 0x3F, 0x01, 0x8C, 0x30, 0x23, 0x06, 0x0C, + 0x60, 0xC3, 0x0E, 0x30, 0xC0, 0xFC, 0x10, 0x0F, 0x00, 0x01, 0xE0, 0x3F, + 0x81, 0x8C, 0x18, 0x60, 0xC3, 0x06, 0x30, 0x1F, 0x00, 0xE0, 0x1F, 0x01, + 0xDC, 0xD8, 0x6D, 0x81, 0xEC, 0x0E, 0x60, 0x73, 0x87, 0xCF, 0xE6, 0x3E, + 0x38, 0xFE, 0xA0, 0x03, 0x06, 0x04, 0x0C, 0x18, 0x18, 0x30, 0x30, 0x60, + 0x60, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x40, 0x60, + 0x60, 0x20, 0x04, 0x06, 0x06, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x06, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x20, 0x60, + 0xC0, 0x0C, 0x0C, 0x49, 0x7F, 0x3C, 0x3C, 0x6C, 0x00, 0x03, 0x00, 0x30, + 0x03, 0x00, 0x30, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0xC0, + 0x0C, 0x00, 0x77, 0x22, 0x6C, 0xFF, 0xF0, 0xFC, 0x00, 0x40, 0x30, 0x08, + 0x06, 0x01, 0x00, 0xC0, 0x20, 0x18, 0x04, 0x02, 0x00, 0x80, 0x40, 0x10, + 0x08, 0x02, 0x01, 0x00, 0xC0, 0x20, 0x00, 0x07, 0xC0, 0xFE, 0x1C, 0x73, + 0x83, 0x30, 0x36, 0x03, 0x60, 0x36, 0x03, 0xC0, 0x7C, 0x07, 0xC0, 0x6C, + 0x06, 0xC0, 0xEC, 0x0C, 0xE3, 0x87, 0xF0, 0x3E, 0x00, 0x02, 0x0C, 0x77, + 0xEF, 0xC1, 0x83, 0x0C, 0x18, 0x30, 0x61, 0xC3, 0x06, 0x0C, 0x18, 0x60, + 0x03, 0xF0, 0x1F, 0xE0, 0xE1, 0xC7, 0x03, 0x18, 0x0C, 0x00, 0x30, 0x01, + 0x80, 0x0E, 0x00, 0x70, 0x07, 0x80, 0x78, 0x07, 0x80, 0x38, 0x01, 0xC0, + 0x06, 0x00, 0x1F, 0xFC, 0xFF, 0xE0, 0x07, 0xC0, 0xFE, 0x1C, 0x73, 0x03, + 0x30, 0x30, 0x03, 0x00, 0xE0, 0x7C, 0x07, 0xC0, 0x0E, 0x00, 0x60, 0x06, + 0xC0, 0x6C, 0x0C, 0xE1, 0xC7, 0xF8, 0x3E, 0x00, 0x00, 0x60, 0x06, 0x00, + 0xE0, 0x1E, 0x03, 0xE0, 0x6C, 0x0C, 0xC1, 0x8C, 0x30, 0xC6, 0x1C, 0xC1, + 0x8F, 0xFF, 0xFF, 0xE0, 0x18, 0x03, 0x00, 0x30, 0x03, 0x00, 0x0F, 0xF8, + 0x7F, 0xC6, 0x00, 0x30, 0x01, 0x00, 0x1B, 0xC0, 0xFF, 0x06, 0x1C, 0x60, + 0x60, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x0C, 0x60, 0x63, 0x86, 0x0F, 0xE0, + 0x3E, 0x00, 0x03, 0xC0, 0xFE, 0x1C, 0x73, 0x83, 0x30, 0x06, 0x00, 0x67, + 0x87, 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x4C, 0x0C, 0xE1, + 0x87, 0xF8, 0x3E, 0x00, 0x3F, 0xFB, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, + 0x00, 0xC0, 0x06, 0x00, 0x60, 0x06, 0x00, 0x70, 0x03, 0x00, 0x30, 0x03, + 0x80, 0x18, 0x01, 0xC0, 0x0C, 0x00, 0xE0, 0x00, 0x07, 0xC0, 0xFE, 0x1C, + 0x73, 0x03, 0x30, 0x33, 0x03, 0x38, 0x61, 0xFC, 0x3F, 0xC7, 0x0E, 0x60, + 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xE1, 0xC7, 0xF8, 0x3E, 0x00, 0x07, 0xC1, + 0xFE, 0x38, 0x73, 0x03, 0x60, 0x36, 0x03, 0x60, 0x36, 0x07, 0x70, 0xF3, + 0xFE, 0x1E, 0x60, 0x0E, 0x00, 0xCC, 0x1C, 0xE3, 0x87, 0xF0, 0x3C, 0x00, + 0x39, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xC0, 0x1C, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x1C, 0x20, 0x86, 0x30, 0x00, 0x00, 0x01, 0xC0, + 0x3C, 0x0F, 0x81, 0xE0, 0x7C, 0x03, 0x80, 0x0F, 0x00, 0x1F, 0x00, 0x3E, + 0x00, 0x38, 0x00, 0x40, 0x7F, 0xFB, 0xFF, 0x80, 0x00, 0x00, 0x0F, 0xFF, + 0x7F, 0xF0, 0x20, 0x01, 0xC0, 0x07, 0xC0, 0x0F, 0x80, 0x0F, 0x00, 0x1C, + 0x03, 0xE0, 0x78, 0x1F, 0x03, 0xC0, 0x38, 0x00, 0x00, 0x00, 0x0F, 0x87, + 0xF9, 0xC3, 0xB0, 0x3C, 0x06, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x01, 0xC0, + 0x30, 0x0C, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, + 0x3F, 0x80, 0x01, 0xFF, 0xE0, 0x0F, 0x01, 0xE0, 0x38, 0x00, 0xE0, 0xE0, + 0x00, 0xC3, 0x87, 0x81, 0xCE, 0x1F, 0xB1, 0x98, 0x71, 0xC3, 0x61, 0x83, + 0x86, 0xC6, 0x06, 0x0F, 0x0C, 0x0C, 0x3E, 0x30, 0x30, 0x6C, 0x60, 0x61, + 0xD8, 0xC1, 0x87, 0x31, 0xC7, 0x1C, 0x61, 0xF7, 0xF0, 0x63, 0xCF, 0x80, + 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x78, 0x00, 0x7C, 0x00, 0xFC, 0x00, 0xDC, 0x01, 0xCC, + 0x01, 0x8C, 0x03, 0x8C, 0x03, 0x0C, 0x06, 0x0C, 0x0E, 0x0E, 0x0F, 0xFE, + 0x1F, 0xFE, 0x18, 0x06, 0x38, 0x06, 0x30, 0x06, 0x70, 0x06, 0x60, 0x07, + 0x0F, 0xF8, 0x1F, 0xF8, 0x60, 0x38, 0xC0, 0x31, 0x80, 0x63, 0x00, 0xCE, + 0x03, 0x18, 0x0C, 0x3F, 0xF0, 0x7F, 0xF0, 0xC0, 0x73, 0x00, 0x66, 0x00, + 0xCC, 0x01, 0x98, 0x06, 0x70, 0x1C, 0xFF, 0xF1, 0xFF, 0x80, 0x01, 0xF8, + 0x07, 0xFE, 0x0E, 0x0E, 0x1C, 0x03, 0x38, 0x03, 0x30, 0x00, 0x60, 0x00, + 0x60, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x06, + 0xC0, 0x0C, 0xE0, 0x1C, 0x70, 0x78, 0x3F, 0xF0, 0x1F, 0x80, 0x0F, 0xF8, + 0x1F, 0xFC, 0x18, 0x0E, 0x18, 0x07, 0x18, 0x03, 0x18, 0x03, 0x38, 0x03, + 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x70, 0x06, 0x70, 0x06, 0x60, 0x0C, + 0x60, 0x0C, 0x60, 0x18, 0xE0, 0x78, 0xFF, 0xE0, 0xFF, 0x80, 0x0F, 0xFF, + 0x1F, 0xFE, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x38, 0x00, + 0x30, 0x00, 0x3F, 0xFC, 0x3F, 0xF8, 0x70, 0x00, 0x70, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xFF, 0xF8, 0xFF, 0xF8, 0x0F, 0xFE, + 0x3F, 0xFC, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x0E, 0x00, 0x18, + 0x00, 0x3F, 0xF0, 0x7F, 0xE1, 0xC0, 0x03, 0x80, 0x06, 0x00, 0x0C, 0x00, + 0x18, 0x00, 0x70, 0x00, 0xC0, 0x01, 0x80, 0x00, 0x01, 0xF8, 0x07, 0xFE, + 0x0E, 0x0F, 0x18, 0x03, 0x30, 0x03, 0x70, 0x00, 0x60, 0x00, 0x60, 0x00, + 0xC0, 0x7F, 0xC0, 0x7E, 0xC0, 0x02, 0xC0, 0x06, 0xC0, 0x06, 0xE0, 0x0E, + 0x60, 0x1E, 0x78, 0x3C, 0x3F, 0xE4, 0x0F, 0x84, 0x0C, 0x01, 0x8E, 0x00, + 0xC6, 0x00, 0xE3, 0x00, 0x61, 0x80, 0x30, 0xC0, 0x18, 0xE0, 0x0C, 0x60, + 0x0E, 0x3F, 0xFE, 0x1F, 0xFF, 0x1C, 0x01, 0x8E, 0x01, 0xC6, 0x00, 0xE3, + 0x00, 0x61, 0x80, 0x31, 0xC0, 0x18, 0xC0, 0x1C, 0x60, 0x0C, 0x00, 0x0C, + 0x71, 0x86, 0x18, 0x63, 0x8C, 0x30, 0xC3, 0x1C, 0x61, 0x86, 0x18, 0xE3, + 0x00, 0x00, 0x18, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x38, 0x01, + 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x38, 0x01, 0x8C, 0x0C, 0x60, 0x63, + 0x07, 0x1C, 0x70, 0x7F, 0x01, 0xF0, 0x00, 0x0C, 0x03, 0x87, 0x01, 0xC1, + 0x80, 0xE0, 0x60, 0x60, 0x18, 0x70, 0x06, 0x38, 0x03, 0x9C, 0x00, 0xCE, + 0x00, 0x37, 0x80, 0x0F, 0x70, 0x07, 0x8C, 0x01, 0xC3, 0x80, 0x60, 0x60, + 0x18, 0x1C, 0x06, 0x03, 0x03, 0x80, 0xE0, 0xC0, 0x18, 0x30, 0x07, 0x00, + 0x0C, 0x03, 0x80, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x0E, 0x01, 0x80, 0x30, + 0x06, 0x01, 0xC0, 0x38, 0x06, 0x00, 0xC0, 0x18, 0x07, 0x00, 0xFF, 0xFF, + 0xFC, 0x0E, 0x00, 0x71, 0xE0, 0x0F, 0x1E, 0x00, 0xF1, 0xE0, 0x1E, 0x1E, + 0x01, 0xE1, 0xE0, 0x36, 0x3B, 0x03, 0x63, 0x30, 0x6E, 0x33, 0x0E, 0xC3, + 0x30, 0xCC, 0x33, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x31, 0xC6, 0x33, 0x18, + 0x61, 0xE1, 0x8E, 0x1E, 0x18, 0xC1, 0xC1, 0x8C, 0x1C, 0x38, 0x0C, 0x01, + 0x8F, 0x00, 0xC7, 0x80, 0x63, 0xE0, 0x71, 0xF0, 0x30, 0xD8, 0x18, 0xEE, + 0x0C, 0x63, 0x06, 0x31, 0xC7, 0x18, 0xE3, 0x0C, 0x31, 0x8C, 0x1C, 0xC6, + 0x06, 0x63, 0x03, 0xF1, 0x80, 0xF1, 0xC0, 0x78, 0xC0, 0x3C, 0x60, 0x0E, + 0x00, 0x01, 0xF8, 0x03, 0xFF, 0x03, 0x83, 0xC3, 0x80, 0x63, 0x00, 0x3B, + 0x80, 0x0D, 0x80, 0x06, 0xC0, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, + 0xF8, 0x00, 0x6C, 0x00, 0x36, 0x00, 0x31, 0x80, 0x30, 0xF0, 0x78, 0x3F, + 0xF0, 0x07, 0xE0, 0x00, 0x0F, 0xF8, 0x3F, 0xF8, 0x60, 0x38, 0xC0, 0x31, + 0x80, 0x63, 0x00, 0xCE, 0x03, 0x18, 0x0E, 0x3F, 0xF8, 0x7F, 0xE1, 0xC0, + 0x03, 0x80, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x70, 0x00, 0xC0, 0x01, + 0x80, 0x00, 0x00, 0xFC, 0x01, 0xFF, 0xC0, 0xF0, 0x78, 0x70, 0x06, 0x38, + 0x01, 0xCC, 0x00, 0x36, 0x00, 0x0D, 0x80, 0x03, 0xC0, 0x00, 0xF0, 0x00, + 0x3C, 0x00, 0x1B, 0x00, 0x06, 0xC0, 0x03, 0x38, 0x1D, 0xC6, 0x03, 0xE1, + 0xE0, 0xF0, 0x3F, 0xFE, 0x03, 0xF1, 0xC0, 0x00, 0x20, 0x0F, 0xFC, 0x1F, + 0xFE, 0x18, 0x07, 0x18, 0x03, 0x18, 0x03, 0x18, 0x03, 0x38, 0x06, 0x30, + 0x0C, 0x3F, 0xF8, 0x3F, 0xF8, 0x70, 0x1C, 0x70, 0x0C, 0x60, 0x0C, 0x60, + 0x0C, 0x60, 0x18, 0xE0, 0x18, 0xC0, 0x18, 0xC0, 0x1C, 0x03, 0xF8, 0x1F, + 0xF8, 0x70, 0x38, 0xC0, 0x33, 0x00, 0x66, 0x00, 0x0C, 0x00, 0x1E, 0x00, + 0x1F, 0xC0, 0x0F, 0xF0, 0x01, 0xF0, 0x00, 0xEC, 0x00, 0xD8, 0x01, 0xB0, + 0x06, 0x70, 0x38, 0x7F, 0xE0, 0x3F, 0x00, 0xFF, 0xFF, 0xFF, 0xF0, 0x70, + 0x01, 0xC0, 0x06, 0x00, 0x18, 0x00, 0x60, 0x03, 0x80, 0x0C, 0x00, 0x30, + 0x00, 0xC0, 0x03, 0x00, 0x1C, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, 0x18, + 0x00, 0xE0, 0x00, 0x18, 0x03, 0x38, 0x03, 0x30, 0x07, 0x30, 0x06, 0x30, + 0x06, 0x70, 0x06, 0x70, 0x0E, 0x60, 0x0C, 0x60, 0x0C, 0x60, 0x0C, 0xE0, + 0x0C, 0xC0, 0x1C, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x38, 0xE0, 0x70, 0x7F, + 0xE0, 0x1F, 0x80, 0xC0, 0x0F, 0xC0, 0x1B, 0x80, 0x73, 0x00, 0xC6, 0x03, + 0x0C, 0x06, 0x18, 0x18, 0x30, 0x70, 0x60, 0xC0, 0xE3, 0x81, 0xC6, 0x01, + 0x9C, 0x03, 0x30, 0x06, 0xE0, 0x0D, 0x80, 0x1E, 0x00, 0x3C, 0x00, 0x70, + 0x00, 0xC0, 0x70, 0x1F, 0x01, 0xC0, 0x6C, 0x0F, 0x03, 0xB0, 0x3C, 0x0C, + 0xC1, 0xF0, 0x73, 0x06, 0xC1, 0x8C, 0x3B, 0x06, 0x30, 0xC6, 0x30, 0xC7, + 0x18, 0xC3, 0x18, 0x67, 0x0C, 0xE1, 0x98, 0x33, 0x06, 0xE0, 0xDC, 0x1B, + 0x03, 0x60, 0x6C, 0x07, 0x81, 0xE0, 0x1C, 0x07, 0x80, 0x70, 0x1C, 0x01, + 0x80, 0x70, 0x00, 0x07, 0x00, 0xE0, 0xE0, 0x38, 0x0C, 0x0E, 0x01, 0xC3, + 0x80, 0x18, 0xE0, 0x03, 0x98, 0x00, 0x36, 0x00, 0x07, 0x80, 0x00, 0xF0, + 0x00, 0x1E, 0x00, 0x07, 0xC0, 0x01, 0xDC, 0x00, 0x73, 0x80, 0x1C, 0x30, + 0x03, 0x07, 0x00, 0xC0, 0x60, 0x38, 0x0E, 0x0E, 0x00, 0xC0, 0xE0, 0x06, + 0x60, 0x0C, 0x70, 0x1C, 0x70, 0x38, 0x30, 0x70, 0x38, 0x60, 0x18, 0xC0, + 0x1D, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x18, 0x00, 0x0F, 0xFF, + 0x87, 0xFF, 0x80, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, + 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, + 0xC0, 0x01, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xFF, 0xF8, 0x7F, 0xFC, + 0x00, 0x07, 0xC1, 0xE0, 0x60, 0x18, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x1C, + 0x06, 0x01, 0x80, 0x60, 0x18, 0x0E, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x06, + 0x01, 0x80, 0x60, 0x1E, 0x07, 0x80, 0x93, 0x6C, 0x92, 0x49, 0x24, 0xDB, + 0x24, 0x07, 0x81, 0xE0, 0x18, 0x06, 0x01, 0x80, 0xC0, 0x30, 0x0C, 0x03, + 0x01, 0xC0, 0x60, 0x18, 0x06, 0x01, 0x80, 0xE0, 0x30, 0x0C, 0x03, 0x00, + 0xC0, 0x60, 0x18, 0x1E, 0x0F, 0x80, 0x03, 0x01, 0xC0, 0xD8, 0x36, 0x19, + 0x84, 0x63, 0x19, 0x83, 0x60, 0xC0, 0xFF, 0xFC, 0xE6, 0x23, 0x07, 0xC3, + 0xFC, 0xE3, 0x98, 0x30, 0x06, 0x01, 0x87, 0xF3, 0xC6, 0xC0, 0xD8, 0x3B, + 0x0E, 0x7F, 0x77, 0xCC, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x30, 0x01, 0x80, + 0x0C, 0xF0, 0x7F, 0xC3, 0x87, 0x38, 0x19, 0x80, 0xCC, 0x06, 0x60, 0x32, + 0x03, 0xB0, 0x19, 0xC1, 0xCE, 0x1C, 0x7F, 0xC3, 0x7C, 0x00, 0x0F, 0x83, + 0xF8, 0xE3, 0xB8, 0x36, 0x07, 0xC0, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x1B, + 0x86, 0x3F, 0xC3, 0xE0, 0x00, 0x0C, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, + 0x18, 0x3E, 0x61, 0xFF, 0x0E, 0x3C, 0x70, 0x71, 0x80, 0xCE, 0x07, 0x30, + 0x18, 0xC0, 0x63, 0x01, 0x8C, 0x0E, 0x38, 0x78, 0x7F, 0xC0, 0xFB, 0x00, + 0x07, 0xC1, 0xFE, 0x38, 0x77, 0x03, 0x60, 0x37, 0xFF, 0xFF, 0xFC, 0x00, + 0xC0, 0x0C, 0x06, 0xE1, 0xC7, 0xF8, 0x3E, 0x00, 0x07, 0x0F, 0x1C, 0x18, + 0x18, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, + 0xC0, 0xC0, 0x03, 0xCC, 0x3F, 0xA1, 0xC7, 0x8E, 0x0E, 0x30, 0x38, 0xC0, + 0xC6, 0x03, 0x18, 0x0C, 0x60, 0x71, 0x81, 0xC7, 0x0E, 0x0F, 0xF8, 0x1E, + 0x60, 0x03, 0x80, 0x0C, 0x30, 0x70, 0x7F, 0x80, 0xF8, 0x00, 0x0C, 0x00, + 0xC0, 0x0C, 0x01, 0x80, 0x18, 0x01, 0x9E, 0x1F, 0xF1, 0xC7, 0x38, 0x33, + 0x03, 0x30, 0x33, 0x07, 0x30, 0x66, 0x06, 0x60, 0x66, 0x06, 0x60, 0xC6, + 0x0C, 0x18, 0xC0, 0x00, 0x18, 0xC6, 0x33, 0x18, 0xC6, 0x31, 0x98, 0xC6, + 0x00, 0x01, 0x80, 0xC0, 0x00, 0x00, 0x00, 0x18, 0x1C, 0x0C, 0x06, 0x03, + 0x01, 0x81, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x18, 0x0C, 0x06, 0x03, 0x03, + 0x87, 0x83, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x30, 0x01, 0x80, 0x0C, + 0x18, 0x61, 0x83, 0x38, 0x33, 0x81, 0xB8, 0x0F, 0xC0, 0x77, 0x03, 0x18, + 0x30, 0xC1, 0x87, 0x0C, 0x18, 0x60, 0xE3, 0x03, 0x00, 0x18, 0xC6, 0x63, + 0x18, 0xC6, 0x33, 0x18, 0xC6, 0x31, 0x98, 0xC6, 0x00, 0x1B, 0xE3, 0xC3, + 0xFD, 0xFC, 0xF1, 0xE1, 0x9C, 0x18, 0x33, 0x03, 0x06, 0x60, 0xC0, 0xCC, + 0x18, 0x3B, 0x83, 0x06, 0x60, 0x60, 0xCC, 0x0C, 0x19, 0x83, 0x03, 0x30, + 0x60, 0xE6, 0x0C, 0x18, 0x1B, 0xE1, 0xFF, 0x3C, 0x73, 0x83, 0x30, 0x33, + 0x03, 0x30, 0x77, 0x06, 0x60, 0x66, 0x06, 0x60, 0x66, 0x0C, 0x60, 0xC0, + 0x07, 0xC1, 0xFE, 0x38, 0x77, 0x03, 0x60, 0x3E, 0x03, 0xC0, 0x3C, 0x06, + 0xC0, 0x6C, 0x0E, 0xE1, 0xC7, 0xF8, 0x3E, 0x00, 0x0C, 0xF0, 0x3F, 0xE0, + 0xE1, 0xC7, 0x03, 0x1C, 0x0C, 0x60, 0x31, 0x80, 0xCE, 0x07, 0x38, 0x18, + 0xE0, 0xE3, 0xC7, 0x0F, 0xF8, 0x77, 0xC1, 0x80, 0x06, 0x00, 0x18, 0x00, + 0x60, 0x03, 0x80, 0x00, 0x0F, 0x98, 0xFF, 0xCE, 0x3C, 0xE0, 0xE6, 0x03, + 0x70, 0x1B, 0x01, 0x98, 0x0C, 0xC0, 0x66, 0x07, 0x38, 0x78, 0xFF, 0x83, + 0xCC, 0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0x80, 0x0C, 0x00, 0x1B, 0x8F, + 0xCF, 0x07, 0x03, 0x01, 0x80, 0xC0, 0xE0, 0x60, 0x30, 0x18, 0x0C, 0x06, + 0x00, 0x0F, 0xC1, 0xFF, 0x30, 0x76, 0x03, 0x60, 0x07, 0x80, 0x3F, 0x80, + 0x7E, 0x00, 0x6C, 0x06, 0xE0, 0xCF, 0xF8, 0x3E, 0x00, 0x18, 0x30, 0x67, + 0xEF, 0xC6, 0x0C, 0x30, 0x60, 0xC1, 0x83, 0x0C, 0x18, 0x3C, 0x38, 0x30, + 0x33, 0x03, 0x30, 0x37, 0x06, 0x60, 0x66, 0x06, 0x60, 0x66, 0x06, 0xC0, + 0xEC, 0x0C, 0xC3, 0xCF, 0xFC, 0x7C, 0xC0, 0xC0, 0x78, 0x1B, 0x03, 0x60, + 0xC6, 0x18, 0xC6, 0x19, 0xC3, 0x30, 0x6C, 0x0D, 0x81, 0xE0, 0x3C, 0x03, + 0x00, 0xC1, 0xC3, 0xE1, 0xE1, 0xB0, 0xF0, 0xD8, 0x78, 0xCC, 0x6C, 0x66, + 0x36, 0x63, 0x33, 0x30, 0x99, 0xB0, 0x58, 0xD8, 0x2C, 0x78, 0x1C, 0x3C, + 0x0E, 0x1C, 0x06, 0x0E, 0x00, 0x0C, 0x1C, 0x30, 0xE0, 0xE3, 0x01, 0x98, + 0x07, 0xC0, 0x0E, 0x00, 0x30, 0x01, 0xE0, 0x0F, 0x80, 0x73, 0x01, 0x8C, + 0x0C, 0x38, 0x60, 0x60, 0x18, 0x0C, 0x60, 0x61, 0x83, 0x86, 0x0C, 0x1C, + 0x60, 0x31, 0x80, 0xCC, 0x03, 0x30, 0x0D, 0x80, 0x36, 0x00, 0xF0, 0x03, + 0x80, 0x06, 0x00, 0x30, 0x00, 0xC0, 0x06, 0x00, 0xF0, 0x03, 0x80, 0x00, + 0x1F, 0xF1, 0xFF, 0x00, 0x70, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, + 0x18, 0x03, 0x00, 0x60, 0x0F, 0xFC, 0xFF, 0xC0, 0x07, 0x0E, 0x18, 0x18, + 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x60, 0xE0, 0xE0, 0x60, 0x60, 0x60, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x60, 0x0C, 0x30, 0x82, 0x08, 0x61, + 0x84, 0x10, 0x43, 0x0C, 0x20, 0x86, 0x18, 0x41, 0x04, 0x30, 0xC2, 0x00, + 0x00, 0x06, 0x07, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x06, 0x03, + 0x01, 0xC0, 0xE0, 0x60, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x06, 0x03, 0x01, + 0x83, 0x83, 0x80, 0x38, 0x0F, 0x82, 0x38, 0x83, 0xE0, 0x38}; + +const GFXglyph FreeSansOblique12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 7, 0, 1}, // 0x20 ' ' + {0, 6, 18, 7, 3, -17}, // 0x21 '!' + {14, 6, 6, 9, 4, -16}, // 0x22 '"' + {19, 15, 18, 13, 1, -17}, // 0x23 '#' + {53, 13, 21, 13, 2, -17}, // 0x24 '$' + {88, 19, 17, 21, 3, -16}, // 0x25 '%' + {129, 13, 17, 16, 2, -16}, // 0x26 '&' + {157, 2, 6, 5, 4, -16}, // 0x27 ''' + {159, 8, 23, 8, 3, -17}, // 0x28 '(' + {182, 8, 23, 8, 0, -16}, // 0x29 ')' + {205, 8, 8, 9, 4, -17}, // 0x2A '*' + {213, 12, 11, 14, 2, -10}, // 0x2B '+' + {230, 4, 6, 7, 1, -1}, // 0x2C ',' + {233, 6, 2, 8, 2, -7}, // 0x2D '-' + {235, 3, 2, 7, 2, -1}, // 0x2E '.' + {236, 10, 18, 7, 0, -17}, // 0x2F '/' + {259, 12, 17, 13, 2, -16}, // 0x30 '0' + {285, 7, 17, 13, 5, -16}, // 0x31 '1' + {300, 14, 17, 13, 1, -16}, // 0x32 '2' + {330, 12, 17, 13, 2, -16}, // 0x33 '3' + {356, 12, 17, 13, 2, -16}, // 0x34 '4' + {382, 13, 17, 13, 2, -16}, // 0x35 '5' + {410, 12, 17, 13, 2, -16}, // 0x36 '6' + {436, 13, 17, 13, 3, -16}, // 0x37 '7' + {464, 12, 17, 13, 2, -16}, // 0x38 '8' + {490, 12, 17, 13, 2, -16}, // 0x39 '9' + {516, 5, 12, 7, 3, -11}, // 0x3A ':' + {524, 6, 16, 7, 2, -11}, // 0x3B ';' + {536, 13, 12, 14, 2, -11}, // 0x3C '<' + {556, 13, 6, 14, 2, -8}, // 0x3D '=' + {566, 13, 12, 14, 1, -10}, // 0x3E '>' + {586, 11, 18, 13, 4, -17}, // 0x3F '?' + {611, 23, 21, 24, 2, -17}, // 0x40 '@' + {672, 16, 18, 16, 0, -17}, // 0x41 'A' + {708, 15, 18, 16, 2, -17}, // 0x42 'B' + {742, 16, 18, 17, 2, -17}, // 0x43 'C' + {778, 16, 18, 17, 2, -17}, // 0x44 'D' + {814, 16, 18, 16, 2, -17}, // 0x45 'E' + {850, 15, 18, 14, 2, -17}, // 0x46 'F' + {884, 16, 18, 19, 3, -17}, // 0x47 'G' + {920, 17, 18, 17, 2, -17}, // 0x48 'H' + {959, 6, 18, 7, 2, -17}, // 0x49 'I' + {973, 13, 18, 12, 1, -17}, // 0x4A 'J' + {1003, 18, 18, 16, 2, -17}, // 0x4B 'K' + {1044, 11, 18, 13, 2, -17}, // 0x4C 'L' + {1069, 20, 18, 20, 2, -17}, // 0x4D 'M' + {1114, 17, 18, 18, 2, -17}, // 0x4E 'N' + {1153, 17, 18, 18, 2, -17}, // 0x4F 'O' + {1192, 15, 18, 15, 2, -17}, // 0x50 'P' + {1226, 18, 19, 19, 2, -17}, // 0x51 'Q' + {1269, 16, 18, 17, 2, -17}, // 0x52 'R' + {1305, 15, 18, 16, 2, -17}, // 0x53 'S' + {1339, 14, 18, 15, 4, -17}, // 0x54 'T' + {1371, 16, 18, 17, 3, -17}, // 0x55 'U' + {1407, 15, 18, 15, 4, -17}, // 0x56 'V' + {1441, 22, 18, 22, 4, -17}, // 0x57 'W' + {1491, 19, 18, 16, 0, -17}, // 0x58 'X' + {1534, 16, 18, 16, 4, -17}, // 0x59 'Y' + {1570, 17, 18, 15, 1, -17}, // 0x5A 'Z' + {1609, 10, 23, 7, 0, -17}, // 0x5B '[' + {1638, 3, 18, 7, 4, -17}, // 0x5C '\' + {1645, 10, 23, 7, -1, -16}, // 0x5D ']' + {1674, 10, 9, 11, 2, -16}, // 0x5E '^' + {1686, 14, 1, 13, -1, 4}, // 0x5F '_' + {1688, 4, 4, 8, 4, -17}, // 0x60 '`' + {1690, 11, 13, 13, 2, -12}, // 0x61 'a' + {1708, 13, 18, 13, 1, -17}, // 0x62 'b' + {1738, 11, 13, 12, 2, -12}, // 0x63 'c' + {1756, 14, 18, 13, 2, -17}, // 0x64 'd' + {1788, 12, 13, 13, 2, -12}, // 0x65 'e' + {1808, 8, 18, 6, 2, -17}, // 0x66 'f' + {1826, 14, 18, 13, 1, -12}, // 0x67 'g' + {1858, 12, 18, 13, 1, -17}, // 0x68 'h' + {1885, 5, 18, 5, 2, -17}, // 0x69 'i' + {1897, 9, 23, 6, -1, -17}, // 0x6A 'j' + {1923, 13, 18, 12, 1, -17}, // 0x6B 'k' + {1953, 5, 18, 5, 2, -17}, // 0x6C 'l' + {1965, 19, 13, 20, 1, -12}, // 0x6D 'm' + {1996, 12, 13, 13, 1, -12}, // 0x6E 'n' + {2016, 12, 13, 13, 2, -12}, // 0x6F 'o' + {2036, 14, 18, 14, 0, -12}, // 0x70 'p' + {2068, 13, 18, 13, 2, -12}, // 0x71 'q' + {2098, 9, 13, 8, 1, -12}, // 0x72 'r' + {2113, 12, 13, 12, 1, -12}, // 0x73 's' + {2133, 7, 16, 6, 2, -15}, // 0x74 't' + {2147, 12, 13, 13, 2, -12}, // 0x75 'u' + {2167, 11, 13, 12, 3, -12}, // 0x76 'v' + {2185, 17, 13, 17, 3, -12}, // 0x77 'w' + {2213, 14, 13, 12, 0, -12}, // 0x78 'x' + {2236, 14, 18, 11, 0, -12}, // 0x79 'y' + {2268, 12, 13, 12, 1, -12}, // 0x7A 'z' + {2288, 8, 23, 8, 3, -17}, // 0x7B '{' + {2311, 6, 23, 6, 1, -17}, // 0x7C '|' + {2329, 9, 23, 8, -1, -16}, // 0x7D '}' + {2355, 11, 5, 14, 3, -10}}; // 0x7E '~' + +const GFXfont FreeSansOblique12pt7b PROGMEM = { + (uint8_t *)FreeSansOblique12pt7bBitmaps, + (GFXglyph *)FreeSansOblique12pt7bGlyphs, 0x20, 0x7E, 29}; + +// Approx. 3034 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique18pt7b.h new file mode 100644 index 0000000..cb1106a --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique18pt7b.h @@ -0,0 +1,517 @@ +const uint8_t FreeSansOblique18pt7bBitmaps[] PROGMEM = { + 0x03, 0x83, 0x81, 0xC0, 0xE0, 0x70, 0x78, 0x38, 0x1C, 0x0E, 0x07, 0x07, + 0x83, 0x81, 0xC0, 0xE0, 0x60, 0x30, 0x30, 0x18, 0x0C, 0x04, 0x00, 0x00, + 0x01, 0xC0, 0xE0, 0x70, 0x78, 0x00, 0x71, 0xDC, 0x7F, 0x3F, 0x8E, 0xE3, + 0xB8, 0xEC, 0x33, 0x0C, 0xC3, 0x00, 0x00, 0x38, 0x70, 0x01, 0xC3, 0x80, + 0x0C, 0x18, 0x00, 0xE1, 0xC0, 0x06, 0x0C, 0x00, 0x70, 0xE0, 0x03, 0x87, + 0x03, 0xFF, 0xFF, 0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0x80, 0x60, 0xC0, 0x07, + 0x0E, 0x00, 0x30, 0x60, 0x03, 0x87, 0x00, 0x18, 0x30, 0x1F, 0xFF, 0xF8, + 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x07, 0x0E, 0x00, 0x30, 0x70, 0x03, 0x87, + 0x00, 0x1C, 0x38, 0x00, 0xC1, 0x80, 0x0E, 0x1C, 0x00, 0x60, 0xC0, 0x00, + 0x00, 0x0C, 0x00, 0x07, 0xF8, 0x01, 0xFF, 0xC0, 0x3F, 0xFE, 0x07, 0x99, + 0xF0, 0xF1, 0x87, 0x0E, 0x18, 0x71, 0xC1, 0x87, 0x1C, 0x38, 0x01, 0xC3, + 0x00, 0x1C, 0x30, 0x01, 0xE3, 0x00, 0x0F, 0xB0, 0x00, 0xFF, 0x80, 0x03, + 0xFF, 0x00, 0x0F, 0xF8, 0x00, 0x6F, 0xC0, 0x06, 0x3C, 0x00, 0xC1, 0xCE, + 0x0C, 0x1C, 0xE0, 0xC1, 0xCE, 0x0C, 0x38, 0xF1, 0xC3, 0x8F, 0x98, 0xF0, + 0x7F, 0xFE, 0x03, 0xFF, 0xC0, 0x0F, 0xF0, 0x00, 0x30, 0x00, 0x03, 0x00, + 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0xE0, 0x03, + 0x81, 0xFC, 0x00, 0xC0, 0xFF, 0x00, 0x60, 0x70, 0xE0, 0x38, 0x38, 0x18, + 0x1C, 0x0C, 0x06, 0x0E, 0x03, 0x01, 0x83, 0x00, 0xC0, 0xE1, 0x80, 0x38, + 0x70, 0xE0, 0x0F, 0xF8, 0x70, 0x01, 0xFC, 0x18, 0x00, 0x3E, 0x0C, 0x00, + 0x00, 0x06, 0x07, 0x80, 0x03, 0x87, 0xF8, 0x00, 0xC3, 0xFE, 0x00, 0x61, + 0xE1, 0xC0, 0x30, 0x60, 0x30, 0x1C, 0x30, 0x0C, 0x0E, 0x0C, 0x03, 0x03, + 0x03, 0x01, 0x81, 0x80, 0xE1, 0xE0, 0xC0, 0x1F, 0xF0, 0x70, 0x07, 0xF8, + 0x18, 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x07, 0xF8, 0x00, 0xFF, 0xC0, + 0x1E, 0x3C, 0x03, 0xC1, 0xC0, 0x38, 0x1C, 0x03, 0x81, 0xC0, 0x38, 0x38, + 0x03, 0xC7, 0x00, 0x1D, 0xE0, 0x01, 0xFC, 0x00, 0x1F, 0x00, 0x07, 0xF0, + 0x01, 0xF7, 0x87, 0x3C, 0x3C, 0xE7, 0x81, 0xCE, 0x70, 0x1F, 0xCE, 0x00, + 0xFC, 0xE0, 0x07, 0x8E, 0x00, 0x78, 0xF0, 0x1F, 0x8F, 0x87, 0xFC, 0x7F, + 0xF9, 0xC3, 0xFE, 0x1E, 0x1F, 0x80, 0xE0, 0x77, 0xFE, 0xEE, 0xCC, 0xC0, + 0x00, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x80, 0x30, 0x06, 0x00, 0xE0, + 0x0C, 0x01, 0xC0, 0x18, 0x03, 0x80, 0x38, 0x07, 0x00, 0x70, 0x07, 0x00, + 0x70, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, + 0xE0, 0x0E, 0x00, 0xE0, 0x06, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x00, + 0x18, 0x00, 0x01, 0x80, 0x0C, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x06, 0x00, + 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, + 0x70, 0x07, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x1C, 0x03, + 0x80, 0x38, 0x03, 0x00, 0x70, 0x06, 0x00, 0xC0, 0x1C, 0x01, 0x80, 0x30, + 0x06, 0x00, 0xC0, 0x00, 0x06, 0x01, 0x84, 0x47, 0xF7, 0xFF, 0xCF, 0xC1, + 0xE0, 0xD8, 0x67, 0x18, 0xC0, 0x00, 0x70, 0x00, 0x1C, 0x00, 0x0F, 0x00, + 0x03, 0x80, 0x00, 0xE0, 0x00, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC0, 0x70, 0x00, 0x1C, 0x00, 0x07, 0x00, 0x01, 0xC0, 0x00, 0xE0, + 0x00, 0x38, 0x00, 0x0E, 0x00, 0x3B, 0xDC, 0x21, 0x18, 0x98, 0xFF, 0xFF, + 0xFF, 0xE0, 0x7F, 0xFE, 0x00, 0x06, 0x00, 0x18, 0x00, 0x30, 0x00, 0xC0, + 0x01, 0x80, 0x06, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x60, 0x01, 0x80, 0x03, + 0x00, 0x0C, 0x00, 0x18, 0x00, 0x60, 0x00, 0xC0, 0x03, 0x00, 0x06, 0x00, + 0x18, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x00, 0x06, 0x00, 0x18, 0x00, 0x30, + 0x00, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x7C, 0x00, 0x7F, 0xC0, 0x7F, 0xF8, + 0x3E, 0x1E, 0x0F, 0x03, 0xC7, 0x80, 0x71, 0xC0, 0x1C, 0xE0, 0x07, 0x38, + 0x01, 0xDE, 0x00, 0x77, 0x00, 0x1D, 0xC0, 0x0F, 0x70, 0x03, 0xFC, 0x00, + 0xEE, 0x00, 0x3B, 0x80, 0x0E, 0xE0, 0x07, 0xB8, 0x01, 0xCE, 0x00, 0xF3, + 0x80, 0x38, 0xF0, 0x1E, 0x1E, 0x1F, 0x07, 0xFF, 0x80, 0xFF, 0xC0, 0x0F, + 0x80, 0x00, 0x00, 0xC0, 0x70, 0x3C, 0x3E, 0xFF, 0xBF, 0xEF, 0xF8, 0x1E, + 0x07, 0x01, 0xC0, 0x70, 0x1C, 0x0F, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x07, + 0x81, 0xC0, 0x70, 0x1C, 0x07, 0x01, 0xC0, 0xE0, 0x38, 0x00, 0x00, 0x3F, + 0x00, 0x0F, 0xFC, 0x03, 0xFF, 0xE0, 0x7C, 0x1E, 0x07, 0x80, 0xF0, 0xF0, + 0x07, 0x0E, 0x00, 0x70, 0xE0, 0x07, 0x00, 0x00, 0x70, 0x00, 0x0E, 0x00, + 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x0F, 0x80, 0x03, 0xF0, 0x00, 0xFC, 0x00, + 0x1F, 0x00, 0x07, 0xC0, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0x80, 0x00, + 0x70, 0x00, 0x07, 0x00, 0x00, 0xFF, 0xFF, 0x8F, 0xFF, 0xF0, 0xFF, 0xFF, + 0x00, 0x00, 0x7E, 0x00, 0x3F, 0xF0, 0x0F, 0xFF, 0x03, 0xC1, 0xF0, 0x70, + 0x0E, 0x1C, 0x01, 0xC3, 0x80, 0x38, 0xE0, 0x07, 0x00, 0x01, 0xC0, 0x00, + 0xF0, 0x03, 0xFC, 0x00, 0x7F, 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0x00, 0x00, + 0xE0, 0x00, 0x1C, 0x00, 0x03, 0x9C, 0x00, 0x73, 0x80, 0x1E, 0x70, 0x03, + 0x8F, 0x00, 0xF1, 0xF0, 0x7C, 0x1F, 0xFF, 0x01, 0xFF, 0xC0, 0x0F, 0xC0, + 0x00, 0x00, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x78, 0x00, 0x3E, 0x00, 0x1F, + 0x80, 0x0F, 0xE0, 0x07, 0xF0, 0x03, 0xDC, 0x01, 0xE7, 0x00, 0x71, 0xC0, + 0x38, 0xF0, 0x1C, 0x38, 0x0E, 0x0E, 0x07, 0x03, 0x83, 0x80, 0xE1, 0xC0, + 0x70, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x70, 0x00, 0x38, + 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x01, 0xFF, + 0xF0, 0x3F, 0xFF, 0x03, 0xFF, 0xE0, 0x78, 0x00, 0x07, 0x00, 0x00, 0x70, + 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0xFC, 0x01, 0xFF, 0xF0, 0x1F, + 0xFF, 0x83, 0xE0, 0x78, 0x3C, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xC0, + 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x18, 0x00, 0x03, 0x8E, 0x00, 0x78, + 0xE0, 0x0F, 0x0F, 0x81, 0xE0, 0x7F, 0xFC, 0x03, 0xFF, 0x80, 0x0F, 0xE0, + 0x00, 0x00, 0x7E, 0x00, 0x3F, 0xF0, 0x0F, 0xFF, 0x03, 0xE1, 0xF0, 0xF0, + 0x0E, 0x1C, 0x01, 0xC7, 0x00, 0x01, 0xE0, 0x00, 0x38, 0x00, 0x07, 0x1F, + 0x01, 0xCF, 0xF8, 0x3B, 0xFF, 0x87, 0xE0, 0xF8, 0xF0, 0x0F, 0x3C, 0x00, + 0xE7, 0x80, 0x1C, 0xE0, 0x03, 0x9C, 0x00, 0x73, 0x80, 0x1C, 0x70, 0x03, + 0x8F, 0x00, 0xE0, 0xF0, 0x78, 0x1F, 0xFF, 0x01, 0xFF, 0x80, 0x0F, 0xC0, + 0x00, 0x3F, 0xFF, 0xCF, 0xFF, 0xF7, 0xFF, 0xFC, 0x00, 0x0E, 0x00, 0x07, + 0x00, 0x03, 0x80, 0x00, 0xC0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, + 0x0E, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x78, 0x00, 0x1C, + 0x00, 0x0E, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xF0, 0x00, 0x38, 0x00, + 0x1E, 0x00, 0x07, 0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7E, + 0x00, 0x3F, 0xF0, 0x1F, 0xFF, 0x07, 0xC1, 0xF0, 0xE0, 0x0E, 0x38, 0x01, + 0xC7, 0x00, 0x38, 0xE0, 0x0E, 0x1C, 0x01, 0xC3, 0xC0, 0xF0, 0x3F, 0xFC, + 0x03, 0xFE, 0x01, 0xFF, 0xF0, 0x7C, 0x1E, 0x1E, 0x01, 0xE3, 0x80, 0x1C, + 0xE0, 0x03, 0x9C, 0x00, 0x73, 0x80, 0x0E, 0x70, 0x03, 0x8F, 0x00, 0xF1, + 0xF0, 0x7C, 0x1F, 0xFF, 0x01, 0xFF, 0xC0, 0x0F, 0xC0, 0x00, 0x00, 0x7E, + 0x00, 0x3F, 0xF0, 0x1F, 0xFF, 0x07, 0xC1, 0xE0, 0xE0, 0x1E, 0x38, 0x01, + 0xC7, 0x00, 0x39, 0xC0, 0x07, 0x38, 0x00, 0xE7, 0x00, 0x3C, 0xE0, 0x07, + 0x9E, 0x01, 0xE3, 0xE0, 0xFC, 0x3F, 0xFB, 0x83, 0xFE, 0xF0, 0x3F, 0x1C, + 0x00, 0x03, 0x80, 0x00, 0xF0, 0x00, 0x1C, 0x70, 0x07, 0x8E, 0x01, 0xE1, + 0xE0, 0xF8, 0x1F, 0xFE, 0x01, 0xFF, 0x80, 0x0F, 0xC0, 0x00, 0x0E, 0x3C, + 0x78, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0xF1, 0xE3, 0x80, 0x07, 0x0F, 0x0F, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x78, 0x70, 0x10, 0x10, + 0x30, 0x20, 0xC0, 0x00, 0x00, 0x20, 0x00, 0x1C, 0x00, 0x1F, 0x80, 0x1F, + 0xC0, 0x0F, 0xC0, 0x0F, 0xE0, 0x07, 0xE0, 0x03, 0xF0, 0x00, 0xF0, 0x00, + 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF8, + 0x00, 0x0F, 0xC0, 0x00, 0x78, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xDF, 0xFF, + 0xF7, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFB, + 0xFF, 0xFE, 0xFF, 0xFF, 0x80, 0x10, 0x00, 0x03, 0xC0, 0x00, 0x7E, 0x00, + 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x3F, + 0x00, 0x01, 0xE0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0x7E, 0x00, + 0x7F, 0x00, 0x3F, 0x00, 0x07, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0xF8, + 0x0F, 0xFC, 0x1F, 0xFE, 0x3C, 0x1F, 0x78, 0x07, 0x70, 0x07, 0xE0, 0x07, + 0xE0, 0x07, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, + 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x0F, 0xE0, 0x3F, 0x80, 0x0F, 0x80, 0x03, 0xE0, 0x0F, + 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x3C, 0x0F, 0x01, 0xF0, 0x0F, 0x0F, + 0x03, 0xFD, 0xC7, 0x8F, 0x03, 0xFE, 0xE1, 0xC7, 0x03, 0xC3, 0x60, 0xE7, + 0x03, 0xC0, 0xF0, 0x77, 0x83, 0xC0, 0x70, 0x3B, 0x83, 0xC0, 0x78, 0x1D, + 0xC1, 0xC0, 0x38, 0x1F, 0xC1, 0xE0, 0x1C, 0x0E, 0xE0, 0xE0, 0x1C, 0x0F, + 0x70, 0x70, 0x0E, 0x07, 0x38, 0x38, 0x0E, 0x07, 0x9C, 0x1C, 0x0F, 0x07, + 0x8E, 0x0F, 0x0F, 0x8F, 0x87, 0x03, 0xFD, 0xFF, 0x83, 0xC1, 0xFC, 0xFF, + 0x80, 0xE0, 0x7C, 0x3F, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x00, 0x07, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x07, 0x00, 0x00, 0x7F, 0xFF, + 0x80, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x01, + 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x80, 0x00, 0xFF, + 0x00, 0x01, 0xDE, 0x00, 0x07, 0x9C, 0x00, 0x0E, 0x38, 0x00, 0x3C, 0x70, + 0x00, 0x70, 0xF0, 0x01, 0xC1, 0xE0, 0x07, 0x83, 0xC0, 0x0E, 0x07, 0x80, + 0x38, 0x07, 0x00, 0x70, 0x0E, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFC, 0x0F, + 0xFF, 0xF8, 0x1C, 0x00, 0xF0, 0x70, 0x01, 0xE1, 0xE0, 0x01, 0xC3, 0x80, + 0x03, 0x8F, 0x00, 0x07, 0x1C, 0x00, 0x0E, 0x78, 0x00, 0x1E, 0xE0, 0x00, + 0x3C, 0x07, 0xFF, 0xC0, 0x3F, 0xFF, 0x81, 0xFF, 0xFC, 0x0E, 0x00, 0xF0, + 0xF0, 0x03, 0x87, 0x00, 0x1C, 0x38, 0x00, 0xE1, 0xC0, 0x07, 0x0E, 0x00, + 0x70, 0xF0, 0x03, 0x87, 0x00, 0x78, 0x3F, 0xFF, 0x81, 0xFF, 0xF8, 0x0F, + 0xFF, 0xF0, 0xE0, 0x03, 0xC7, 0x00, 0x0E, 0x38, 0x00, 0x71, 0xC0, 0x03, + 0x9E, 0x00, 0x1C, 0xE0, 0x00, 0xE7, 0x00, 0x0E, 0x38, 0x00, 0xF1, 0xC0, + 0x0F, 0x1F, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x1F, + 0x80, 0x03, 0xFF, 0x80, 0x1F, 0xFF, 0x01, 0xF8, 0x3E, 0x07, 0x80, 0x38, + 0x38, 0x00, 0xF1, 0xC0, 0x01, 0xCF, 0x00, 0x07, 0x38, 0x00, 0x01, 0xE0, + 0x00, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, + 0x0E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x7B, 0x80, 0x01, 0xCE, + 0x00, 0x0F, 0x3C, 0x00, 0x38, 0x70, 0x01, 0xE1, 0xE0, 0x0F, 0x07, 0xC0, + 0xF8, 0x0F, 0xFF, 0xC0, 0x1F, 0xFC, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xFF, + 0xC0, 0x0F, 0xFF, 0xE0, 0x1F, 0xFF, 0xE0, 0x38, 0x03, 0xE0, 0xF0, 0x03, + 0xC1, 0xC0, 0x03, 0x83, 0x80, 0x03, 0x87, 0x00, 0x07, 0x1E, 0x00, 0x0E, + 0x3C, 0x00, 0x1C, 0x70, 0x00, 0x38, 0xE0, 0x00, 0x71, 0xC0, 0x00, 0xE7, + 0x80, 0x03, 0x8F, 0x00, 0x07, 0x1C, 0x00, 0x0E, 0x38, 0x00, 0x3C, 0x70, + 0x00, 0x71, 0xE0, 0x01, 0xE3, 0x80, 0x03, 0x87, 0x00, 0x0E, 0x0E, 0x00, + 0x3C, 0x1C, 0x01, 0xF0, 0x7F, 0xFF, 0xC0, 0xFF, 0xFE, 0x01, 0xFF, 0xF0, + 0x00, 0x07, 0xFF, 0xFE, 0x0F, 0xFF, 0xFC, 0x1F, 0xFF, 0xF0, 0x38, 0x00, + 0x00, 0xF0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x70, 0x00, 0x00, 0xFF, 0xFF, 0x81, + 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x70, 0x00, 0x01, 0xE0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0xFF, 0xFF, + 0xC1, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFC, 0x1F, 0xFF, 0xF0, 0x7F, 0xFF, + 0xC1, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x03, + 0x80, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xC0, 0x00, 0x07, 0xFF, + 0xF0, 0x1F, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x0E, 0x00, 0x00, + 0x38, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1C, 0x00, 0x00, 0x70, + 0x00, 0x01, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xE0, 0x00, + 0x03, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x7F, 0xF8, 0x01, 0xFF, + 0xFC, 0x03, 0xE0, 0x3E, 0x07, 0x80, 0x0E, 0x0F, 0x00, 0x0F, 0x1E, 0x00, + 0x07, 0x1C, 0x00, 0x07, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0xF0, 0x07, 0xFE, 0xE0, 0x07, 0xFE, 0xE0, 0x07, + 0xFE, 0xE0, 0x00, 0x0E, 0xE0, 0x00, 0x0E, 0xE0, 0x00, 0x0E, 0xE0, 0x00, + 0x1C, 0xF0, 0x00, 0x3C, 0x70, 0x00, 0x7C, 0x78, 0x00, 0xFC, 0x3E, 0x03, + 0xDC, 0x1F, 0xFF, 0x98, 0x0F, 0xFE, 0x18, 0x03, 0xF8, 0x18, 0x07, 0x00, + 0x07, 0x83, 0x80, 0x03, 0xC1, 0xC0, 0x01, 0xC0, 0xE0, 0x00, 0xE0, 0xF0, + 0x00, 0x70, 0x70, 0x00, 0x78, 0x38, 0x00, 0x3C, 0x1C, 0x00, 0x1C, 0x1E, + 0x00, 0x0E, 0x0F, 0x00, 0x07, 0x07, 0x00, 0x07, 0x83, 0xFF, 0xFF, 0x81, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xE0, 0xE0, 0x00, 0x70, 0x70, 0x00, 0x78, + 0x38, 0x00, 0x38, 0x1C, 0x00, 0x1C, 0x1E, 0x00, 0x0E, 0x0E, 0x00, 0x0F, + 0x07, 0x00, 0x07, 0x83, 0x80, 0x03, 0x81, 0xC0, 0x01, 0xC1, 0xE0, 0x00, + 0xE0, 0xE0, 0x00, 0xF0, 0x70, 0x00, 0x78, 0x00, 0x07, 0x0F, 0x0F, 0x0E, + 0x0E, 0x0E, 0x0E, 0x1E, 0x1C, 0x1C, 0x1C, 0x1C, 0x3C, 0x3C, 0x38, 0x38, + 0x38, 0x38, 0x78, 0x70, 0x70, 0x70, 0x70, 0xF0, 0xF0, 0xE0, 0x00, 0x01, + 0xC0, 0x00, 0x70, 0x00, 0x3C, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xE0, + 0x00, 0x38, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x01, 0xC0, 0x00, 0x70, 0x00, + 0x1C, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x1E, + 0x1C, 0x07, 0x0E, 0x01, 0xC3, 0x80, 0x70, 0xE0, 0x3C, 0x38, 0x0E, 0x0F, + 0x0F, 0x81, 0xFF, 0xC0, 0x7F, 0xE0, 0x07, 0xE0, 0x00, 0x07, 0x00, 0x07, + 0x83, 0x80, 0x07, 0x81, 0xC0, 0x0F, 0x00, 0xE0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0x70, 0x0F, 0x00, 0x38, 0x0F, 0x00, 0x1C, 0x1F, 0x00, 0x1E, 0x1E, + 0x00, 0x0F, 0x1E, 0x00, 0x07, 0x1E, 0x00, 0x03, 0x9F, 0x00, 0x01, 0xDF, + 0xC0, 0x01, 0xFC, 0xE0, 0x00, 0xFC, 0x78, 0x00, 0x7C, 0x1C, 0x00, 0x3C, + 0x0F, 0x00, 0x1C, 0x07, 0x80, 0x1E, 0x01, 0xE0, 0x0E, 0x00, 0xF0, 0x07, + 0x00, 0x38, 0x03, 0x80, 0x1E, 0x01, 0xC0, 0x07, 0x01, 0xE0, 0x03, 0xC0, + 0xE0, 0x00, 0xE0, 0x70, 0x00, 0x78, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, + 0x00, 0x07, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1E, + 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x38, + 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x78, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, + 0xC0, 0x00, 0xF8, 0x3E, 0x00, 0x07, 0xC1, 0xF0, 0x00, 0x7E, 0x0F, 0x80, + 0x03, 0xF0, 0xFC, 0x00, 0x3F, 0x07, 0x70, 0x01, 0xF8, 0x3B, 0x80, 0x1D, + 0xC1, 0xDC, 0x00, 0xEE, 0x0E, 0xE0, 0x0E, 0xE0, 0xE7, 0x00, 0x77, 0x07, + 0x38, 0x07, 0x38, 0x39, 0xC0, 0x31, 0xC1, 0xCE, 0x03, 0x9E, 0x1E, 0x38, + 0x38, 0xE0, 0xE1, 0xC1, 0xC7, 0x07, 0x0E, 0x1C, 0x38, 0x38, 0x70, 0xE1, + 0xC1, 0xC3, 0x8E, 0x1E, 0x1E, 0x1C, 0x70, 0xE0, 0xE0, 0xE7, 0x07, 0x07, + 0x07, 0x38, 0x38, 0x38, 0x1F, 0x81, 0xC1, 0xC0, 0xF8, 0x1E, 0x1C, 0x07, + 0xC0, 0xE0, 0xE0, 0x3C, 0x07, 0x07, 0x01, 0xE0, 0x38, 0x00, 0x07, 0x80, + 0x03, 0x83, 0xE0, 0x01, 0xC1, 0xF0, 0x00, 0xE0, 0xF8, 0x00, 0xE0, 0xFE, + 0x00, 0x70, 0x7F, 0x00, 0x38, 0x3B, 0xC0, 0x1C, 0x1D, 0xE0, 0x1E, 0x0E, + 0x70, 0x0E, 0x0E, 0x3C, 0x07, 0x07, 0x0E, 0x03, 0x83, 0x87, 0x81, 0xC1, + 0xC3, 0xC1, 0xE1, 0xE0, 0xE0, 0xE0, 0xE0, 0x78, 0x70, 0x70, 0x1C, 0x38, + 0x38, 0x0F, 0x1C, 0x1C, 0x07, 0x9E, 0x1E, 0x01, 0xCE, 0x0E, 0x00, 0xF7, + 0x07, 0x00, 0x3B, 0x83, 0x80, 0x1F, 0xC1, 0xC0, 0x07, 0xC1, 0xC0, 0x03, + 0xE0, 0xE0, 0x01, 0xF0, 0x70, 0x00, 0x78, 0x00, 0x00, 0x1F, 0xC0, 0x00, + 0xFF, 0xF0, 0x01, 0xFF, 0xF8, 0x03, 0xE0, 0x7C, 0x07, 0x80, 0x1E, 0x0F, + 0x00, 0x0E, 0x1C, 0x00, 0x0F, 0x3C, 0x00, 0x07, 0x38, 0x00, 0x07, 0x70, + 0x00, 0x07, 0x70, 0x00, 0x07, 0x70, 0x00, 0x07, 0xE0, 0x00, 0x07, 0xE0, + 0x00, 0x0F, 0xE0, 0x00, 0x0E, 0xE0, 0x00, 0x0E, 0xE0, 0x00, 0x0E, 0xE0, + 0x00, 0x1C, 0xE0, 0x00, 0x1C, 0xF0, 0x00, 0x38, 0x70, 0x00, 0x78, 0x78, + 0x00, 0xF0, 0x3E, 0x07, 0xE0, 0x1F, 0xFF, 0xC0, 0x0F, 0xFF, 0x00, 0x03, + 0xF8, 0x00, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, 0xC0, 0x7F, 0xFF, 0x81, 0xC0, + 0x1F, 0x0F, 0x00, 0x3C, 0x38, 0x00, 0x70, 0xE0, 0x01, 0xC3, 0x80, 0x07, + 0x1E, 0x00, 0x1C, 0x78, 0x00, 0xE1, 0xC0, 0x07, 0x87, 0x00, 0x3C, 0x1F, + 0xFF, 0xE0, 0xFF, 0xFF, 0x03, 0xFF, 0xF0, 0x0E, 0x00, 0x00, 0x38, 0x00, + 0x00, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1C, 0x00, 0x00, 0x70, 0x00, 0x01, + 0xC0, 0x00, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, + 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xFC, 0x00, 0x7F, 0xFF, 0x00, + 0x7C, 0x07, 0xC0, 0x78, 0x00, 0xF0, 0x78, 0x00, 0x38, 0x78, 0x00, 0x1E, + 0x78, 0x00, 0x07, 0x38, 0x00, 0x03, 0xBC, 0x00, 0x01, 0xDC, 0x00, 0x00, + 0xEE, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x1D, 0xC0, 0x00, 0x0E, 0xE0, 0x00, 0x0F, 0x70, 0x00, 0x07, 0x38, 0x00, + 0x87, 0x9E, 0x00, 0xE7, 0x87, 0x00, 0x7F, 0x83, 0xC0, 0x1F, 0x80, 0xF8, + 0x1F, 0x80, 0x3F, 0xFF, 0xE0, 0x0F, 0xFF, 0x78, 0x01, 0xFE, 0x1E, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x02, 0x00, 0x07, 0xFF, 0xF0, 0x0F, 0xFF, 0xF8, + 0x1F, 0xFF, 0xF0, 0x38, 0x00, 0xF0, 0xF0, 0x00, 0xE1, 0xC0, 0x01, 0xC3, + 0x80, 0x03, 0x87, 0x00, 0x07, 0x1E, 0x00, 0x0E, 0x3C, 0x00, 0x38, 0x70, + 0x00, 0xF0, 0xE0, 0x03, 0xC1, 0xFF, 0xFE, 0x07, 0xFF, 0xF8, 0x0F, 0xFF, + 0xF8, 0x1C, 0x00, 0x78, 0x38, 0x00, 0x70, 0x70, 0x00, 0xE1, 0xE0, 0x01, + 0xC3, 0x80, 0x03, 0x87, 0x00, 0x06, 0x0E, 0x00, 0x1C, 0x1C, 0x00, 0x38, + 0x78, 0x00, 0x70, 0xE0, 0x00, 0xE1, 0xC0, 0x01, 0xE0, 0x00, 0x3F, 0xC0, + 0x07, 0xFF, 0xC0, 0x3F, 0xFF, 0x81, 0xF0, 0x1E, 0x0F, 0x00, 0x3C, 0x38, + 0x00, 0x71, 0xC0, 0x01, 0xC7, 0x00, 0x07, 0x1C, 0x00, 0x00, 0x78, 0x00, + 0x01, 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xFE, 0x00, 0x07, 0xFF, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xF3, 0x80, 0x01, 0xCE, 0x00, + 0x07, 0x38, 0x00, 0x18, 0xE0, 0x00, 0xE3, 0xC0, 0x07, 0x07, 0x80, 0x7C, + 0x1F, 0xFF, 0xE0, 0x3F, 0xFE, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x1E, 0x00, + 0x01, 0xE0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x03, 0xC0, + 0x00, 0x38, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x03, 0x80, 0x00, 0x78, + 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0xF0, 0x00, 0x0F, + 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x1E, 0x00, 0x01, + 0xE0, 0x00, 0x0E, 0x00, 0x0F, 0x0E, 0x00, 0x0F, 0x0E, 0x00, 0x0E, 0x0E, + 0x00, 0x0E, 0x1E, 0x00, 0x0E, 0x1C, 0x00, 0x1E, 0x1C, 0x00, 0x1C, 0x1C, + 0x00, 0x1C, 0x3C, 0x00, 0x1C, 0x3C, 0x00, 0x1C, 0x38, 0x00, 0x3C, 0x38, + 0x00, 0x38, 0x38, 0x00, 0x38, 0x78, 0x00, 0x38, 0x70, 0x00, 0x78, 0x70, + 0x00, 0x78, 0x70, 0x00, 0x70, 0xF0, 0x00, 0x70, 0xF0, 0x00, 0x70, 0xE0, + 0x00, 0xF0, 0xE0, 0x00, 0xE0, 0xF0, 0x03, 0xE0, 0x78, 0x0F, 0xC0, 0x7F, + 0xFF, 0x80, 0x1F, 0xFE, 0x00, 0x07, 0xF0, 0x00, 0xE0, 0x00, 0x3F, 0x80, + 0x03, 0xFC, 0x00, 0x1D, 0xE0, 0x01, 0xE7, 0x00, 0x0E, 0x38, 0x00, 0xE1, + 0xC0, 0x07, 0x0E, 0x00, 0x70, 0x70, 0x07, 0x83, 0xC0, 0x38, 0x1E, 0x03, + 0xC0, 0xF0, 0x1C, 0x03, 0x81, 0xE0, 0x1C, 0x0E, 0x00, 0xE0, 0xF0, 0x07, + 0x07, 0x00, 0x3C, 0x70, 0x01, 0xE3, 0x80, 0x0F, 0x38, 0x00, 0x39, 0xC0, + 0x01, 0xDC, 0x00, 0x0E, 0xE0, 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x1F, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0xE0, 0x07, 0x80, + 0x1E, 0xE0, 0x07, 0xC0, 0x1E, 0xE0, 0x0F, 0xC0, 0x1C, 0xE0, 0x0F, 0xC0, + 0x3C, 0xE0, 0x1F, 0xC0, 0x38, 0xE0, 0x1D, 0xC0, 0x78, 0xE0, 0x3D, 0xC0, + 0x70, 0xE0, 0x39, 0xC0, 0xF0, 0xE0, 0x79, 0xC0, 0xE0, 0xE0, 0x71, 0xC0, + 0xE0, 0xE0, 0xF1, 0xC1, 0xC0, 0xE0, 0xE1, 0xC1, 0xC0, 0xE1, 0xE1, 0xC3, + 0xC0, 0x61, 0xC1, 0xC3, 0x80, 0x63, 0xC1, 0xC7, 0x80, 0x63, 0x80, 0xE7, + 0x00, 0x67, 0x80, 0xEF, 0x00, 0x67, 0x00, 0xEE, 0x00, 0x7F, 0x00, 0xEE, + 0x00, 0x7E, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFC, 0x00, 0x7C, 0x00, 0xF8, + 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x78, 0x00, 0xF8, 0x00, 0x78, 0x00, 0xF0, + 0x00, 0x03, 0xC0, 0x03, 0xC0, 0x78, 0x00, 0xF0, 0x07, 0x80, 0x1C, 0x00, + 0xF0, 0x07, 0x80, 0x0F, 0x01, 0xE0, 0x01, 0xE0, 0x78, 0x00, 0x1C, 0x1E, + 0x00, 0x03, 0xC7, 0x80, 0x00, 0x39, 0xE0, 0x00, 0x07, 0xB8, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xF3, 0x80, 0x00, 0x3C, + 0x78, 0x00, 0x0F, 0x0F, 0x00, 0x03, 0xC0, 0xF0, 0x00, 0x70, 0x1E, 0x00, + 0x1E, 0x01, 0xE0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x03, 0xC0, 0x78, 0x00, + 0x78, 0x1E, 0x00, 0x0F, 0x00, 0xF0, 0x00, 0x3C, 0xE0, 0x00, 0x71, 0xE0, + 0x01, 0xE3, 0xC0, 0x07, 0x83, 0xC0, 0x1E, 0x07, 0x80, 0x78, 0x07, 0x00, + 0xE0, 0x0F, 0x03, 0xC0, 0x1E, 0x0F, 0x00, 0x1C, 0x3C, 0x00, 0x3C, 0xF0, + 0x00, 0x39, 0xC0, 0x00, 0x7F, 0x80, 0x00, 0xFE, 0x00, 0x00, 0xF8, 0x00, + 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x78, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xC0, 0x00, 0x03, + 0x80, 0x00, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x81, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0xFF, + 0xFF, 0xF0, 0x7F, 0xFF, 0xF8, 0x00, 0x01, 0xF8, 0x1F, 0xC0, 0xFE, 0x07, + 0x00, 0x38, 0x03, 0xC0, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x03, 0xC0, + 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, + 0x00, 0x38, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x78, 0x03, 0x80, + 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x70, 0x03, 0xF8, 0x1F, 0xC0, 0xFE, 0x00, + 0xCC, 0xCC, 0xCC, 0x46, 0x66, 0x66, 0x66, 0x66, 0x66, 0x62, 0x33, 0x33, + 0x33, 0x03, 0xF8, 0x1F, 0xC0, 0xFE, 0x00, 0x70, 0x07, 0x00, 0x38, 0x01, + 0xC0, 0x0E, 0x00, 0xF0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0xE0, + 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, + 0xC0, 0x1E, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x1E, 0x00, 0xE0, + 0x07, 0x03, 0xF8, 0x1F, 0xC0, 0xFC, 0x00, 0x00, 0xF0, 0x03, 0xC0, 0x1F, + 0x00, 0x7C, 0x03, 0xB8, 0x1C, 0xE0, 0x63, 0x83, 0x8E, 0x1C, 0x38, 0x60, + 0x73, 0x81, 0xCC, 0x07, 0x70, 0x1F, 0x80, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC0, 0xF1, 0xC3, 0x86, 0x0C, 0x00, 0xFE, 0x01, 0xFF, 0xE0, 0xFF, + 0xFC, 0x3C, 0x0F, 0x1C, 0x01, 0xC0, 0x00, 0x70, 0x00, 0x1C, 0x00, 0x0E, + 0x00, 0x1F, 0x83, 0xFF, 0xE3, 0xFE, 0x39, 0xF0, 0x1E, 0xF0, 0x07, 0x38, + 0x01, 0xCE, 0x00, 0xF3, 0xC0, 0xFC, 0xFF, 0xF7, 0x9F, 0xF1, 0xE1, 0xF0, + 0x38, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0xF0, + 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0x71, 0xF0, 0x0E, 0xFF, 0x83, 0xFF, + 0xF8, 0x7F, 0x0F, 0x0F, 0x80, 0xF1, 0xE0, 0x0E, 0x38, 0x01, 0xCF, 0x00, + 0x39, 0xE0, 0x07, 0x38, 0x00, 0xE7, 0x00, 0x38, 0xE0, 0x07, 0x3C, 0x00, + 0xE7, 0x80, 0x38, 0xF8, 0x0F, 0x1F, 0x87, 0xC3, 0xFF, 0xF0, 0xE7, 0xFC, + 0x1C, 0x7E, 0x00, 0x01, 0xF8, 0x07, 0xFC, 0x0F, 0xFE, 0x1E, 0x0F, 0x3C, + 0x07, 0x78, 0x07, 0x70, 0x07, 0x70, 0x00, 0xF0, 0x00, 0xE0, 0x00, 0xE0, + 0x00, 0xE0, 0x00, 0xE0, 0x0E, 0xE0, 0x1C, 0xF0, 0x3C, 0x78, 0x78, 0x7F, + 0xF0, 0x3F, 0xE0, 0x0F, 0x80, 0x00, 0x00, 0x70, 0x00, 0x0F, 0x00, 0x00, + 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x01, 0xE0, 0x1F, + 0x1C, 0x07, 0xFD, 0xC0, 0xFF, 0xDC, 0x1E, 0x0F, 0xC3, 0xC0, 0x7C, 0x38, + 0x07, 0x87, 0x00, 0x38, 0x70, 0x03, 0x8F, 0x00, 0x38, 0xE0, 0x07, 0x8E, + 0x00, 0x70, 0xE0, 0x07, 0x0E, 0x00, 0xF0, 0xE0, 0x0F, 0x0F, 0x01, 0xF0, + 0x78, 0x7E, 0x07, 0xFF, 0xE0, 0x3F, 0xEE, 0x01, 0xF8, 0xE0, 0x01, 0xF8, + 0x03, 0xFF, 0x03, 0xFF, 0xC3, 0xC1, 0xF3, 0xC0, 0x79, 0xC0, 0x1D, 0xC0, + 0x0E, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x1C, 0x00, 0x0E, + 0x00, 0x07, 0x00, 0x73, 0xC0, 0x78, 0xF0, 0x78, 0x7F, 0xF8, 0x1F, 0xF8, + 0x03, 0xF0, 0x00, 0x01, 0xE0, 0x7C, 0x1F, 0x83, 0x80, 0x70, 0x1C, 0x03, + 0x83, 0xFC, 0x7F, 0x8F, 0xF0, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x0F, 0x01, + 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, + 0xE0, 0x1C, 0x00, 0x00, 0xFC, 0x60, 0x7F, 0xCC, 0x1F, 0xFF, 0x87, 0xC3, + 0xF1, 0xE0, 0x3E, 0x38, 0x03, 0x8E, 0x00, 0x71, 0xC0, 0x0E, 0x38, 0x01, + 0xCE, 0x00, 0x79, 0xC0, 0x0E, 0x38, 0x01, 0xC7, 0x00, 0x78, 0xE0, 0x0F, + 0x1E, 0x03, 0xC1, 0xE1, 0xF8, 0x3F, 0xFF, 0x03, 0xFE, 0xE0, 0x1F, 0x1C, + 0x00, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x18, 0x38, 0x07, 0x07, 0x83, 0xC0, + 0x7F, 0xF8, 0x0F, 0xFC, 0x00, 0x7E, 0x00, 0x00, 0x07, 0x00, 0x01, 0xC0, + 0x00, 0x70, 0x00, 0x1C, 0x00, 0x0F, 0x00, 0x03, 0x80, 0x00, 0xE0, 0x00, + 0x38, 0xFC, 0x0E, 0xFF, 0x87, 0xFF, 0xF1, 0xF8, 0x3C, 0x7C, 0x07, 0x1E, + 0x01, 0xC7, 0x00, 0x73, 0xC0, 0x1C, 0xE0, 0x0F, 0x38, 0x03, 0x8E, 0x00, + 0xE3, 0x80, 0x39, 0xE0, 0x0E, 0x70, 0x07, 0x9C, 0x01, 0xC7, 0x00, 0x71, + 0xC0, 0x1C, 0xE0, 0x07, 0x38, 0x03, 0x80, 0x07, 0x07, 0x0F, 0x0E, 0x00, + 0x00, 0x00, 0x1E, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x78, 0x70, 0x70, 0x70, 0x70, 0xF0, 0xE0, 0xE0, 0x00, 0x3C, 0x00, + 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, + 0x70, 0x01, 0xC0, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80, 0x1E, 0x00, + 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, + 0x38, 0x00, 0xE0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x0F, 0x00, + 0x38, 0x00, 0xE0, 0x1F, 0x80, 0x7C, 0x03, 0xE0, 0x00, 0x07, 0x00, 0x00, + 0xE0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0xF0, 0x00, 0x1C, 0x00, 0x03, + 0x80, 0x00, 0x70, 0x1E, 0x0E, 0x07, 0x83, 0xC1, 0xE0, 0x70, 0x70, 0x0E, + 0x1C, 0x01, 0xCF, 0x00, 0x3B, 0xC0, 0x0F, 0xF8, 0x01, 0xFF, 0x80, 0x3E, + 0x70, 0x07, 0x8E, 0x00, 0xE0, 0xE0, 0x38, 0x1C, 0x07, 0x03, 0xC0, 0xE0, + 0x38, 0x1C, 0x07, 0x03, 0x80, 0xF0, 0xE0, 0x0E, 0x1C, 0x01, 0xE0, 0x07, + 0x07, 0x0F, 0x0E, 0x0E, 0x0E, 0x0E, 0x1E, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x78, 0x70, 0x70, 0x70, 0x70, 0xF0, 0xE0, + 0xE0, 0x1E, 0x7C, 0x0F, 0x83, 0xBF, 0xE7, 0xF8, 0x7F, 0xFD, 0xFF, 0x8F, + 0xC3, 0xF0, 0xF1, 0xE0, 0x3C, 0x0E, 0x38, 0x07, 0x01, 0xCF, 0x01, 0xE0, + 0x39, 0xC0, 0x38, 0x07, 0x38, 0x07, 0x00, 0xE7, 0x00, 0xE0, 0x1C, 0xE0, + 0x1C, 0x07, 0x3C, 0x07, 0x00, 0xE7, 0x00, 0xE0, 0x1C, 0xE0, 0x1C, 0x03, + 0x9C, 0x03, 0x80, 0xF3, 0x80, 0x70, 0x1C, 0x70, 0x1C, 0x03, 0x9C, 0x03, + 0x80, 0x73, 0x80, 0x70, 0x0E, 0x00, 0x1E, 0x3E, 0x07, 0x7F, 0xE1, 0xFF, + 0xF8, 0x7E, 0x0F, 0x1F, 0x01, 0xC7, 0x80, 0x73, 0xC0, 0x1C, 0xE0, 0x07, + 0x38, 0x03, 0xCE, 0x00, 0xE3, 0x80, 0x39, 0xE0, 0x0E, 0x70, 0x03, 0x9C, + 0x01, 0xC7, 0x00, 0x71, 0xC0, 0x1C, 0x70, 0x07, 0x38, 0x01, 0xCE, 0x00, + 0xE0, 0x01, 0xF8, 0x03, 0xFF, 0x03, 0xFF, 0xC3, 0xE1, 0xE3, 0xC0, 0x79, + 0xC0, 0x1D, 0xC0, 0x0E, 0xE0, 0x07, 0x70, 0x03, 0xF0, 0x01, 0xF8, 0x01, + 0xDC, 0x00, 0xEE, 0x00, 0x77, 0x00, 0x73, 0xC0, 0x78, 0xF0, 0xF8, 0x7F, + 0xF8, 0x1F, 0xF8, 0x03, 0xF0, 0x00, 0x03, 0x8F, 0x80, 0x1D, 0xFF, 0x01, + 0xFF, 0xFC, 0x0F, 0xC1, 0xE0, 0x7C, 0x07, 0x83, 0xC0, 0x1C, 0x1C, 0x00, + 0xE1, 0xE0, 0x07, 0x0E, 0x00, 0x38, 0x70, 0x01, 0xC3, 0x80, 0x1E, 0x1C, + 0x00, 0xE1, 0xE0, 0x07, 0x0F, 0x00, 0x70, 0x78, 0x07, 0x83, 0xF0, 0xF8, + 0x3F, 0xFF, 0x81, 0xDF, 0xF8, 0x0E, 0x3F, 0x00, 0x70, 0x00, 0x03, 0x80, + 0x00, 0x3C, 0x00, 0x01, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x80, 0x00, 0x00, 0x00, 0xF8, 0xF0, 0x7F, 0xEE, 0x0F, 0xFF, 0xE1, 0xF0, + 0xFE, 0x3C, 0x07, 0xE3, 0x80, 0x3E, 0x70, 0x03, 0xC7, 0x00, 0x3C, 0x70, + 0x03, 0xCE, 0x00, 0x3C, 0xE0, 0x07, 0x8E, 0x00, 0x78, 0xE0, 0x07, 0x8E, + 0x00, 0xF8, 0xF0, 0x1F, 0x87, 0x87, 0xF0, 0x7F, 0xF7, 0x03, 0xFE, 0x70, + 0x0F, 0x8F, 0x00, 0x00, 0xF0, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x0E, + 0x00, 0x01, 0xE0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x04, 0x00, 0x1E, + 0x78, 0xE7, 0xC7, 0x7C, 0x3F, 0x01, 0xF0, 0x0F, 0x00, 0xF0, 0x07, 0x00, + 0x38, 0x01, 0xC0, 0x0E, 0x00, 0xF0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, + 0x00, 0x70, 0x07, 0x00, 0x38, 0x00, 0x01, 0xF8, 0x07, 0xFE, 0x0F, 0xFF, + 0x1E, 0x0F, 0x3C, 0x07, 0x38, 0x07, 0x38, 0x00, 0x3C, 0x00, 0x3F, 0x80, + 0x1F, 0xF8, 0x07, 0xFC, 0x00, 0x7E, 0x00, 0x0E, 0xE0, 0x0E, 0xE0, 0x1E, + 0xF0, 0x3C, 0x7F, 0xF8, 0x7F, 0xF0, 0x1F, 0xC0, 0x0E, 0x03, 0x80, 0xE0, + 0x38, 0x7F, 0xDF, 0xEF, 0xF8, 0x70, 0x1C, 0x0E, 0x03, 0x80, 0xE0, 0x38, + 0x1E, 0x07, 0x01, 0xC0, 0x70, 0x1C, 0x0F, 0x03, 0x80, 0xFC, 0x3F, 0x07, + 0x80, 0x1C, 0x03, 0xC7, 0x00, 0xE1, 0xC0, 0x38, 0xF0, 0x0E, 0x38, 0x03, + 0x8E, 0x00, 0xE3, 0x80, 0x70, 0xE0, 0x1C, 0x78, 0x07, 0x1C, 0x01, 0xC7, + 0x00, 0x71, 0xC0, 0x3C, 0x70, 0x0E, 0x38, 0x07, 0x8E, 0x03, 0xE3, 0x81, + 0xF8, 0xFF, 0xFE, 0x1F, 0xFF, 0x03, 0xF1, 0xC0, 0xE0, 0x07, 0xE0, 0x0F, + 0xE0, 0x0E, 0xE0, 0x1C, 0x70, 0x1C, 0x70, 0x38, 0x70, 0x38, 0x70, 0x70, + 0x70, 0xF0, 0x70, 0xE0, 0x71, 0xC0, 0x71, 0xC0, 0x33, 0x80, 0x3B, 0x80, + 0x3F, 0x00, 0x3F, 0x00, 0x3E, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0xE0, 0x1C, + 0x07, 0xE0, 0x3C, 0x0E, 0xE0, 0x3C, 0x0E, 0xE0, 0x7C, 0x1C, 0xE0, 0x7C, + 0x1C, 0xE0, 0xEC, 0x38, 0xE0, 0xEC, 0x38, 0x61, 0xCC, 0x70, 0x61, 0xCC, + 0x70, 0x63, 0x8C, 0xE0, 0x73, 0x8C, 0xE0, 0x77, 0x0C, 0xC0, 0x77, 0x0D, + 0xC0, 0x7E, 0x0D, 0x80, 0x7E, 0x0F, 0x80, 0x7C, 0x0F, 0x80, 0x7C, 0x0F, + 0x00, 0x78, 0x0F, 0x00, 0x78, 0x0E, 0x00, 0x0E, 0x00, 0xE1, 0xE0, 0x38, + 0x1C, 0x0E, 0x03, 0xC3, 0x80, 0x38, 0xE0, 0x07, 0xBC, 0x00, 0x77, 0x00, + 0x0F, 0xC0, 0x00, 0xF0, 0x00, 0x1C, 0x00, 0x07, 0xC0, 0x01, 0xF8, 0x00, + 0x77, 0x80, 0x1E, 0x70, 0x07, 0x8F, 0x00, 0xE0, 0xE0, 0x38, 0x1C, 0x0E, + 0x01, 0xC3, 0x80, 0x38, 0x00, 0x0E, 0x00, 0x70, 0xF0, 0x0F, 0x07, 0x00, + 0xE0, 0x70, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x38, 0x07, 0x03, 0x80, 0x70, + 0x70, 0x07, 0x07, 0x00, 0x70, 0xE0, 0x03, 0x9E, 0x00, 0x39, 0xC0, 0x03, + 0xB8, 0x00, 0x3B, 0x80, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xE0, 0x00, + 0x1E, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, + 0x07, 0x00, 0x00, 0xE0, 0x00, 0xFE, 0x00, 0x0F, 0xC0, 0x00, 0xF0, 0x00, + 0x00, 0x07, 0xFF, 0xC0, 0xFF, 0xF8, 0x3F, 0xFF, 0x00, 0x01, 0xC0, 0x00, + 0x70, 0x00, 0x1C, 0x00, 0x07, 0x00, 0x01, 0xC0, 0x00, 0x70, 0x00, 0x1C, + 0x00, 0x07, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, + 0x03, 0xC0, 0x00, 0x7F, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xF8, 0x00, 0x00, + 0x70, 0x1F, 0x01, 0xF0, 0x3C, 0x03, 0x80, 0x38, 0x07, 0x00, 0x70, 0x07, + 0x00, 0x70, 0x07, 0x00, 0xE0, 0x0E, 0x01, 0xE0, 0x3C, 0x0F, 0x80, 0xE0, + 0x0F, 0x00, 0x78, 0x03, 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, 0x38, + 0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x0E, 0x00, 0xF8, 0x0F, 0x80, 0x78, + 0x00, 0x01, 0x80, 0xC0, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x06, 0x03, + 0x01, 0x81, 0xC0, 0xC0, 0x60, 0x30, 0x18, 0x18, 0x0C, 0x06, 0x03, 0x01, + 0x81, 0x80, 0xC0, 0x60, 0x30, 0x38, 0x18, 0x0C, 0x06, 0x03, 0x03, 0x01, + 0x80, 0xC0, 0x00, 0x01, 0xE0, 0x1F, 0x01, 0xF0, 0x07, 0x00, 0xE0, 0x0E, + 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, + 0x01, 0xE0, 0x0F, 0x00, 0x70, 0x1F, 0x03, 0xC0, 0x78, 0x07, 0x00, 0x70, + 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x1C, 0x03, 0xC0, + 0xF8, 0x0F, 0x80, 0xE0, 0x00, 0x1C, 0x00, 0x3F, 0x00, 0x7F, 0x83, 0x63, + 0xC7, 0xC1, 0xFE, 0x00, 0xFC, 0x00, 0x78}; + +const GFXglyph FreeSansOblique18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 10, 0, 1}, // 0x20 ' ' + {0, 9, 26, 10, 4, -25}, // 0x21 '!' + {30, 10, 9, 12, 6, -24}, // 0x22 '"' + {42, 21, 25, 19, 2, -24}, // 0x23 '#' + {108, 20, 31, 19, 2, -26}, // 0x24 '$' + {186, 26, 25, 31, 5, -24}, // 0x25 '%' + {268, 20, 25, 23, 3, -24}, // 0x26 '&' + {331, 4, 9, 7, 6, -24}, // 0x27 ''' + {336, 12, 33, 12, 4, -25}, // 0x28 '(' + {386, 12, 33, 12, -1, -24}, // 0x29 ')' + {436, 10, 10, 14, 6, -25}, // 0x2A '*' + {449, 18, 16, 20, 3, -15}, // 0x2B '+' + {485, 5, 8, 10, 2, -2}, // 0x2C ',' + {490, 9, 3, 12, 3, -10}, // 0x2D '-' + {494, 4, 4, 10, 3, -3}, // 0x2E '.' + {496, 15, 26, 10, 0, -25}, // 0x2F '/' + {545, 18, 25, 19, 3, -24}, // 0x30 '0' + {602, 10, 25, 19, 7, -24}, // 0x31 '1' + {634, 20, 25, 19, 2, -24}, // 0x32 '2' + {697, 19, 25, 19, 2, -24}, // 0x33 '3' + {757, 18, 25, 19, 2, -24}, // 0x34 '4' + {814, 20, 25, 19, 2, -24}, // 0x35 '5' + {877, 19, 25, 19, 3, -24}, // 0x36 '6' + {937, 18, 25, 19, 5, -24}, // 0x37 '7' + {994, 19, 25, 19, 3, -24}, // 0x38 '8' + {1054, 19, 25, 19, 2, -24}, // 0x39 '9' + {1114, 7, 19, 10, 4, -18}, // 0x3A ':' + {1131, 8, 24, 10, 3, -18}, // 0x3B ';' + {1155, 19, 17, 20, 3, -16}, // 0x3C '<' + {1196, 18, 9, 20, 3, -12}, // 0x3D '=' + {1217, 19, 17, 20, 2, -15}, // 0x3E '>' + {1258, 16, 26, 19, 6, -25}, // 0x3F '?' + {1310, 33, 31, 36, 3, -25}, // 0x40 '@' + {1438, 23, 26, 23, 0, -25}, // 0x41 'A' + {1513, 21, 26, 23, 3, -25}, // 0x42 'B' + {1582, 22, 26, 25, 4, -25}, // 0x43 'C' + {1654, 23, 26, 25, 3, -25}, // 0x44 'D' + {1729, 23, 26, 23, 3, -25}, // 0x45 'E' + {1804, 22, 26, 21, 3, -25}, // 0x46 'F' + {1876, 24, 26, 27, 4, -25}, // 0x47 'G' + {1954, 25, 26, 25, 3, -25}, // 0x48 'H' + {2036, 8, 26, 10, 4, -25}, // 0x49 'I' + {2062, 18, 26, 18, 2, -25}, // 0x4A 'J' + {2121, 25, 26, 23, 3, -25}, // 0x4B 'K' + {2203, 16, 26, 19, 3, -25}, // 0x4C 'L' + {2255, 29, 26, 30, 3, -25}, // 0x4D 'M' + {2350, 25, 26, 26, 3, -25}, // 0x4E 'N' + {2432, 24, 26, 27, 4, -25}, // 0x4F 'O' + {2510, 22, 26, 23, 3, -25}, // 0x50 'P' + {2582, 25, 28, 27, 4, -25}, // 0x51 'Q' + {2670, 23, 26, 25, 3, -25}, // 0x52 'R' + {2745, 22, 26, 23, 3, -25}, // 0x53 'S' + {2817, 20, 26, 21, 6, -25}, // 0x54 'T' + {2882, 24, 26, 25, 4, -25}, // 0x55 'U' + {2960, 21, 26, 23, 6, -25}, // 0x56 'V' + {3029, 32, 26, 33, 6, -25}, // 0x57 'W' + {3133, 27, 26, 23, 1, -25}, // 0x58 'X' + {3221, 23, 26, 24, 6, -25}, // 0x59 'Y' + {3296, 25, 26, 21, 1, -25}, // 0x5A 'Z' + {3378, 13, 33, 10, 1, -25}, // 0x5B '[' + {3432, 4, 26, 10, 5, -25}, // 0x5C '\' + {3445, 13, 33, 10, -1, -24}, // 0x5D ']' + {3499, 14, 14, 16, 3, -24}, // 0x5E '^' + {3524, 21, 2, 19, -2, 5}, // 0x5F '_' + {3530, 6, 5, 12, 6, -25}, // 0x60 '`' + {3534, 18, 19, 19, 2, -18}, // 0x61 'a' + {3577, 19, 26, 20, 2, -25}, // 0x62 'b' + {3639, 16, 19, 18, 3, -18}, // 0x63 'c' + {3677, 20, 26, 20, 3, -25}, // 0x64 'd' + {3742, 17, 19, 19, 3, -18}, // 0x65 'e' + {3783, 11, 26, 9, 2, -25}, // 0x66 'f' + {3819, 19, 27, 19, 2, -18}, // 0x67 'g' + {3884, 18, 26, 19, 2, -25}, // 0x68 'h' + {3943, 8, 26, 8, 2, -25}, // 0x69 'i' + {3969, 14, 34, 8, -2, -25}, // 0x6A 'j' + {4029, 19, 26, 18, 2, -25}, // 0x6B 'k' + {4091, 8, 26, 8, 2, -25}, // 0x6C 'l' + {4117, 27, 19, 29, 2, -18}, // 0x6D 'm' + {4182, 18, 19, 19, 2, -18}, // 0x6E 'n' + {4225, 17, 19, 19, 3, -18}, // 0x6F 'o' + {4266, 21, 26, 20, 0, -18}, // 0x70 'p' + {4335, 20, 27, 19, 2, -18}, // 0x71 'q' + {4403, 13, 19, 11, 2, -18}, // 0x72 'r' + {4434, 16, 19, 18, 2, -18}, // 0x73 's' + {4472, 10, 23, 9, 3, -22}, // 0x74 't' + {4501, 18, 19, 19, 3, -18}, // 0x75 'u' + {4544, 16, 19, 17, 4, -18}, // 0x76 'v' + {4582, 24, 19, 25, 4, -18}, // 0x77 'w' + {4639, 19, 19, 17, 1, -18}, // 0x78 'x' + {4685, 20, 27, 17, 0, -18}, // 0x79 'y' + {4753, 19, 19, 17, 1, -18}, // 0x7A 'z' + {4799, 12, 33, 12, 3, -25}, // 0x7B '{' + {4849, 9, 33, 9, 2, -25}, // 0x7C '|' + {4887, 12, 33, 12, 0, -24}, // 0x7D '}' + {4937, 16, 7, 20, 5, -15}}; // 0x7E '~' + +const GFXfont FreeSansOblique18pt7b PROGMEM = { + (uint8_t *)FreeSansOblique18pt7bBitmaps, + (GFXglyph *)FreeSansOblique18pt7bGlyphs, 0x20, 0x7E, 42}; + +// Approx. 5623 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique24pt7b.h new file mode 100644 index 0000000..d5fd29f --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique24pt7b.h @@ -0,0 +1,839 @@ +const uint8_t FreeSansOblique24pt7bBitmaps[] PROGMEM = { + 0x01, 0xE0, 0x3C, 0x0F, 0x81, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x3C, 0x07, + 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, + 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x38, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1E, 0x07, 0x80, 0xF0, 0x1E, 0x00, 0x78, + 0x7B, 0xC3, 0xDE, 0x1F, 0xE1, 0xEF, 0x0F, 0x78, 0x7B, 0xC3, 0xDC, 0x1C, + 0xE0, 0xE7, 0x07, 0x30, 0x31, 0x81, 0x80, 0x00, 0x07, 0x81, 0xC0, 0x00, + 0x78, 0x3C, 0x00, 0x07, 0x03, 0xC0, 0x00, 0xF0, 0x38, 0x00, 0x0E, 0x07, + 0x80, 0x01, 0xE0, 0x70, 0x00, 0x1E, 0x0F, 0x00, 0x01, 0xC0, 0xF0, 0x00, + 0x3C, 0x0E, 0x00, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, + 0xFE, 0x00, 0x70, 0x3C, 0x00, 0x0F, 0x03, 0x80, 0x00, 0xF0, 0x78, 0x00, + 0x0E, 0x07, 0x80, 0x01, 0xE0, 0x70, 0x00, 0x1C, 0x0F, 0x00, 0x03, 0xC0, + 0xE0, 0x00, 0x3C, 0x1E, 0x00, 0x03, 0x81, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, + 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0x03, 0x80, 0x00, 0xE0, + 0x78, 0x00, 0x1E, 0x07, 0x00, 0x01, 0xC0, 0xF0, 0x00, 0x1C, 0x0F, 0x00, + 0x03, 0xC0, 0xE0, 0x00, 0x38, 0x1E, 0x00, 0x07, 0x81, 0xC0, 0x00, 0x78, + 0x3C, 0x00, 0x07, 0x03, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0x00, 0x7F, + 0xFF, 0xE0, 0x1F, 0x18, 0xF8, 0x0F, 0x8E, 0x1F, 0x07, 0xC3, 0x83, 0xC1, + 0xE0, 0xE0, 0xF0, 0x70, 0x38, 0x3C, 0x3C, 0x0C, 0x0F, 0x0F, 0x07, 0x00, + 0x03, 0xC1, 0xC0, 0x00, 0xF0, 0x70, 0x00, 0x3E, 0x1C, 0x00, 0x0F, 0xE6, + 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x3F, 0xFE, 0x00, 0x03, 0xFF, 0xE0, 0x00, + 0x3F, 0xFC, 0x00, 0x03, 0xFF, 0x80, 0x01, 0xC7, 0xF0, 0x00, 0x70, 0x7C, + 0x00, 0x1C, 0x0F, 0x00, 0x06, 0x03, 0xCF, 0x03, 0x80, 0xF3, 0xC0, 0xE0, + 0x3C, 0xF0, 0x38, 0x0E, 0x3C, 0x0E, 0x07, 0x8F, 0x03, 0x01, 0xE3, 0xE1, + 0xC0, 0xF0, 0xF8, 0x70, 0x78, 0x1F, 0x9C, 0xFC, 0x03, 0xFF, 0xFE, 0x00, + 0x7F, 0xFF, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x80, 0x1F, 0x00, 0x00, 0x70, 0x07, 0xFC, 0x00, 0x0E, + 0x00, 0xFF, 0xE0, 0x01, 0xC0, 0x1E, 0x1E, 0x00, 0x3C, 0x03, 0x80, 0xF0, + 0x03, 0x80, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x0E, 0x00, 0xE0, + 0x07, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x3C, 0x00, 0xE0, 0x0E, 0x03, 0x80, + 0x0E, 0x00, 0xE0, 0x70, 0x00, 0xF0, 0x1C, 0x0E, 0x00, 0x07, 0x87, 0xC1, + 0xE0, 0x00, 0x7F, 0xF8, 0x1C, 0x00, 0x03, 0xFE, 0x03, 0x80, 0x00, 0x0F, + 0x80, 0x70, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x1F, + 0x00, 0x00, 0x1C, 0x07, 0xFC, 0x00, 0x03, 0x80, 0xFF, 0xE0, 0x00, 0x70, + 0x1E, 0x1E, 0x00, 0x0F, 0x03, 0x80, 0xF0, 0x00, 0xE0, 0x70, 0x07, 0x00, + 0x1C, 0x07, 0x00, 0x70, 0x03, 0x80, 0xE0, 0x07, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x1C, 0x00, + 0xF0, 0x1C, 0x03, 0x80, 0x07, 0x87, 0xC0, 0x70, 0x00, 0x7F, 0xF8, 0x07, + 0x00, 0x03, 0xFE, 0x00, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF8, 0x00, + 0x03, 0xFF, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0xF8, 0x7C, 0x00, 0x78, 0x0F, + 0x00, 0x1E, 0x03, 0xC0, 0x0F, 0x00, 0xF0, 0x03, 0xC0, 0x3C, 0x00, 0xF0, + 0x1E, 0x00, 0x3C, 0x07, 0x80, 0x0F, 0x87, 0xC0, 0x01, 0xE3, 0xE0, 0x00, + 0x7F, 0xF0, 0x00, 0x0F, 0xF8, 0x00, 0x03, 0xF8, 0x00, 0x03, 0xFC, 0x00, + 0x03, 0xFF, 0x00, 0x01, 0xFB, 0xE0, 0x70, 0xF8, 0x7C, 0x1C, 0x7C, 0x1F, + 0x0E, 0x3C, 0x03, 0xE3, 0x9E, 0x00, 0x79, 0xE7, 0x80, 0x1F, 0xF3, 0xC0, + 0x03, 0xF8, 0xF0, 0x00, 0xFE, 0x3C, 0x00, 0x1F, 0x0F, 0x00, 0x07, 0xC3, + 0xE0, 0x03, 0xF8, 0xF8, 0x03, 0xFE, 0x3F, 0x83, 0xF7, 0xC7, 0xFF, 0xF8, + 0xF0, 0xFF, 0xFC, 0x3E, 0x1F, 0xFC, 0x07, 0x81, 0xFC, 0x00, 0x00, 0x7B, + 0xDF, 0xEF, 0x7B, 0xDC, 0xE7, 0x31, 0x80, 0x00, 0x0E, 0x00, 0x38, 0x00, + 0xE0, 0x03, 0x80, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xE0, 0x03, 0x80, + 0x0F, 0x00, 0x1C, 0x00, 0x78, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x0E, + 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, + 0x1C, 0x00, 0x78, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, + 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, + 0x07, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x30, 0x00, + 0x70, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x70, 0x00, 0xE0, + 0x00, 0xC0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x0E, 0x00, + 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, + 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x01, 0xE0, 0x03, 0x80, + 0x07, 0x00, 0x0E, 0x00, 0x3C, 0x00, 0x70, 0x00, 0xE0, 0x01, 0xC0, 0x07, + 0x00, 0x0E, 0x00, 0x3C, 0x00, 0x70, 0x01, 0xE0, 0x03, 0x80, 0x0F, 0x00, + 0x1C, 0x00, 0x78, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x1C, 0x00, 0x70, + 0x01, 0xC0, 0x07, 0x00, 0x00, 0x01, 0xC0, 0x07, 0x00, 0x38, 0x18, 0xE3, + 0x7B, 0xBF, 0xFF, 0xF3, 0xFF, 0x01, 0xE0, 0x1F, 0xC0, 0xF7, 0x07, 0x9E, + 0x1C, 0x38, 0x20, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x78, + 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, + 0x00, 0x1C, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x78, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3E, 0x7C, 0xF9, 0xE7, + 0xC1, 0x83, 0x0C, 0x18, 0x63, 0xC6, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0x7D, 0xF7, 0xBE, 0xF8, 0x00, 0x00, 0x18, 0x00, 0x01, 0xC0, 0x00, + 0x1C, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x70, 0x00, 0x07, 0x00, + 0x00, 0x30, 0x00, 0x03, 0x80, 0x00, 0x18, 0x00, 0x01, 0xC0, 0x00, 0x0C, + 0x00, 0x00, 0xE0, 0x00, 0x06, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x80, 0x00, 0x1C, 0x00, 0x00, 0xC0, 0x00, 0x0E, 0x00, + 0x00, 0x60, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x38, + 0x00, 0x01, 0x80, 0x00, 0x1C, 0x00, 0x00, 0xC0, 0x00, 0x0E, 0x00, 0x00, + 0x60, 0x00, 0x07, 0x00, 0x00, 0x30, 0x00, 0x03, 0x80, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x0F, 0xFF, + 0xE0, 0x3F, 0x0F, 0xC0, 0xF8, 0x07, 0x81, 0xE0, 0x0F, 0x87, 0x80, 0x0F, + 0x1F, 0x00, 0x1E, 0x3C, 0x00, 0x3C, 0x78, 0x00, 0x79, 0xE0, 0x00, 0xF3, + 0xC0, 0x01, 0xE7, 0x80, 0x07, 0xDE, 0x00, 0x0F, 0xBC, 0x00, 0x1E, 0x78, + 0x00, 0x3C, 0xF0, 0x00, 0x79, 0xE0, 0x00, 0xF7, 0x80, 0x03, 0xEF, 0x00, + 0x07, 0xDE, 0x00, 0x0F, 0x3C, 0x00, 0x1E, 0x78, 0x00, 0x7C, 0xF0, 0x00, + 0xF1, 0xE0, 0x03, 0xE3, 0xC0, 0x07, 0x87, 0xC0, 0x1F, 0x0F, 0x80, 0x7C, + 0x0F, 0xC3, 0xF0, 0x1F, 0xFF, 0xC0, 0x1F, 0xFF, 0x00, 0x1F, 0xFC, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x18, 0x01, 0xC0, 0x1C, 0x01, 0xE0, 0x1F, 0x0F, + 0xFB, 0xFF, 0xDF, 0xFC, 0xFF, 0xE0, 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x3C, + 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x3C, 0x01, 0xE0, 0x0F, 0x00, + 0x78, 0x07, 0xC0, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x3C, + 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x3C, 0x00, 0x00, 0x03, 0xFC, + 0x00, 0x03, 0xFF, 0xE0, 0x00, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xE0, 0x0F, + 0xC0, 0xFC, 0x03, 0xE0, 0x07, 0xC0, 0xF8, 0x00, 0xF8, 0x1F, 0x00, 0x0F, + 0x03, 0xC0, 0x01, 0xE0, 0xF8, 0x00, 0x3C, 0x1E, 0x00, 0x07, 0x80, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x1F, + 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xFF, + 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFC, + 0x00, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xFE, 0x00, 0x3F, 0xFF, 0x80, 0x3F, + 0xFF, 0xE0, 0x1F, 0x81, 0xF8, 0x1F, 0x00, 0x7C, 0x1F, 0x00, 0x1E, 0x0F, + 0x00, 0x0F, 0x0F, 0x80, 0x07, 0x87, 0x80, 0x03, 0xC0, 0x00, 0x03, 0xC0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x7F, 0xE0, + 0x00, 0x3F, 0xE0, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0x79, 0xE0, 0x00, 0x3C, 0xF0, 0x00, 0x1E, 0x78, 0x00, 0x1E, 0x3C, 0x00, + 0x0F, 0x1E, 0x00, 0x0F, 0x0F, 0x80, 0x1F, 0x83, 0xF0, 0x3F, 0x81, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x1F, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x03, 0xFC, 0x00, 0x07, 0xBC, + 0x00, 0x0F, 0xBC, 0x00, 0x1F, 0x7C, 0x00, 0x3E, 0x78, 0x00, 0x7C, 0x78, + 0x00, 0xF8, 0x78, 0x00, 0xF0, 0x78, 0x01, 0xE0, 0xF0, 0x03, 0xC0, 0xF0, + 0x07, 0x80, 0xF0, 0x0F, 0x00, 0xF0, 0x1E, 0x01, 0xF0, 0x3C, 0x01, 0xE0, + 0x78, 0x01, 0xE0, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, + 0x00, 0x07, 0x80, 0x00, 0x07, 0x80, 0x00, 0x07, 0x80, 0x00, 0x07, 0x80, + 0x00, 0x0F, 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x1F, 0xFF, 0xF8, 0x03, 0xFF, + 0xFF, 0x00, 0x7F, 0xFF, 0xE0, 0x1E, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x03, 0xC7, 0xE0, 0x00, 0xF7, 0xFF, 0x80, 0x1F, + 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0x80, 0xFE, 0x03, 0xF0, 0x1F, 0x00, 0x3F, + 0x03, 0xC0, 0x03, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x07, 0x80, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x1E, 0x1E, 0x00, 0x03, 0xC3, 0xC0, 0x00, 0xF0, 0x7C, 0x00, + 0x3C, 0x0F, 0x80, 0x0F, 0x80, 0xFC, 0x07, 0xE0, 0x1F, 0xFF, 0xF8, 0x01, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFC, 0x00, 0xFF, 0xFE, 0x01, 0xF8, + 0x3E, 0x03, 0xE0, 0x1F, 0x07, 0xC0, 0x1F, 0x0F, 0x80, 0x0F, 0x0F, 0x00, + 0x0F, 0x1F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3C, 0x1F, + 0x80, 0x3C, 0x7F, 0xE0, 0x3D, 0xFF, 0xF0, 0x7B, 0xFF, 0xF8, 0x7F, 0xC1, + 0xF8, 0x7F, 0x00, 0x7C, 0x7E, 0x00, 0x7C, 0xFC, 0x00, 0x3C, 0xF8, 0x00, + 0x3C, 0xF8, 0x00, 0x3C, 0xF0, 0x00, 0x3C, 0xF0, 0x00, 0x38, 0xF0, 0x00, + 0x78, 0xF0, 0x00, 0x78, 0xF0, 0x00, 0xF0, 0xF8, 0x01, 0xF0, 0x7C, 0x03, + 0xE0, 0x7E, 0x0F, 0xC0, 0x3F, 0xFF, 0xC0, 0x3F, 0xFF, 0x80, 0x0F, 0xFE, + 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xE1, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x80, + 0x00, 0x03, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x80, 0x00, 0x03, + 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xE0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0x80, + 0x00, 0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xFE, 0x00, 0x1F, + 0xFF, 0x80, 0x1F, 0xFF, 0xE0, 0x1F, 0x81, 0xF8, 0x1F, 0x00, 0x7C, 0x0F, + 0x00, 0x1E, 0x0F, 0x00, 0x0F, 0x07, 0x80, 0x07, 0x83, 0xC0, 0x03, 0xC1, + 0xE0, 0x03, 0xC0, 0xF8, 0x03, 0xC0, 0x7E, 0x07, 0xC0, 0x1F, 0xFF, 0xC0, + 0x07, 0xFF, 0xC0, 0x03, 0xFF, 0xE0, 0x07, 0xFF, 0xF8, 0x07, 0xE0, 0x7E, + 0x07, 0xC0, 0x0F, 0x07, 0x80, 0x07, 0xC7, 0xC0, 0x01, 0xE3, 0xC0, 0x00, + 0xF3, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x3C, 0xF0, 0x00, 0x1C, 0x78, 0x00, + 0x1E, 0x3C, 0x00, 0x0F, 0x1F, 0x00, 0x0F, 0x0F, 0xC0, 0x0F, 0x83, 0xF0, + 0x3F, 0x81, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x0F, 0xFF, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x7F, 0xF0, 0x01, 0xFF, 0xFC, + 0x03, 0xFF, 0xFC, 0x07, 0xF0, 0x7E, 0x07, 0xC0, 0x3E, 0x0F, 0x80, 0x1F, + 0x0F, 0x00, 0x0F, 0x1E, 0x00, 0x0F, 0x1E, 0x00, 0x0F, 0x3C, 0x00, 0x0F, + 0x3C, 0x00, 0x0F, 0x3C, 0x00, 0x1F, 0x3C, 0x00, 0x1F, 0x3C, 0x00, 0x3F, + 0x3E, 0x00, 0x7E, 0x3E, 0x00, 0xFE, 0x1F, 0x83, 0xFE, 0x1F, 0xFF, 0xFE, + 0x0F, 0xFF, 0xBC, 0x07, 0xFE, 0x3C, 0x01, 0xF8, 0x7C, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x78, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF0, 0xF0, 0x01, 0xF0, + 0xF0, 0x03, 0xE0, 0xF8, 0x07, 0xC0, 0xFC, 0x1F, 0xC0, 0x7F, 0xFF, 0x80, + 0x3F, 0xFE, 0x00, 0x1F, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xC1, 0xF0, + 0x78, 0x3E, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x1F, + 0x07, 0x83, 0xE0, 0xF8, 0x00, 0x03, 0xE0, 0x7C, 0x0F, 0x03, 0xE0, 0x7C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x7C, 0x0F, + 0x81, 0xE0, 0x7C, 0x01, 0x80, 0x30, 0x0C, 0x01, 0x80, 0x60, 0x3C, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x70, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0xFE, 0x00, 0x01, 0xFF, 0x00, 0x03, 0xFE, 0x00, 0x03, 0xFE, 0x00, + 0x07, 0xFC, 0x00, 0x07, 0xFC, 0x00, 0x0F, 0xF8, 0x00, 0x07, 0xF0, 0x00, + 0x03, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x00, + 0x01, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x00, 0x80, 0x1F, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xE3, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, + 0xC7, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xF8, 0x04, 0x00, 0x00, 0x01, 0xC0, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x03, 0xFE, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x7F, 0xC0, 0x00, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x01, + 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x03, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0x00, 0x3F, 0xF8, 0x0F, 0xFF, 0xC1, 0xFF, 0xFE, 0x1F, 0x03, 0xE3, 0xE0, + 0x1F, 0x7C, 0x00, 0xF7, 0x80, 0x0F, 0x78, 0x00, 0xFF, 0x00, 0x0F, 0xF0, + 0x01, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, + 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x1F, 0x00, + 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0x80, 0x00, 0x78, 0x00, 0x0F, 0x80, + 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1E, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xE0, 0x00, 0x3E, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, + 0x00, 0x00, 0x7F, 0xE0, 0x0F, 0xF8, 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0xE0, + 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x0F, 0xC0, 0x00, 0x00, 0xFC, 0x00, + 0xFC, 0x00, 0x00, 0x01, 0xF0, 0x0F, 0xC0, 0x00, 0x00, 0x0F, 0x80, 0xF8, + 0x00, 0xFC, 0x00, 0x3E, 0x0F, 0x80, 0x1F, 0xF9, 0xE1, 0xF0, 0x78, 0x03, + 0xFF, 0xCF, 0x07, 0x87, 0xC0, 0x3F, 0x0F, 0xF0, 0x3C, 0x7C, 0x03, 0xE0, + 0x3F, 0x01, 0xE3, 0xC0, 0x3E, 0x01, 0xF8, 0x0F, 0x3E, 0x03, 0xE0, 0x0F, + 0x80, 0x79, 0xE0, 0x1E, 0x00, 0x7C, 0x03, 0xDF, 0x01, 0xE0, 0x03, 0xC0, + 0x3E, 0xF0, 0x1F, 0x00, 0x3E, 0x01, 0xE7, 0x80, 0xF0, 0x01, 0xE0, 0x0F, + 0x38, 0x07, 0x80, 0x0F, 0x00, 0xFB, 0xC0, 0x78, 0x00, 0xF0, 0x07, 0x9E, + 0x03, 0xC0, 0x07, 0x80, 0x7C, 0xF0, 0x1E, 0x00, 0x78, 0x07, 0xC7, 0x80, + 0xF0, 0x07, 0xC0, 0x7E, 0x3C, 0x07, 0x80, 0x7C, 0x07, 0xE1, 0xE0, 0x3E, + 0x07, 0xE0, 0x7E, 0x0F, 0x00, 0xF8, 0x7F, 0x8F, 0xC0, 0x7C, 0x07, 0xFF, + 0x7F, 0xFC, 0x01, 0xE0, 0x1F, 0xF1, 0xFF, 0x80, 0x0F, 0x00, 0x7E, 0x0F, + 0xF0, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x00, 0xF8, 0x00, 0x00, + 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0x80, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x1F, 0xE0, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x03, 0xDF, 0x00, 0x00, 0x1E, + 0x7C, 0x00, 0x00, 0x79, 0xF0, 0x00, 0x03, 0xC7, 0xC0, 0x00, 0x0F, 0x1F, + 0x00, 0x00, 0x78, 0x3C, 0x00, 0x03, 0xE0, 0xF0, 0x00, 0x0F, 0x03, 0xE0, + 0x00, 0x78, 0x0F, 0x80, 0x01, 0xE0, 0x3E, 0x00, 0x0F, 0x00, 0xF8, 0x00, + 0x3C, 0x03, 0xE0, 0x01, 0xE0, 0x0F, 0x80, 0x0F, 0x80, 0x1E, 0x00, 0x3C, + 0x00, 0x7C, 0x01, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, + 0xFF, 0x00, 0xFF, 0xFF, 0xFC, 0x07, 0xC0, 0x01, 0xF0, 0x3E, 0x00, 0x03, + 0xC0, 0xF8, 0x00, 0x0F, 0x87, 0xC0, 0x00, 0x3E, 0x1E, 0x00, 0x00, 0xF8, + 0xF8, 0x00, 0x03, 0xE3, 0xC0, 0x00, 0x0F, 0x9F, 0x00, 0x00, 0x3E, 0xF8, + 0x00, 0x00, 0x7B, 0xE0, 0x00, 0x01, 0xF0, 0x01, 0xFF, 0xFF, 0x00, 0x0F, + 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xE0, 0x3E, 0x00, + 0x1F, 0x81, 0xE0, 0x00, 0x7C, 0x0F, 0x00, 0x01, 0xE0, 0x78, 0x00, 0x0F, + 0x03, 0xC0, 0x00, 0x78, 0x3C, 0x00, 0x03, 0xC1, 0xE0, 0x00, 0x3C, 0x0F, + 0x00, 0x01, 0xE0, 0x78, 0x00, 0x1E, 0x07, 0xC0, 0x03, 0xE0, 0x3F, 0xFF, + 0xFC, 0x01, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFE, + 0x07, 0x80, 0x01, 0xF0, 0x3C, 0x00, 0x07, 0xC1, 0xE0, 0x00, 0x1E, 0x0F, + 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x07, 0x87, 0x80, 0x00, 0x3C, 0x3C, 0x00, + 0x01, 0xE1, 0xE0, 0x00, 0x1E, 0x1F, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x0F, + 0x87, 0x80, 0x00, 0xF8, 0x3C, 0x00, 0x1F, 0x81, 0xFF, 0xFF, 0xF8, 0x1F, + 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x1F, + 0xFF, 0xFC, 0x00, 0xFE, 0x03, 0xF0, 0x07, 0xE0, 0x03, 0xE0, 0x3E, 0x00, + 0x07, 0x81, 0xF0, 0x00, 0x1E, 0x07, 0x80, 0x00, 0x3C, 0x3C, 0x00, 0x00, + 0xF1, 0xF0, 0x00, 0x03, 0xC7, 0x80, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x3C, 0xF0, 0x00, 0x01, 0xF3, 0xC0, 0x00, 0x07, + 0x8F, 0x80, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0xF0, 0x7C, 0x00, 0x07, 0xC1, + 0xF0, 0x00, 0x3E, 0x03, 0xE0, 0x03, 0xF0, 0x0F, 0xE0, 0x3F, 0x80, 0x1F, + 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x07, 0xFF, + 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0x80, 0x3E, 0x00, 0x3F, 0x80, 0x78, 0x00, + 0x1F, 0x80, 0xF0, 0x00, 0x1F, 0x03, 0xE0, 0x00, 0x1E, 0x07, 0xC0, 0x00, + 0x3E, 0x0F, 0x00, 0x00, 0x3C, 0x1E, 0x00, 0x00, 0x78, 0x3C, 0x00, 0x00, + 0xF0, 0xF8, 0x00, 0x01, 0xE1, 0xF0, 0x00, 0x03, 0xC3, 0xC0, 0x00, 0x07, + 0x87, 0x80, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x3C, 0x3E, 0x00, 0x00, 0x78, + 0x7C, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x01, 0xE1, 0xE0, 0x00, 0x07, 0x87, + 0xC0, 0x00, 0x0F, 0x0F, 0x80, 0x00, 0x3E, 0x1E, 0x00, 0x00, 0x78, 0x3C, + 0x00, 0x01, 0xF0, 0x78, 0x00, 0x03, 0xC1, 0xF0, 0x00, 0x0F, 0x03, 0xE0, + 0x00, 0x3E, 0x07, 0x80, 0x01, 0xF8, 0x0F, 0x00, 0x0F, 0xE0, 0x1F, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, + 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, + 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xE0, 0x3E, 0x00, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x07, 0xC0, 0x00, + 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, + 0x07, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0x80, + 0x7F, 0xFF, 0xFF, 0x00, 0xF0, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, + 0xFF, 0xC0, 0xFF, 0xFF, 0xFE, 0x03, 0xE0, 0x00, 0x00, 0x0F, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x0F, + 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x03, 0xFF, + 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, + 0xF0, 0x07, 0x80, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x03, 0xE0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xC0, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, + 0xFF, 0xFF, 0xC0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xC0, 0x00, 0xF8, 0x0F, + 0xC0, 0x00, 0x3E, 0x07, 0x80, 0x00, 0x1F, 0x07, 0x80, 0x00, 0x07, 0x87, + 0xC0, 0x00, 0x03, 0xC3, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, + 0xE0, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x1F, 0xFF, + 0xBC, 0x00, 0x0F, 0xFF, 0xDE, 0x00, 0x0F, 0xFF, 0xEF, 0x00, 0x07, 0xFF, + 0xF7, 0x80, 0x00, 0x00, 0x73, 0xC0, 0x00, 0x00, 0x39, 0xE0, 0x00, 0x00, + 0x3C, 0xF0, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x1F, 0x3E, 0x00, 0x00, + 0x0F, 0x8F, 0x00, 0x00, 0x0F, 0x87, 0xC0, 0x00, 0x0F, 0xC3, 0xF0, 0x00, + 0x0F, 0xE0, 0xFC, 0x00, 0x1F, 0xF0, 0x7F, 0x80, 0x7F, 0x78, 0x1F, 0xFF, + 0xFE, 0x38, 0x03, 0xFF, 0xFE, 0x1C, 0x00, 0xFF, 0xFC, 0x0E, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0xF0, 0x00, 0x03, 0xC0, + 0x78, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x01, 0xF0, 0x3E, 0x00, 0x00, 0xF0, + 0x1E, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x3C, 0x0F, 0x80, 0x00, 0x3E, + 0x07, 0xC0, 0x00, 0x1F, 0x03, 0xC0, 0x00, 0x0F, 0x01, 0xE0, 0x00, 0x07, + 0x80, 0xF0, 0x00, 0x03, 0xC0, 0xF8, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x01, + 0xF0, 0x3C, 0x00, 0x00, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, + 0xFC, 0x0F, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x00, + 0x0F, 0x01, 0xE0, 0x00, 0x07, 0x81, 0xF0, 0x00, 0x07, 0xC0, 0xF8, 0x00, + 0x03, 0xE0, 0x78, 0x00, 0x01, 0xE0, 0x3C, 0x00, 0x00, 0xF0, 0x1E, 0x00, + 0x00, 0x78, 0x1F, 0x00, 0x00, 0x7C, 0x0F, 0x80, 0x00, 0x3C, 0x07, 0x80, + 0x00, 0x1E, 0x03, 0xC0, 0x00, 0x0F, 0x01, 0xE0, 0x00, 0x0F, 0x81, 0xF0, + 0x00, 0x07, 0xC0, 0xF0, 0x00, 0x03, 0xC0, 0x78, 0x00, 0x01, 0xE0, 0x00, + 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xE0, 0x3C, 0x07, 0x81, 0xF0, 0x3E, 0x07, + 0x80, 0xF0, 0x1E, 0x07, 0xC0, 0xF8, 0x1E, 0x03, 0xC0, 0x78, 0x1F, 0x03, + 0xE0, 0x78, 0x0F, 0x01, 0xE0, 0x7C, 0x0F, 0x81, 0xE0, 0x3C, 0x07, 0x81, + 0xF0, 0x3E, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0xC0, 0xF8, 0x1E, 0x00, 0x00, + 0x00, 0x07, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xE0, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x80, 0x00, 0x07, + 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0x80, 0x00, 0x03, 0xC0, 0xF0, + 0x01, 0xE0, 0x78, 0x00, 0xF0, 0x78, 0x00, 0xF8, 0x3C, 0x00, 0x78, 0x1E, + 0x00, 0x3C, 0x0F, 0x00, 0x3E, 0x07, 0xC0, 0x3E, 0x03, 0xF0, 0x7E, 0x00, + 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0x00, 0x0F, 0xFE, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x01, 0xE0, 0x00, 0x0F, 0xC0, 0x78, 0x00, 0x07, 0xC0, 0x1E, 0x00, + 0x03, 0xE0, 0x0F, 0x80, 0x03, 0xF0, 0x03, 0xE0, 0x01, 0xF8, 0x00, 0xF0, + 0x00, 0xFC, 0x00, 0x3C, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x07, + 0xC0, 0x3F, 0x00, 0x01, 0xE0, 0x1F, 0x80, 0x00, 0x78, 0x0F, 0x80, 0x00, + 0x1E, 0x07, 0xC0, 0x00, 0x0F, 0x83, 0xE0, 0x00, 0x03, 0xE3, 0xF0, 0x00, + 0x00, 0xF1, 0xFC, 0x00, 0x00, 0x3C, 0xFF, 0x00, 0x00, 0x0F, 0x7F, 0xE0, + 0x00, 0x07, 0xFE, 0xF8, 0x00, 0x01, 0xFE, 0x1E, 0x00, 0x00, 0x7F, 0x07, + 0xC0, 0x00, 0x1F, 0x80, 0xF0, 0x00, 0x0F, 0xC0, 0x3E, 0x00, 0x03, 0xE0, + 0x07, 0x80, 0x00, 0xF0, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x7C, 0x00, 0x0F, + 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x78, 0x00, 0x1F, 0x00, 0x1E, 0x00, 0x03, 0xE0, 0x07, 0x80, 0x00, 0xF8, + 0x03, 0xE0, 0x00, 0x1F, 0x00, 0xF0, 0x00, 0x07, 0xC0, 0x3C, 0x00, 0x00, + 0xF8, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, 0xF8, + 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, 0x01, 0xF0, 0x00, + 0x07, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x0F, + 0x80, 0x00, 0x3E, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, + 0x00, 0x7C, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, + 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF0, + 0x00, 0x07, 0xC0, 0x00, 0x1F, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xE0, + 0x01, 0xF8, 0x00, 0x00, 0x7E, 0x03, 0xF8, 0x00, 0x01, 0xFC, 0x0F, 0xF0, + 0x00, 0x03, 0xF8, 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x3F, 0xC0, 0x00, 0x1F, + 0xC0, 0x7F, 0x80, 0x00, 0x7F, 0x80, 0xFF, 0x00, 0x00, 0xEF, 0x03, 0xFE, + 0x00, 0x03, 0xFE, 0x07, 0xBC, 0x00, 0x0F, 0x78, 0x0F, 0x3C, 0x00, 0x1E, + 0xF0, 0x1E, 0x78, 0x00, 0x79, 0xE0, 0x3C, 0xF0, 0x00, 0xF3, 0xC0, 0xF9, + 0xE0, 0x03, 0xCF, 0x81, 0xE3, 0xC0, 0x07, 0x9E, 0x03, 0xC7, 0x80, 0x1E, + 0x3C, 0x07, 0x8F, 0x00, 0x38, 0x78, 0x1F, 0x1E, 0x00, 0xF0, 0xF0, 0x3C, + 0x1E, 0x03, 0xC3, 0xE0, 0x78, 0x3C, 0x07, 0x87, 0x80, 0xF0, 0x78, 0x1E, + 0x0F, 0x01, 0xE0, 0xF0, 0x3C, 0x1E, 0x07, 0xC1, 0xE0, 0xF0, 0x7C, 0x0F, + 0x03, 0xC1, 0xE0, 0xF0, 0x1E, 0x07, 0x87, 0x81, 0xE0, 0x3C, 0x0F, 0x0E, + 0x03, 0xC0, 0x78, 0x0F, 0x3C, 0x07, 0x81, 0xF0, 0x1E, 0x70, 0x1F, 0x03, + 0xC0, 0x3D, 0xE0, 0x3C, 0x07, 0x80, 0x7F, 0x80, 0x78, 0x0F, 0x00, 0xFF, + 0x00, 0xF0, 0x3E, 0x01, 0xFC, 0x01, 0xE0, 0x78, 0x03, 0xF8, 0x07, 0xC0, + 0xF0, 0x07, 0xE0, 0x0F, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x01, 0xF0, + 0x00, 0x03, 0xC0, 0x7E, 0x00, 0x01, 0xF0, 0x3F, 0x80, 0x00, 0x78, 0x0F, + 0xE0, 0x00, 0x1E, 0x03, 0xFC, 0x00, 0x07, 0x80, 0xFF, 0x00, 0x03, 0xE0, + 0x3F, 0xE0, 0x00, 0xF0, 0x1F, 0xF8, 0x00, 0x3C, 0x07, 0x9E, 0x00, 0x0F, + 0x01, 0xE7, 0xC0, 0x03, 0xC0, 0x78, 0xF0, 0x01, 0xF0, 0x1E, 0x3E, 0x00, + 0x78, 0x0F, 0x87, 0x80, 0x1E, 0x03, 0xC1, 0xF0, 0x07, 0x80, 0xF0, 0x7C, + 0x01, 0xE0, 0x3C, 0x0F, 0x00, 0xF8, 0x1F, 0x03, 0xE0, 0x3C, 0x07, 0x80, + 0x78, 0x0F, 0x01, 0xE0, 0x1F, 0x03, 0xC0, 0x78, 0x07, 0xC1, 0xF0, 0x1E, + 0x00, 0xF8, 0x78, 0x0F, 0x80, 0x3E, 0x1E, 0x03, 0xC0, 0x07, 0x87, 0x80, + 0xF0, 0x01, 0xF1, 0xE0, 0x3C, 0x00, 0x3C, 0xF8, 0x0F, 0x00, 0x0F, 0xBC, + 0x07, 0xC0, 0x03, 0xEF, 0x01, 0xE0, 0x00, 0x7F, 0xC0, 0x78, 0x00, 0x1F, + 0xF0, 0x1E, 0x00, 0x03, 0xFC, 0x0F, 0x80, 0x00, 0xFE, 0x03, 0xC0, 0x00, + 0x1F, 0x80, 0xF0, 0x00, 0x07, 0xE0, 0x3C, 0x00, 0x01, 0xF8, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFC, 0x00, + 0x0F, 0xFF, 0xFF, 0x80, 0x0F, 0xF0, 0x1F, 0xC0, 0x0F, 0xC0, 0x03, 0xF0, + 0x0F, 0x80, 0x00, 0xFC, 0x0F, 0x80, 0x00, 0x3E, 0x0F, 0x80, 0x00, 0x0F, + 0x07, 0x80, 0x00, 0x07, 0xC7, 0xC0, 0x00, 0x01, 0xE3, 0xC0, 0x00, 0x00, + 0xF3, 0xC0, 0x00, 0x00, 0x79, 0xE0, 0x00, 0x00, 0x3D, 0xE0, 0x00, 0x00, + 0x1E, 0xF0, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x07, 0xFC, 0x00, 0x00, 0x03, 0xDE, 0x00, 0x00, 0x01, 0xEF, 0x00, 0x00, + 0x00, 0xF7, 0x80, 0x00, 0x00, 0xFB, 0xC0, 0x00, 0x00, 0x79, 0xE0, 0x00, + 0x00, 0x3C, 0xF0, 0x00, 0x00, 0x3E, 0x78, 0x00, 0x00, 0x1E, 0x3E, 0x00, + 0x00, 0x1F, 0x0F, 0x00, 0x00, 0x1F, 0x07, 0xC0, 0x00, 0x1F, 0x03, 0xF0, + 0x00, 0x1F, 0x00, 0xFC, 0x00, 0x3F, 0x80, 0x3F, 0x80, 0x7F, 0x80, 0x1F, + 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, + 0x0F, 0xF8, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFE, 0x00, + 0x7F, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xE0, 0x3E, 0x00, 0x3F, 0x81, 0xE0, + 0x00, 0x7C, 0x0F, 0x00, 0x01, 0xE0, 0xF8, 0x00, 0x0F, 0x07, 0xC0, 0x00, + 0x78, 0x3C, 0x00, 0x03, 0xC1, 0xE0, 0x00, 0x1E, 0x0F, 0x00, 0x01, 0xE0, + 0xF8, 0x00, 0x0F, 0x07, 0xC0, 0x00, 0xF8, 0x3C, 0x00, 0x0F, 0x81, 0xE0, + 0x01, 0xF8, 0x0F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, + 0x80, 0x3F, 0xFF, 0xF0, 0x01, 0xE0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x01, 0xE0, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x07, 0x80, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFF, + 0x80, 0x0F, 0xF0, 0x1F, 0xC0, 0x0F, 0xC0, 0x03, 0xF0, 0x0F, 0xC0, 0x00, + 0xFC, 0x0F, 0x80, 0x00, 0x3E, 0x0F, 0x80, 0x00, 0x0F, 0x07, 0x80, 0x00, + 0x07, 0xC7, 0xC0, 0x00, 0x01, 0xE3, 0xC0, 0x00, 0x00, 0xF3, 0xC0, 0x00, + 0x00, 0x79, 0xE0, 0x00, 0x00, 0x3D, 0xE0, 0x00, 0x00, 0x1E, 0xF0, 0x00, + 0x00, 0x0F, 0x78, 0x00, 0x00, 0x07, 0xB8, 0x00, 0x00, 0x03, 0xFC, 0x00, + 0x00, 0x03, 0xDE, 0x00, 0x00, 0x01, 0xEF, 0x00, 0x00, 0x00, 0xF7, 0x80, + 0x00, 0x00, 0x7B, 0xC0, 0x00, 0x00, 0x79, 0xE0, 0x00, 0x00, 0x3C, 0xF0, + 0x00, 0x00, 0x3C, 0x78, 0x00, 0x08, 0x3E, 0x3E, 0x00, 0x0E, 0x1E, 0x0F, + 0x00, 0x0F, 0x9F, 0x07, 0xC0, 0x07, 0xFF, 0x03, 0xF0, 0x01, 0xFF, 0x00, + 0xFC, 0x00, 0x7F, 0x00, 0x3F, 0x80, 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xE0, + 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFC, 0x7E, 0x00, 0x0F, 0xF0, 0x1F, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0xC0, 0x07, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFE, + 0x03, 0xE0, 0x00, 0xFC, 0x0F, 0x00, 0x01, 0xF0, 0x3C, 0x00, 0x03, 0xC1, + 0xF0, 0x00, 0x0F, 0x07, 0xC0, 0x00, 0x3C, 0x1E, 0x00, 0x00, 0xF0, 0x78, + 0x00, 0x03, 0xC1, 0xE0, 0x00, 0x1E, 0x0F, 0x80, 0x00, 0x78, 0x3E, 0x00, + 0x03, 0xE0, 0xF0, 0x00, 0x1F, 0x03, 0xC0, 0x01, 0xF8, 0x0F, 0xFF, 0xFF, + 0xC0, 0x7F, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xF0, + 0x1E, 0x00, 0x07, 0xE0, 0xF8, 0x00, 0x0F, 0x83, 0xE0, 0x00, 0x1E, 0x0F, + 0x00, 0x00, 0x78, 0x3C, 0x00, 0x01, 0xE0, 0xF0, 0x00, 0x07, 0x87, 0xC0, + 0x00, 0x1E, 0x1F, 0x00, 0x00, 0xF0, 0x78, 0x00, 0x03, 0xC1, 0xE0, 0x00, + 0x0F, 0x07, 0x80, 0x00, 0x3C, 0x3E, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x03, + 0xC3, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, 0xFC, + 0x00, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xC0, 0x1F, 0xC0, 0x7F, 0x01, + 0xF0, 0x00, 0xFC, 0x0F, 0x00, 0x03, 0xE0, 0xF0, 0x00, 0x0F, 0x07, 0x00, + 0x00, 0x78, 0x78, 0x00, 0x03, 0xC3, 0xC0, 0x00, 0x1E, 0x1E, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x0F, + 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x3C, 0xF0, 0x00, 0x01, 0xE7, + 0x80, 0x00, 0x0F, 0x3C, 0x00, 0x00, 0x71, 0xE0, 0x00, 0x07, 0x8F, 0x00, + 0x00, 0x3C, 0x7C, 0x00, 0x03, 0xC1, 0xF0, 0x00, 0x7C, 0x0F, 0xE0, 0x1F, + 0xC0, 0x3F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xF8, 0x00, + 0x03, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0x7F, + 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFE, 0x00, 0x0F, 0x00, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0F, + 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0x0F, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x03, 0xC0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x0F, 0x00, 0x00, 0x3C, 0x1E, + 0x00, 0x00, 0xF8, 0x7C, 0x00, 0x01, 0xF0, 0xF8, 0x00, 0x03, 0xC1, 0xE0, + 0x00, 0x07, 0x83, 0xC0, 0x00, 0x0F, 0x0F, 0x80, 0x00, 0x3E, 0x1F, 0x00, + 0x00, 0x7C, 0x3C, 0x00, 0x00, 0xF0, 0x78, 0x00, 0x01, 0xE0, 0xF0, 0x00, + 0x03, 0xC3, 0xE0, 0x00, 0x0F, 0x87, 0xC0, 0x00, 0x1F, 0x0F, 0x00, 0x00, + 0x3C, 0x1E, 0x00, 0x00, 0x78, 0x3C, 0x00, 0x01, 0xF0, 0xF8, 0x00, 0x03, + 0xE1, 0xF0, 0x00, 0x07, 0x83, 0xC0, 0x00, 0x0F, 0x07, 0x80, 0x00, 0x1E, + 0x1F, 0x00, 0x00, 0x7C, 0x3E, 0x00, 0x00, 0xF8, 0x78, 0x00, 0x01, 0xE0, + 0xF0, 0x00, 0x03, 0xC1, 0xE0, 0x00, 0x0F, 0x83, 0xC0, 0x00, 0x1E, 0x07, + 0x80, 0x00, 0x7C, 0x0F, 0x80, 0x01, 0xF0, 0x0F, 0x80, 0x07, 0xE0, 0x1F, + 0xC0, 0x7F, 0x80, 0x1F, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x1F, + 0xFF, 0xC0, 0x00, 0x07, 0xFC, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFF, 0xC0, + 0x00, 0x0F, 0xBE, 0x00, 0x00, 0x79, 0xF0, 0x00, 0x07, 0xC7, 0x80, 0x00, + 0x3C, 0x3C, 0x00, 0x03, 0xE1, 0xE0, 0x00, 0x1E, 0x0F, 0x80, 0x01, 0xF0, + 0x7C, 0x00, 0x0F, 0x03, 0xE0, 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0x80, 0x78, + 0x00, 0x78, 0x03, 0xC0, 0x07, 0xC0, 0x1E, 0x00, 0x3C, 0x00, 0xF0, 0x03, + 0xE0, 0x07, 0xC0, 0x1E, 0x00, 0x3E, 0x01, 0xF0, 0x01, 0xF0, 0x0F, 0x00, + 0x07, 0x80, 0xF0, 0x00, 0x3C, 0x07, 0x80, 0x01, 0xE0, 0x78, 0x00, 0x0F, + 0x07, 0xC0, 0x00, 0x7C, 0x3C, 0x00, 0x03, 0xE3, 0xE0, 0x00, 0x1F, 0x1E, + 0x00, 0x00, 0xF9, 0xF0, 0x00, 0x03, 0xCF, 0x00, 0x00, 0x1E, 0xF0, 0x00, + 0x00, 0xF7, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF0, + 0x00, 0x1F, 0x00, 0x03, 0xDE, 0x00, 0x07, 0xE0, 0x00, 0xFB, 0xC0, 0x00, + 0xFC, 0x00, 0x1E, 0x78, 0x00, 0x3F, 0x80, 0x07, 0xCF, 0x00, 0x07, 0xF0, + 0x00, 0xF9, 0xE0, 0x01, 0xFE, 0x00, 0x3E, 0x3C, 0x00, 0x7F, 0xC0, 0x07, + 0xC7, 0x80, 0x0F, 0x78, 0x01, 0xF0, 0xF0, 0x03, 0xEF, 0x00, 0x3E, 0x1E, + 0x00, 0x79, 0xE0, 0x0F, 0x83, 0xC0, 0x1F, 0x3C, 0x01, 0xF0, 0x78, 0x03, + 0xC7, 0x80, 0x3C, 0x0F, 0x00, 0xF8, 0xF0, 0x0F, 0x80, 0xE0, 0x1E, 0x1E, + 0x01, 0xE0, 0x1C, 0x07, 0xC1, 0xC0, 0x7C, 0x03, 0x80, 0xF0, 0x3C, 0x0F, + 0x00, 0x70, 0x3E, 0x07, 0x83, 0xE0, 0x0E, 0x07, 0x80, 0xF0, 0x78, 0x01, + 0xC1, 0xF0, 0x1E, 0x1F, 0x00, 0x3C, 0x3C, 0x03, 0xC3, 0xE0, 0x07, 0x8F, + 0x80, 0x78, 0x78, 0x00, 0xF1, 0xE0, 0x0F, 0x1F, 0x00, 0x1E, 0x7C, 0x01, + 0xE3, 0xC0, 0x03, 0xCF, 0x00, 0x3C, 0xF8, 0x00, 0x7B, 0xE0, 0x07, 0x9E, + 0x00, 0x0F, 0x78, 0x00, 0xF7, 0xC0, 0x01, 0xFF, 0x00, 0x1E, 0xF0, 0x00, + 0x3F, 0xC0, 0x03, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x7F, 0x80, 0x00, 0xFE, + 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, + 0x1F, 0x80, 0x00, 0x7E, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x7C, + 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x07, 0xC0, 0x00, 0x3E, 0x00, + 0x7C, 0x00, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x1F, + 0x00, 0x01, 0xF0, 0x03, 0xE0, 0x00, 0x1F, 0x00, 0x7C, 0x00, 0x00, 0xF8, + 0x0F, 0x80, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0x00, 0x7C, 0x1F, 0x00, 0x00, + 0x07, 0xC3, 0xE0, 0x00, 0x00, 0x7C, 0x7C, 0x00, 0x00, 0x03, 0xEF, 0x80, + 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x0F, 0xBE, 0x00, + 0x00, 0x01, 0xF3, 0xE0, 0x00, 0x00, 0x3E, 0x1F, 0x00, 0x00, 0x03, 0xE1, + 0xF0, 0x00, 0x00, 0x7C, 0x0F, 0x80, 0x00, 0x0F, 0x80, 0xF8, 0x00, 0x01, + 0xF0, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x03, 0xE0, + 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x03, 0xF0, 0x03, 0xF0, 0x00, + 0x1F, 0x00, 0x7E, 0x00, 0x01, 0xF8, 0x0F, 0xC0, 0x00, 0x0F, 0x80, 0xF8, + 0x00, 0x00, 0x7D, 0xF0, 0x00, 0x03, 0xE7, 0xC0, 0x00, 0x1F, 0x1F, 0x80, + 0x00, 0xF8, 0x3E, 0x00, 0x03, 0xE0, 0xF8, 0x00, 0x1F, 0x01, 0xF0, 0x00, + 0xF8, 0x07, 0xC0, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0x3E, 0x01, 0xF0, + 0x00, 0xF8, 0x07, 0xC0, 0x01, 0xF0, 0x3E, 0x00, 0x07, 0xC1, 0xF0, 0x00, + 0x0F, 0x0F, 0x80, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x79, 0xE0, 0x00, 0x01, + 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x0F, 0x80, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, + 0x07, 0x80, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x03, + 0xE0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xC0, + 0x1F, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFE, + 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0x03, 0xE0, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0F, 0x80, + 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0x80, 0x7F, + 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x7F, 0xC0, 0x1F, + 0xF0, 0x07, 0xFC, 0x01, 0xFE, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, + 0x03, 0xC0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, + 0xE0, 0x00, 0xF8, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, + 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0xF8, 0x00, + 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x7C, 0x00, 0x1E, + 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x0F, 0x00, + 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x7C, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, + 0xE0, 0x00, 0x7F, 0xC0, 0x3F, 0xE0, 0x0F, 0xF8, 0x03, 0xFE, 0x00, 0xE3, + 0x8E, 0x38, 0xE1, 0x86, 0x18, 0x61, 0x87, 0x1C, 0x71, 0xC7, 0x0C, 0x30, + 0xC3, 0x0C, 0x38, 0xE3, 0x8E, 0x38, 0x61, 0x86, 0x18, 0x61, 0xC7, 0x1C, + 0x71, 0xC0, 0x00, 0x7F, 0xC0, 0x1F, 0xF0, 0x07, 0xFC, 0x03, 0xFE, 0x00, + 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x03, + 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, + 0x00, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, + 0x3C, 0x00, 0x1F, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, + 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, 0x00, + 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x03, + 0xC0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x7F, 0x80, 0x3F, 0xE0, + 0x0F, 0xF8, 0x03, 0xFE, 0x00, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x1F, 0xC0, 0x0E, 0xE0, 0x0E, 0x70, 0x0F, 0x38, 0x07, 0x1C, 0x07, 0x0E, + 0x03, 0x83, 0x83, 0x81, 0xC3, 0xC0, 0xE1, 0xC0, 0x71, 0xC0, 0x39, 0xE0, + 0x0E, 0xE0, 0x07, 0xF0, 0x03, 0xF0, 0x01, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0xF8, 0x78, 0x3C, 0x1C, 0x0E, 0x0E, 0x07, 0x00, + 0x1F, 0xE0, 0x01, 0xFF, 0xF0, 0x07, 0xFF, 0xF0, 0x1F, 0xFF, 0xF0, 0x7E, + 0x07, 0xE1, 0xF0, 0x07, 0xC3, 0xC0, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xF0, 0x00, 0x07, 0xE0, 0x0F, 0xFF, + 0xC0, 0xFF, 0xFF, 0x07, 0xFF, 0x9E, 0x1F, 0xC0, 0x3C, 0x7C, 0x00, 0x78, + 0xF0, 0x00, 0xF3, 0xC0, 0x03, 0xC7, 0x80, 0x07, 0x8F, 0x00, 0x1F, 0x1E, + 0x00, 0x7E, 0x3F, 0x07, 0xFC, 0x3F, 0xFF, 0x7E, 0x7F, 0xFC, 0xFC, 0x7F, + 0xF0, 0xF8, 0x3F, 0x00, 0xF0, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x78, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x83, 0xE0, 0x03, 0xC7, 0xFC, + 0x01, 0xEF, 0xFF, 0x00, 0xFF, 0xFF, 0xC0, 0xF7, 0x83, 0xF0, 0x7F, 0x00, + 0xF8, 0x3F, 0x00, 0x3E, 0x1F, 0x00, 0x0F, 0x1F, 0x80, 0x07, 0x8F, 0x80, + 0x03, 0xC7, 0x80, 0x01, 0xE3, 0xC0, 0x00, 0xF1, 0xE0, 0x00, 0x79, 0xF0, + 0x00, 0x3C, 0xF0, 0x00, 0x3C, 0x78, 0x00, 0x1E, 0x3C, 0x00, 0x0F, 0x1E, + 0x00, 0x0F, 0x9F, 0x00, 0x07, 0x8F, 0xC0, 0x07, 0xC7, 0xE0, 0x07, 0xC3, + 0xF8, 0x07, 0xC1, 0xFE, 0x0F, 0xC1, 0xEF, 0xFF, 0xE0, 0xF3, 0xFF, 0xC0, + 0x78, 0xFF, 0xC0, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x03, 0xFF, + 0x80, 0x3F, 0xFF, 0x01, 0xFF, 0xFE, 0x0F, 0xE0, 0xF8, 0x7E, 0x01, 0xF1, + 0xF0, 0x03, 0xCF, 0x80, 0x0F, 0x3C, 0x00, 0x3D, 0xF0, 0x00, 0x07, 0x80, + 0x00, 0x1E, 0x00, 0x00, 0xF8, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x0F, 0x3C, + 0x00, 0x3C, 0xF8, 0x01, 0xE1, 0xF0, 0x0F, 0x87, 0xE0, 0xFC, 0x0F, 0xFF, + 0xE0, 0x3F, 0xFF, 0x00, 0x7F, 0xF8, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0xE0, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, + 0x3C, 0x00, 0x3F, 0x07, 0x80, 0x1F, 0xF8, 0xF0, 0x0F, 0xFF, 0x3E, 0x03, + 0xFF, 0xF7, 0x80, 0xFC, 0x1F, 0xF0, 0x3F, 0x00, 0xFE, 0x07, 0xC0, 0x0F, + 0xC1, 0xF0, 0x01, 0xF0, 0x3C, 0x00, 0x3E, 0x0F, 0x80, 0x07, 0xC1, 0xE0, + 0x00, 0x78, 0x3C, 0x00, 0x1F, 0x0F, 0x80, 0x03, 0xC1, 0xE0, 0x00, 0x78, + 0x3C, 0x00, 0x0F, 0x07, 0x80, 0x01, 0xE0, 0xF0, 0x00, 0x7C, 0x1E, 0x00, + 0x0F, 0x03, 0xC0, 0x03, 0xE0, 0x78, 0x00, 0x7C, 0x0F, 0x80, 0x1F, 0x80, + 0xF8, 0x07, 0xF0, 0x1F, 0x83, 0xFC, 0x03, 0xFF, 0xFF, 0x80, 0x3F, 0xFE, + 0xF0, 0x03, 0xFF, 0x1E, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x80, + 0x01, 0xFF, 0xC0, 0x07, 0xFF, 0xE0, 0x3F, 0xFF, 0xC0, 0xFE, 0x0F, 0xC1, + 0xF0, 0x07, 0xC7, 0xC0, 0x0F, 0x8F, 0x00, 0x0F, 0x3C, 0x00, 0x1E, 0x78, + 0x00, 0x3D, 0xE0, 0x00, 0x7B, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xBF, 0xFF, 0xFF, 0x78, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, + 0x03, 0xC0, 0x00, 0x07, 0x80, 0x03, 0xCF, 0x80, 0x0F, 0x0F, 0x80, 0x3E, + 0x1F, 0x81, 0xF8, 0x1F, 0xFF, 0xE0, 0x1F, 0xFF, 0x80, 0x1F, 0xFC, 0x00, + 0x0F, 0xE0, 0x00, 0x00, 0x3E, 0x01, 0xFC, 0x07, 0xF8, 0x0F, 0xE0, 0x3E, + 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x07, 0xC0, 0x7F, 0xF0, 0xFF, 0xE3, + 0xFF, 0xC0, 0x78, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x3E, + 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x0F, 0x80, 0x1E, 0x00, + 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1E, + 0x00, 0x3C, 0x00, 0xF0, 0x01, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x07, + 0xFE, 0x3C, 0x01, 0xFF, 0xE7, 0x00, 0xFF, 0xFE, 0xE0, 0x1F, 0x83, 0xFC, + 0x07, 0xC0, 0x3F, 0x81, 0xF0, 0x03, 0xF0, 0x3C, 0x00, 0x7C, 0x0F, 0x00, + 0x0F, 0x81, 0xE0, 0x01, 0xF0, 0x78, 0x00, 0x3E, 0x0F, 0x00, 0x07, 0xC1, + 0xE0, 0x00, 0xF0, 0x38, 0x00, 0x1E, 0x0F, 0x00, 0x03, 0xC1, 0xE0, 0x00, + 0xF8, 0x3C, 0x00, 0x1F, 0x07, 0x80, 0x03, 0xC0, 0xF0, 0x00, 0xF8, 0x1E, + 0x00, 0x3F, 0x03, 0xE0, 0x07, 0xE0, 0x3E, 0x01, 0xF8, 0x07, 0xE0, 0xFF, + 0x00, 0x7F, 0xFD, 0xE0, 0x0F, 0xFF, 0x3C, 0x00, 0xFF, 0xCF, 0x00, 0x07, + 0xE1, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0F, 0x01, 0xE0, 0x03, 0xE0, + 0x3C, 0x00, 0xF8, 0x07, 0xE0, 0x7F, 0x00, 0x7F, 0xFF, 0xC0, 0x0F, 0xFF, + 0xF0, 0x00, 0x7F, 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x01, 0xE0, 0x00, + 0x03, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x78, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x83, 0xF0, 0x0F, + 0x1F, 0xF0, 0x1E, 0xFF, 0xF0, 0x3F, 0xFF, 0xE0, 0xFF, 0x87, 0xE1, 0xFC, + 0x07, 0xC3, 0xF0, 0x07, 0x87, 0xC0, 0x0F, 0x1F, 0x00, 0x1E, 0x3E, 0x00, + 0x3C, 0x78, 0x00, 0x78, 0xF0, 0x01, 0xE1, 0xE0, 0x03, 0xC7, 0xC0, 0x07, + 0x8F, 0x00, 0x0F, 0x1E, 0x00, 0x1E, 0x3C, 0x00, 0x78, 0x78, 0x00, 0xF1, + 0xE0, 0x01, 0xE3, 0xC0, 0x03, 0xC7, 0x80, 0x0F, 0x8F, 0x00, 0x1E, 0x1E, + 0x00, 0x3C, 0x78, 0x00, 0x78, 0xF0, 0x00, 0xF1, 0xE0, 0x03, 0xC0, 0x01, + 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF0, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0xC0, + 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, + 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x00, 0x00, 0x07, + 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x01, 0xE0, 0x00, + 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x3C, + 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, + 0x07, 0x80, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, + 0xC0, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, + 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x01, 0xF0, 0x00, + 0x78, 0x00, 0x3E, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x0F, 0xE0, 0x03, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x03, 0xC0, 0x0F, 0x81, 0xE0, 0x0F, 0x80, + 0xF0, 0x0F, 0x80, 0xF0, 0x1F, 0x00, 0x78, 0x1F, 0x00, 0x3C, 0x1F, 0x00, + 0x1E, 0x1F, 0x00, 0x1F, 0x1F, 0x00, 0x0F, 0x1E, 0x00, 0x07, 0xBF, 0x80, + 0x03, 0xFF, 0xC0, 0x01, 0xFD, 0xE0, 0x01, 0xFC, 0xF8, 0x00, 0xFC, 0x3C, + 0x00, 0x7C, 0x1F, 0x00, 0x3C, 0x07, 0x80, 0x1E, 0x03, 0xC0, 0x1F, 0x01, + 0xF0, 0x0F, 0x00, 0x78, 0x07, 0x80, 0x3E, 0x03, 0xC0, 0x0F, 0x01, 0xE0, + 0x07, 0x81, 0xE0, 0x03, 0xE0, 0xF0, 0x00, 0xF0, 0x78, 0x00, 0x7C, 0x00, + 0x01, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x3C, 0x07, + 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, + 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, + 0xF0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x00, 0x00, + 0x07, 0xE0, 0x1F, 0x80, 0xF9, 0xFF, 0x07, 0xFC, 0x0F, 0x3F, 0xF8, 0xFF, + 0xE0, 0xF7, 0xFF, 0x9F, 0xFF, 0x0F, 0xF0, 0xFF, 0xC3, 0xF0, 0xFC, 0x07, + 0xF8, 0x1F, 0x1F, 0x80, 0x3F, 0x00, 0xF1, 0xF0, 0x03, 0xE0, 0x0F, 0x1E, + 0x00, 0x3C, 0x00, 0xF1, 0xE0, 0x03, 0xC0, 0x0F, 0x1E, 0x00, 0x3C, 0x00, + 0xF1, 0xE0, 0x07, 0x80, 0x0F, 0x3C, 0x00, 0x78, 0x01, 0xF3, 0xC0, 0x07, + 0x80, 0x1E, 0x3C, 0x00, 0x78, 0x01, 0xE3, 0xC0, 0x0F, 0x80, 0x1E, 0x3C, + 0x00, 0xF0, 0x01, 0xE7, 0xC0, 0x0F, 0x00, 0x3C, 0x78, 0x00, 0xF0, 0x03, + 0xC7, 0x80, 0x0F, 0x00, 0x3C, 0x78, 0x01, 0xE0, 0x03, 0xC7, 0x80, 0x1E, + 0x00, 0x3C, 0xF8, 0x01, 0xE0, 0x07, 0x8F, 0x00, 0x1E, 0x00, 0x78, 0xF0, + 0x01, 0xE0, 0x07, 0x8F, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x07, 0xE0, 0x1F, + 0x3F, 0xF0, 0x3C, 0xFF, 0xF0, 0x7B, 0xFF, 0xE0, 0xFF, 0x07, 0xE1, 0xF8, + 0x07, 0xC7, 0xE0, 0x07, 0x8F, 0x80, 0x0F, 0x1F, 0x00, 0x1E, 0x3C, 0x00, + 0x3C, 0x78, 0x00, 0x78, 0xF0, 0x01, 0xE3, 0xC0, 0x03, 0xC7, 0x80, 0x07, + 0x8F, 0x00, 0x0F, 0x1E, 0x00, 0x3E, 0x3C, 0x00, 0x78, 0xF0, 0x00, 0xF1, + 0xE0, 0x01, 0xE3, 0xC0, 0x03, 0xC7, 0x80, 0x0F, 0x8F, 0x00, 0x1E, 0x3E, + 0x00, 0x3C, 0x78, 0x00, 0x78, 0xF0, 0x00, 0xF1, 0xE0, 0x03, 0xC0, 0x00, + 0x1F, 0x80, 0x01, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0x3F, 0xFF, 0xC0, 0xFE, + 0x0F, 0xC1, 0xF0, 0x0F, 0x87, 0xC0, 0x0F, 0x8F, 0x00, 0x0F, 0x3C, 0x00, + 0x1E, 0x78, 0x00, 0x3D, 0xE0, 0x00, 0x7B, 0xC0, 0x00, 0xF7, 0x80, 0x01, + 0xFE, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0x78, 0x00, 0x1E, 0xF0, 0x00, 0x3D, + 0xE0, 0x00, 0xF3, 0xC0, 0x01, 0xE7, 0x80, 0x07, 0x8F, 0x80, 0x1F, 0x0F, + 0x80, 0x7C, 0x1F, 0x83, 0xF8, 0x1F, 0xFF, 0xE0, 0x3F, 0xFF, 0x00, 0x1F, + 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x3C, 0x7F, 0xE0, + 0x07, 0xBF, 0xFE, 0x01, 0xFF, 0xFF, 0xC0, 0x3D, 0xE0, 0xFC, 0x07, 0xF0, + 0x0F, 0x80, 0xFC, 0x00, 0xF8, 0x1F, 0x00, 0x0F, 0x07, 0xC0, 0x01, 0xE0, + 0xF8, 0x00, 0x3C, 0x1F, 0x00, 0x07, 0x83, 0xC0, 0x00, 0xF0, 0x78, 0x00, + 0x1E, 0x1F, 0x00, 0x03, 0xC3, 0xC0, 0x00, 0xF0, 0x78, 0x00, 0x1E, 0x0F, + 0x00, 0x03, 0xC3, 0xE0, 0x00, 0xF8, 0x7C, 0x00, 0x1E, 0x0F, 0x80, 0x07, + 0xC1, 0xF8, 0x01, 0xF0, 0x3F, 0x80, 0x7C, 0x0F, 0xF8, 0x3F, 0x81, 0xEF, + 0xFF, 0xE0, 0x3C, 0xFF, 0xF8, 0x07, 0x8F, 0xFC, 0x00, 0xF0, 0xFE, 0x00, + 0x3E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x03, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x3F, + 0xF8, 0xF0, 0x1F, 0xFF, 0x3C, 0x0F, 0xFF, 0xDF, 0x07, 0xE0, 0xFF, 0x83, + 0xE0, 0x1F, 0xE1, 0xF0, 0x03, 0xF8, 0x78, 0x00, 0xFE, 0x3C, 0x00, 0x1F, + 0x8F, 0x00, 0x07, 0xC7, 0x80, 0x01, 0xF1, 0xE0, 0x00, 0x7C, 0x78, 0x00, + 0x1F, 0x3C, 0x00, 0x0F, 0x8F, 0x00, 0x03, 0xE3, 0xC0, 0x00, 0xF8, 0xF0, + 0x00, 0x3E, 0x3C, 0x00, 0x1F, 0x8F, 0x00, 0x0F, 0xC3, 0xC0, 0x03, 0xF0, + 0xF8, 0x01, 0xFC, 0x1F, 0x00, 0xFF, 0x07, 0xE0, 0xFF, 0xC0, 0xFF, 0xFD, + 0xE0, 0x1F, 0xFE, 0x78, 0x03, 0xFF, 0x3E, 0x00, 0x3F, 0x0F, 0x80, 0x00, + 0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1F, 0x00, + 0x00, 0x07, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x0F, 0x80, 0x00, 0x07, 0x87, 0xCF, 0xC3, 0xCF, 0xE1, 0xEF, + 0xE0, 0xFF, 0x80, 0x7F, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x0F, + 0x00, 0x07, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, + 0x78, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, + 0x03, 0xC0, 0x03, 0xE0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x00, + 0x00, 0x3F, 0x80, 0x07, 0xFF, 0x00, 0xFF, 0xFC, 0x0F, 0xFF, 0xE0, 0xFC, + 0x1F, 0x87, 0x80, 0x3C, 0x7C, 0x01, 0xE3, 0xC0, 0x0F, 0x1E, 0x00, 0x00, + 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0xE0, 0x03, 0xFF, + 0xC0, 0x07, 0xFF, 0x80, 0x07, 0xFE, 0x00, 0x03, 0xF0, 0x00, 0x07, 0xBC, + 0x00, 0x3D, 0xE0, 0x01, 0xEF, 0x00, 0x1F, 0x7C, 0x01, 0xF3, 0xF0, 0x1F, + 0x8F, 0xFF, 0xF8, 0x7F, 0xFF, 0x80, 0xFF, 0xF0, 0x01, 0xFE, 0x00, 0x03, + 0xC0, 0x1E, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC1, 0xFF, 0xEF, 0xFF, + 0x7F, 0xF0, 0x78, 0x03, 0xC0, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, + 0xC0, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3C, 0x01, 0xE0, + 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x3C, 0x01, 0xE0, 0x0F, 0xF0, 0x7F, 0x81, + 0xF8, 0x07, 0xC0, 0x0F, 0x00, 0x0F, 0x0F, 0x00, 0x1E, 0x0F, 0x00, 0x1E, + 0x1F, 0x00, 0x1E, 0x1E, 0x00, 0x1E, 0x1E, 0x00, 0x1E, 0x1E, 0x00, 0x3C, + 0x1E, 0x00, 0x3C, 0x3E, 0x00, 0x3C, 0x3C, 0x00, 0x3C, 0x3C, 0x00, 0x3C, + 0x3C, 0x00, 0x7C, 0x3C, 0x00, 0x78, 0x78, 0x00, 0x78, 0x78, 0x00, 0x78, + 0x78, 0x00, 0x78, 0x78, 0x00, 0xF8, 0x78, 0x00, 0xF0, 0xF0, 0x01, 0xF0, + 0xF0, 0x03, 0xF0, 0xF0, 0x07, 0xF0, 0xF8, 0x1F, 0xF0, 0xFF, 0xFF, 0xE0, + 0x7F, 0xFD, 0xE0, 0x3F, 0xF1, 0xE0, 0x1F, 0xC0, 0x00, 0xF0, 0x00, 0x7F, + 0xC0, 0x01, 0xEF, 0x00, 0x0F, 0xBC, 0x00, 0x3C, 0x78, 0x01, 0xE1, 0xE0, + 0x07, 0x87, 0x80, 0x3C, 0x1E, 0x01, 0xF0, 0x78, 0x07, 0x81, 0xE0, 0x3E, + 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x80, 0x38, 0x1E, 0x00, 0xF0, 0xF0, 0x03, + 0xC7, 0xC0, 0x0F, 0x1E, 0x00, 0x3C, 0xF0, 0x00, 0xF3, 0xC0, 0x03, 0xDE, + 0x00, 0x07, 0x78, 0x00, 0x1F, 0xC0, 0x00, 0x7E, 0x00, 0x01, 0xF8, 0x00, + 0x07, 0xC0, 0x00, 0x1F, 0x00, 0x00, 0xF0, 0x07, 0xC0, 0x0F, 0x78, 0x03, + 0xE0, 0x0F, 0xBC, 0x03, 0xF0, 0x07, 0x9E, 0x01, 0xF8, 0x03, 0xCF, 0x00, + 0xFC, 0x03, 0xC7, 0x80, 0xFE, 0x01, 0xE3, 0xC0, 0x77, 0x01, 0xE0, 0xE0, + 0x7B, 0x80, 0xF0, 0x70, 0x39, 0xC0, 0xF0, 0x38, 0x3C, 0xE0, 0x78, 0x1C, + 0x1E, 0x78, 0x78, 0x0F, 0x1E, 0x3C, 0x3C, 0x07, 0x8F, 0x1E, 0x3C, 0x03, + 0xC7, 0x0F, 0x1E, 0x01, 0xE7, 0x87, 0x9E, 0x00, 0xF3, 0x81, 0xCF, 0x00, + 0x7B, 0xC0, 0xEF, 0x00, 0x3D, 0xC0, 0x77, 0x80, 0x1F, 0xE0, 0x3F, 0x80, + 0x0F, 0xF0, 0x1F, 0xC0, 0x07, 0xF0, 0x0F, 0xC0, 0x01, 0xF8, 0x07, 0xE0, + 0x00, 0xF8, 0x03, 0xE0, 0x00, 0x7C, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0xF0, + 0x00, 0x00, 0x03, 0xC0, 0x07, 0xC0, 0xF8, 0x01, 0xE0, 0x1E, 0x00, 0xF0, + 0x07, 0x80, 0x78, 0x00, 0xF0, 0x3C, 0x00, 0x3C, 0x1F, 0x00, 0x0F, 0x8F, + 0x80, 0x01, 0xE7, 0xC0, 0x00, 0x7D, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x07, 0xF0, 0x00, 0x03, 0xFE, 0x00, 0x01, 0xF7, 0x80, 0x00, 0xF9, 0xF0, + 0x00, 0x3C, 0x3C, 0x00, 0x1E, 0x0F, 0x80, 0x0F, 0x01, 0xE0, 0x07, 0x80, + 0x7C, 0x03, 0xE0, 0x0F, 0x01, 0xF0, 0x03, 0xE0, 0xF8, 0x00, 0x78, 0x00, + 0x03, 0xC0, 0x01, 0xE0, 0x78, 0x00, 0x78, 0x0F, 0x00, 0x0F, 0x01, 0xE0, + 0x03, 0xC0, 0x3C, 0x00, 0x78, 0x07, 0xC0, 0x1E, 0x00, 0x78, 0x07, 0xC0, + 0x0F, 0x00, 0xF0, 0x01, 0xE0, 0x3C, 0x00, 0x3C, 0x07, 0x80, 0x07, 0x81, + 0xE0, 0x00, 0xF0, 0x3C, 0x00, 0x1E, 0x0F, 0x00, 0x03, 0xC1, 0xC0, 0x00, + 0x3C, 0x78, 0x00, 0x07, 0x9E, 0x00, 0x00, 0xF3, 0xC0, 0x00, 0x1E, 0xF0, + 0x00, 0x03, 0xDE, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x0F, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, + 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, + 0x00, 0x01, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x03, 0xE0, 0x00, 0x03, 0xE0, 0x00, 0x03, 0xE0, + 0x00, 0x03, 0xE0, 0x00, 0x03, 0xE0, 0x00, 0x03, 0xE0, 0x00, 0x03, 0xE0, + 0x00, 0x03, 0xC0, 0x00, 0x03, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xE0, 0xFF, + 0xFF, 0xF0, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x7E, 0x00, 0xFE, + 0x00, 0xF0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x07, 0xC0, 0x07, 0x80, 0x07, 0x80, 0x07, 0x80, + 0x07, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0xF8, 0x00, + 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, + 0x38, 0x00, 0x38, 0x00, 0x3C, 0x00, 0x7C, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x78, 0x00, 0x78, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xE0, 0x00, + 0xE0, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x7C, 0x00, 0x00, 0x70, + 0x07, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0xC0, + 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, 0x00, + 0x30, 0x07, 0x00, 0x70, 0x07, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0E, 0x00, + 0xE0, 0x0C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0x80, 0x38, 0x03, + 0x80, 0x38, 0x03, 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x60, 0x0E, + 0x00, 0xE0, 0x06, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x0F, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x1E, + 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x3E, 0x00, 0x3C, 0x00, 0x1C, + 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x0F, + 0x00, 0x07, 0x00, 0x1F, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x00, 0xF0, + 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x07, 0x80, 0x07, 0x80, 0x07, 0x80, + 0x0F, 0x00, 0x7F, 0x00, 0x7E, 0x00, 0xF8, 0x00, 0x0F, 0x00, 0x01, 0xFE, + 0x00, 0xCF, 0xFC, 0x0E, 0xE3, 0xF0, 0xE6, 0x07, 0xFF, 0x60, 0x0F, 0xF0, + 0x00, 0x1E, 0x00}; + +const GFXglyph FreeSansOblique24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 13, 0, 1}, // 0x20 ' ' + {0, 11, 34, 13, 6, -33}, // 0x21 '!' + {47, 13, 12, 17, 8, -32}, // 0x22 '"' + {67, 28, 34, 26, 3, -32}, // 0x23 '#' + {186, 26, 42, 26, 3, -35}, // 0x24 '$' + {323, 36, 34, 42, 6, -32}, // 0x25 '%' + {476, 26, 34, 31, 4, -32}, // 0x26 '&' + {587, 5, 12, 9, 8, -32}, // 0x27 ''' + {595, 15, 44, 16, 5, -33}, // 0x28 '(' + {678, 15, 44, 16, 1, -33}, // 0x29 ')' + {761, 14, 13, 18, 8, -33}, // 0x2A '*' + {784, 23, 22, 27, 5, -20}, // 0x2B '+' + {848, 7, 12, 13, 3, -4}, // 0x2C ',' + {859, 12, 4, 16, 5, -14}, // 0x2D '-' + {865, 6, 5, 13, 4, -4}, // 0x2E '.' + {869, 21, 35, 13, -1, -33}, // 0x2F '/' + {961, 23, 34, 26, 5, -32}, // 0x30 '0' + {1059, 13, 33, 26, 10, -32}, // 0x31 '1' + {1113, 27, 33, 26, 2, -32}, // 0x32 '2' + {1225, 25, 34, 26, 3, -32}, // 0x33 '3' + {1332, 24, 33, 26, 3, -32}, // 0x34 '4' + {1431, 27, 34, 26, 3, -32}, // 0x35 '5' + {1546, 24, 34, 26, 4, -32}, // 0x36 '6' + {1648, 26, 33, 26, 6, -32}, // 0x37 '7' + {1756, 25, 34, 26, 3, -32}, // 0x38 '8' + {1863, 24, 34, 26, 4, -32}, // 0x39 '9' + {1965, 10, 25, 13, 5, -24}, // 0x3A ':' + {1997, 11, 32, 13, 4, -24}, // 0x3B ';' + {2041, 26, 23, 27, 4, -22}, // 0x3C '<' + {2116, 26, 12, 27, 3, -16}, // 0x3D '=' + {2155, 26, 23, 27, 2, -21}, // 0x3E '>' + {2230, 20, 35, 26, 9, -34}, // 0x3F '?' + {2318, 45, 42, 48, 4, -34}, // 0x40 '@' + {2555, 30, 34, 31, 1, -33}, // 0x41 'A' + {2683, 29, 34, 31, 4, -33}, // 0x42 'B' + {2807, 30, 36, 33, 5, -34}, // 0x43 'C' + {2942, 31, 34, 33, 4, -33}, // 0x44 'D' + {3074, 31, 34, 31, 4, -33}, // 0x45 'E' + {3206, 30, 34, 28, 4, -33}, // 0x46 'F' + {3334, 33, 36, 37, 5, -34}, // 0x47 'G' + {3483, 33, 34, 34, 4, -33}, // 0x48 'H' + {3624, 11, 34, 13, 5, -33}, // 0x49 'I' + {3671, 25, 35, 24, 2, -33}, // 0x4A 'J' + {3781, 34, 34, 31, 4, -33}, // 0x4B 'K' + {3926, 22, 34, 26, 4, -33}, // 0x4C 'L' + {4020, 39, 34, 40, 4, -33}, // 0x4D 'M' + {4186, 34, 34, 34, 4, -33}, // 0x4E 'N' + {4331, 33, 36, 36, 5, -34}, // 0x4F 'O' + {4480, 29, 34, 30, 4, -33}, // 0x50 'P' + {4604, 33, 38, 36, 5, -34}, // 0x51 'Q' + {4761, 30, 34, 33, 4, -33}, // 0x52 'R' + {4889, 29, 36, 31, 4, -34}, // 0x53 'S' + {5020, 28, 34, 29, 7, -33}, // 0x54 'T' + {5139, 31, 35, 34, 6, -33}, // 0x55 'U' + {5275, 29, 34, 30, 8, -33}, // 0x56 'V' + {5399, 43, 34, 44, 8, -33}, // 0x57 'W' + {5582, 36, 34, 31, 1, -33}, // 0x58 'X' + {5735, 30, 34, 32, 8, -33}, // 0x59 'Y' + {5863, 34, 34, 29, 1, -33}, // 0x5A 'Z' + {6008, 18, 44, 13, 1, -33}, // 0x5B '[' + {6107, 6, 35, 13, 7, -33}, // 0x5C '\' + {6134, 18, 44, 13, -1, -33}, // 0x5D ']' + {6233, 17, 18, 22, 6, -32}, // 0x5E '^' + {6272, 29, 2, 26, -3, 7}, // 0x5F '_' + {6280, 8, 7, 16, 8, -34}, // 0x60 '`' + {6287, 23, 27, 26, 3, -25}, // 0x61 'a' + {6365, 25, 35, 26, 3, -33}, // 0x62 'b' + {6475, 22, 27, 24, 4, -25}, // 0x63 'c' + {6550, 27, 35, 26, 4, -33}, // 0x64 'd' + {6669, 23, 27, 26, 4, -25}, // 0x65 'e' + {6747, 15, 34, 12, 3, -33}, // 0x66 'f' + {6811, 27, 36, 26, 2, -25}, // 0x67 'g' + {6933, 23, 34, 25, 3, -33}, // 0x68 'h' + {7031, 11, 34, 10, 3, -33}, // 0x69 'i' + {7078, 18, 44, 11, -2, -33}, // 0x6A 'j' + {7177, 25, 34, 24, 3, -33}, // 0x6B 'k' + {7284, 11, 34, 10, 3, -33}, // 0x6C 'l' + {7331, 36, 26, 38, 3, -25}, // 0x6D 'm' + {7448, 23, 26, 25, 3, -25}, // 0x6E 'n' + {7523, 23, 27, 26, 4, -25}, // 0x6F 'o' + {7601, 27, 36, 26, 1, -25}, // 0x70 'p' + {7723, 26, 36, 26, 3, -25}, // 0x71 'q' + {7840, 17, 26, 15, 3, -25}, // 0x72 'r' + {7896, 21, 27, 24, 3, -25}, // 0x73 's' + {7967, 13, 32, 12, 4, -30}, // 0x74 't' + {8019, 24, 26, 25, 4, -24}, // 0x75 'u' + {8097, 22, 25, 23, 6, -24}, // 0x76 'v' + {8166, 33, 25, 34, 6, -24}, // 0x77 'w' + {8270, 26, 25, 23, 1, -24}, // 0x78 'x' + {8352, 27, 35, 23, 0, -24}, // 0x79 'y' + {8471, 25, 25, 23, 1, -24}, // 0x7A 'z' + {8550, 16, 44, 16, 5, -33}, // 0x7B '{' + {8638, 12, 44, 12, 3, -33}, // 0x7C '|' + {8704, 16, 44, 16, -1, -33}, // 0x7D '}' + {8792, 21, 7, 27, 6, -19}}; // 0x7E '~' + +const GFXfont FreeSansOblique24pt7b PROGMEM = { + (uint8_t *)FreeSansOblique24pt7bBitmaps, + (GFXglyph *)FreeSansOblique24pt7bGlyphs, 0x20, 0x7E, 56}; + +// Approx. 9483 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique9pt7b.h new file mode 100644 index 0000000..a44f373 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSansOblique9pt7b.h @@ -0,0 +1,219 @@ +const uint8_t FreeSansOblique9pt7bBitmaps[] PROGMEM = { + 0x10, 0x84, 0x22, 0x10, 0x84, 0x42, 0x10, 0x08, 0x00, 0xDE, 0xE5, 0x20, + 0x06, 0x40, 0x88, 0x13, 0x06, 0x43, 0xFE, 0x32, 0x04, 0x40, 0x98, 0x32, + 0x1F, 0xF0, 0x98, 0x22, 0x04, 0xC0, 0x02, 0x01, 0xF8, 0x6B, 0x99, 0x33, + 0x40, 0x68, 0x0F, 0x00, 0xF8, 0x07, 0xC1, 0x1B, 0x23, 0x64, 0x4E, 0x98, + 0xFC, 0x04, 0x00, 0x80, 0x3C, 0x08, 0xCC, 0x23, 0x18, 0x86, 0x32, 0x0C, + 0x64, 0x19, 0x90, 0x1E, 0x40, 0x01, 0x1E, 0x02, 0x66, 0x09, 0x8C, 0x23, + 0x18, 0x86, 0x62, 0x07, 0x80, 0x0F, 0x06, 0x63, 0x18, 0xC6, 0x3F, 0x07, + 0x03, 0xC1, 0xB3, 0xC7, 0xB0, 0xCC, 0x33, 0x3E, 0x79, 0x80, 0xFA, 0x04, + 0x10, 0x60, 0x83, 0x04, 0x18, 0x30, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x10, + 0x30, 0x20, 0x08, 0x18, 0x10, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x18, 0x30, + 0x41, 0x82, 0x0C, 0x10, 0x40, 0x19, 0x73, 0x16, 0x48, 0x04, 0x04, 0x02, + 0x1F, 0xF0, 0x80, 0x80, 0x40, 0x20, 0x6D, 0x28, 0xF0, 0xC0, 0x01, 0x02, + 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x0F, + 0x19, 0xC8, 0x6C, 0x36, 0x1A, 0x0F, 0x05, 0x86, 0xC3, 0x61, 0xB1, 0x9C, + 0x87, 0x80, 0x08, 0xCD, 0xE3, 0x18, 0xC4, 0x23, 0x18, 0xC4, 0x00, 0x07, + 0x83, 0x1C, 0x41, 0x98, 0x30, 0x06, 0x01, 0x80, 0x60, 0x38, 0x1C, 0x06, + 0x01, 0x80, 0x20, 0x0F, 0xF8, 0x0F, 0x86, 0x73, 0x0C, 0x83, 0x00, 0xC0, + 0x60, 0xE0, 0x06, 0x01, 0xB0, 0x6C, 0x13, 0x8C, 0x7C, 0x00, 0x00, 0x80, + 0xC0, 0xE0, 0xA0, 0x90, 0x98, 0x8C, 0x86, 0xFF, 0x81, 0x01, 0x80, 0xC0, + 0x60, 0x0F, 0xC3, 0x00, 0x40, 0x08, 0x03, 0x00, 0x7F, 0x1C, 0x70, 0x06, + 0x00, 0xC0, 0x1B, 0x06, 0x71, 0x87, 0xE0, 0x0F, 0x86, 0x73, 0x0D, 0x80, + 0x60, 0x1F, 0xCF, 0x3B, 0x86, 0xC1, 0xB0, 0x6C, 0x33, 0x98, 0x3C, 0x00, + 0x7F, 0xC0, 0x20, 0x10, 0x0C, 0x06, 0x01, 0x00, 0x80, 0x60, 0x10, 0x0C, + 0x02, 0x01, 0x80, 0x40, 0x00, 0x0F, 0x86, 0x73, 0x0C, 0xC3, 0x30, 0xCC, + 0x61, 0xE1, 0x86, 0x41, 0xB0, 0x6C, 0x13, 0x8C, 0x3E, 0x00, 0x0F, 0x06, + 0x73, 0x0D, 0x83, 0x60, 0xD8, 0x77, 0x3C, 0xFE, 0x01, 0x80, 0x6C, 0x33, + 0x98, 0x7C, 0x00, 0x30, 0x00, 0x00, 0x00, 0xC0, 0x18, 0x00, 0x00, 0x00, + 0x0C, 0x62, 0x11, 0x00, 0x00, 0x01, 0xC3, 0x8F, 0x0C, 0x07, 0x00, 0xE0, + 0x1E, 0x01, 0x00, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x40, 0x3C, 0x03, 0x80, + 0x70, 0x18, 0x78, 0xE1, 0xC0, 0x00, 0x00, 0x1F, 0x30, 0xD0, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xFE, + 0x00, 0xC0, 0xE0, 0xC0, 0x18, 0x61, 0xD3, 0x31, 0x9C, 0xD8, 0xC2, 0x36, + 0x31, 0x8F, 0x18, 0x67, 0xC6, 0x11, 0xB1, 0x8C, 0xCC, 0x67, 0x63, 0x0E, + 0xF0, 0x60, 0x00, 0x1C, 0x00, 0x01, 0x81, 0x00, 0x1F, 0xC0, 0x01, 0xC0, + 0x1C, 0x03, 0xC0, 0x24, 0x06, 0x60, 0x46, 0x0C, 0x61, 0x86, 0x1F, 0xE3, + 0x06, 0x20, 0x26, 0x03, 0x40, 0x30, 0x1F, 0xE1, 0x87, 0x30, 0x33, 0x03, + 0x30, 0x23, 0x06, 0x3F, 0xC6, 0x06, 0x60, 0x66, 0x06, 0x60, 0x66, 0x0C, + 0x7F, 0x80, 0x07, 0xC1, 0x86, 0x30, 0x32, 0x03, 0x60, 0x04, 0x00, 0xC0, + 0x0C, 0x00, 0xC0, 0x6C, 0x06, 0xC0, 0xC6, 0x18, 0x3E, 0x00, 0x1F, 0xE0, + 0xC1, 0x84, 0x06, 0x60, 0x33, 0x01, 0x98, 0x0C, 0x80, 0x64, 0x02, 0x60, + 0x33, 0x01, 0x98, 0x18, 0x81, 0x87, 0xF0, 0x00, 0x1F, 0xF1, 0x80, 0x10, + 0x03, 0x00, 0x30, 0x03, 0x00, 0x3F, 0xE2, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x04, 0x00, 0x7F, 0xC0, 0x1F, 0xF1, 0x80, 0x10, 0x03, 0x00, 0x30, 0x03, + 0x00, 0x3F, 0xC2, 0x00, 0x60, 0x06, 0x00, 0x60, 0x04, 0x00, 0x40, 0x00, + 0x07, 0xE0, 0xE1, 0x8C, 0x06, 0xC0, 0x36, 0x00, 0x60, 0x03, 0x07, 0xF8, + 0x02, 0xC0, 0x36, 0x01, 0x98, 0x1C, 0xE1, 0xC1, 0xF2, 0x00, 0x18, 0x08, + 0xC0, 0xC4, 0x06, 0x60, 0x33, 0x01, 0x18, 0x18, 0xFF, 0xC4, 0x06, 0x60, + 0x23, 0x01, 0x18, 0x18, 0x80, 0xC4, 0x06, 0x00, 0x33, 0x32, 0x26, 0x66, + 0x44, 0xCC, 0xC0, 0x00, 0xC0, 0x60, 0x18, 0x06, 0x01, 0x80, 0x60, 0x30, + 0x0C, 0x03, 0x30, 0xCC, 0x63, 0x18, 0x7C, 0x00, 0x18, 0x18, 0x60, 0xC1, + 0x0E, 0x0C, 0x60, 0x33, 0x00, 0xD8, 0x03, 0xF0, 0x0C, 0xC0, 0x61, 0x81, + 0x86, 0x06, 0x0C, 0x10, 0x30, 0x40, 0x60, 0x18, 0x0C, 0x04, 0x06, 0x03, + 0x01, 0x80, 0xC0, 0x40, 0x60, 0x30, 0x18, 0x08, 0x07, 0xF8, 0x18, 0x06, + 0x18, 0x0E, 0x18, 0x0E, 0x34, 0x1E, 0x34, 0x36, 0x34, 0x34, 0x24, 0x64, + 0x24, 0x6C, 0x64, 0xCC, 0x64, 0x8C, 0x65, 0x88, 0x43, 0x08, 0x43, 0x18, + 0x18, 0x08, 0xE0, 0x47, 0x06, 0x6C, 0x33, 0x61, 0x99, 0x08, 0x8C, 0xC4, + 0x66, 0x61, 0xB3, 0x0D, 0x18, 0x38, 0x81, 0xC4, 0x06, 0x00, 0x07, 0xC0, + 0xC3, 0x8C, 0x0E, 0xC0, 0x36, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, + 0x36, 0x01, 0xB8, 0x18, 0xE1, 0x81, 0xF0, 0x00, 0x1F, 0xE1, 0x83, 0x10, + 0x33, 0x03, 0x30, 0x33, 0x06, 0x3F, 0xC2, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x04, 0x00, 0x40, 0x00, 0x07, 0xC0, 0xC3, 0x8C, 0x0E, 0xC0, 0x36, 0x01, + 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x36, 0x09, 0xB8, 0x78, 0xE3, 0x81, + 0xF6, 0x00, 0x10, 0x1F, 0xF0, 0xC0, 0xC4, 0x06, 0x60, 0x33, 0x01, 0x18, + 0x18, 0xFF, 0x04, 0x0C, 0x60, 0x63, 0x03, 0x18, 0x18, 0x80, 0xC4, 0x06, + 0x00, 0x07, 0xC1, 0x87, 0x30, 0x33, 0x03, 0x30, 0x03, 0xC0, 0x0F, 0xC0, + 0x1E, 0x00, 0x6C, 0x06, 0xC0, 0x46, 0x0C, 0x3F, 0x00, 0xFF, 0xC3, 0x00, + 0xC0, 0x20, 0x18, 0x06, 0x01, 0x80, 0x60, 0x10, 0x0C, 0x03, 0x00, 0xC0, + 0x20, 0x00, 0x30, 0x13, 0x03, 0x20, 0x36, 0x03, 0x60, 0x26, 0x06, 0x60, + 0x64, 0x06, 0xC0, 0x6C, 0x04, 0xC0, 0xCE, 0x18, 0x3E, 0x00, 0xC0, 0x78, + 0x0B, 0x03, 0x20, 0xC4, 0x18, 0xC6, 0x18, 0x83, 0x30, 0x64, 0x0D, 0x80, + 0xA0, 0x1C, 0x03, 0x00, 0xC1, 0x83, 0xC1, 0x83, 0xC3, 0x86, 0xC2, 0x86, + 0xC6, 0x84, 0xC4, 0x8C, 0xCC, 0xC8, 0xC8, 0xD8, 0xD8, 0xD0, 0xD0, 0xF0, + 0x70, 0xE0, 0x60, 0xE0, 0x60, 0xE0, 0x0C, 0x0C, 0x30, 0x60, 0x63, 0x01, + 0x98, 0x02, 0xC0, 0x0E, 0x00, 0x38, 0x01, 0xE0, 0x0C, 0x80, 0x33, 0x01, + 0x8C, 0x0C, 0x18, 0x60, 0x60, 0xC0, 0x66, 0x0C, 0x60, 0xC2, 0x18, 0x33, + 0x03, 0x60, 0x1C, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x00, 0x30, + 0x00, 0x1F, 0xF0, 0x07, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, + 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x00, 0xFF, 0xC0, 0x0E, 0x10, 0x20, + 0x41, 0x02, 0x04, 0x08, 0x20, 0x40, 0x81, 0x04, 0x08, 0x10, 0x20, 0xE0, + 0xAA, 0xA9, 0x55, 0x40, 0x0E, 0x08, 0x10, 0x20, 0x41, 0x02, 0x04, 0x08, + 0x20, 0x40, 0x81, 0x04, 0x08, 0x10, 0xE0, 0x0C, 0x18, 0x51, 0xA2, 0x4C, + 0x50, 0x80, 0xFF, 0xE0, 0xC8, 0x80, 0x0F, 0x86, 0x33, 0x0C, 0x03, 0x03, + 0xDF, 0xEE, 0x0B, 0x02, 0xC1, 0x9F, 0xE0, 0x10, 0x04, 0x01, 0x00, 0xDC, + 0x39, 0x88, 0x32, 0x0D, 0x83, 0x40, 0xD0, 0x64, 0x1B, 0x8C, 0xBC, 0x00, + 0x1F, 0x18, 0xD8, 0x6C, 0x0C, 0x06, 0x03, 0x01, 0x86, 0x66, 0x3E, 0x00, + 0x00, 0x20, 0x08, 0x01, 0x0F, 0x23, 0x14, 0xC1, 0x18, 0x26, 0x04, 0xC0, + 0x98, 0x23, 0x04, 0x71, 0x87, 0xD0, 0x0F, 0x0C, 0x76, 0x0D, 0x83, 0xFF, + 0xF0, 0x0C, 0x03, 0x06, 0x63, 0x0F, 0x80, 0x1C, 0xC2, 0x1E, 0x20, 0x84, + 0x10, 0x41, 0x04, 0x20, 0x80, 0x0F, 0x46, 0x33, 0x0C, 0xC1, 0x60, 0xD8, + 0x26, 0x09, 0x86, 0x71, 0x8F, 0xE0, 0x10, 0x04, 0xC2, 0x1F, 0x00, 0x10, + 0x04, 0x01, 0x00, 0x9F, 0x39, 0x88, 0x22, 0x09, 0x02, 0x40, 0x90, 0x44, + 0x12, 0x04, 0x81, 0x00, 0x10, 0x02, 0x22, 0x64, 0x44, 0x48, 0x80, 0x04, + 0x00, 0x01, 0x08, 0x20, 0x82, 0x08, 0x41, 0x04, 0x10, 0x42, 0x08, 0xE0, + 0x10, 0x08, 0x04, 0x04, 0x32, 0x31, 0x20, 0xA0, 0xB8, 0x6C, 0x22, 0x11, + 0x90, 0xC8, 0x30, 0x11, 0x22, 0x22, 0x64, 0x44, 0x48, 0x80, 0x2F, 0x3C, + 0x63, 0x8C, 0x86, 0x19, 0x08, 0x44, 0x10, 0x88, 0x21, 0x10, 0x82, 0x21, + 0x04, 0x82, 0x11, 0x04, 0x20, 0x00, 0x0B, 0xF3, 0x18, 0x82, 0x20, 0x90, + 0x24, 0x09, 0x04, 0x41, 0x20, 0x48, 0x10, 0x0F, 0x0C, 0x76, 0x0D, 0x83, + 0xC0, 0xF0, 0x3C, 0x1B, 0x06, 0xE3, 0x0F, 0x00, 0x17, 0xC3, 0x1C, 0x41, + 0x98, 0x32, 0x06, 0x40, 0xC8, 0x33, 0x06, 0x71, 0x8B, 0xC1, 0x00, 0x20, + 0x08, 0x01, 0x00, 0x00, 0x1E, 0xCC, 0x66, 0x09, 0x82, 0xC0, 0xB0, 0x4C, + 0x13, 0x04, 0x63, 0x0F, 0xC0, 0x20, 0x08, 0x02, 0x00, 0x80, 0x2C, 0x60, + 0x81, 0x04, 0x08, 0x10, 0x20, 0x81, 0x00, 0x1E, 0x33, 0x63, 0x60, 0x70, + 0x1E, 0x03, 0xC3, 0xC6, 0x7C, 0x22, 0xF2, 0x44, 0x44, 0xCC, 0xCE, 0x21, + 0x20, 0x90, 0x48, 0x24, 0x12, 0x13, 0x09, 0x84, 0xE6, 0x3E, 0x00, 0xC1, + 0xE1, 0xB0, 0xC8, 0xC4, 0x43, 0x61, 0xA0, 0xF0, 0x70, 0x18, 0x00, 0xC7, + 0x1E, 0x38, 0xB3, 0xCD, 0x96, 0x4C, 0xB6, 0x6D, 0xB1, 0x4D, 0x0E, 0x78, + 0x63, 0x83, 0x1C, 0x00, 0x10, 0xC3, 0x10, 0x24, 0x07, 0x80, 0xE0, 0x1C, + 0x07, 0x81, 0x90, 0x23, 0x08, 0x20, 0x30, 0x46, 0x18, 0x42, 0x08, 0xC1, + 0x10, 0x24, 0x07, 0x80, 0xE0, 0x1C, 0x03, 0x00, 0x60, 0x08, 0x03, 0x01, + 0xC0, 0x00, 0x3F, 0x80, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0x7F, 0x00, 0x18, 0x88, 0x42, 0x10, 0x88, 0xC3, 0x18, 0x88, 0x42, 0x18, + 0xE0, 0x11, 0x22, 0x22, 0x24, 0x44, 0x4C, 0x88, 0x88, 0x00, 0x38, 0xC2, + 0x10, 0x88, 0xC6, 0x18, 0x88, 0x42, 0x10, 0x88, 0xC0, 0x70, 0x4E, 0x41, + 0xC0}; + +const GFXglyph FreeSansOblique9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 5, 13, 5, 2, -12}, // 0x21 '!' + {9, 5, 4, 6, 3, -12}, // 0x22 '"' + {12, 11, 13, 10, 1, -12}, // 0x23 '#' + {30, 11, 16, 10, 1, -13}, // 0x24 '$' + {52, 15, 13, 16, 2, -12}, // 0x25 '%' + {77, 10, 13, 12, 2, -12}, // 0x26 '&' + {94, 2, 4, 3, 3, -12}, // 0x27 ''' + {95, 7, 17, 6, 2, -12}, // 0x28 '(' + {110, 7, 17, 6, -1, -12}, // 0x29 ')' + {125, 6, 5, 7, 3, -12}, // 0x2A '*' + {129, 9, 8, 11, 2, -7}, // 0x2B '+' + {138, 3, 5, 5, 1, -1}, // 0x2C ',' + {140, 4, 1, 6, 2, -4}, // 0x2D '-' + {141, 2, 1, 5, 2, 0}, // 0x2E '.' + {142, 8, 13, 5, 0, -12}, // 0x2F '/' + {155, 9, 13, 10, 2, -12}, // 0x30 '0' + {170, 5, 13, 10, 4, -12}, // 0x31 '1' + {179, 11, 13, 10, 1, -12}, // 0x32 '2' + {197, 10, 13, 10, 1, -12}, // 0x33 '3' + {214, 9, 13, 10, 1, -12}, // 0x34 '4' + {229, 11, 13, 10, 1, -12}, // 0x35 '5' + {247, 10, 13, 10, 2, -12}, // 0x36 '6' + {264, 10, 13, 10, 2, -12}, // 0x37 '7' + {281, 10, 13, 10, 1, -12}, // 0x38 '8' + {298, 10, 13, 10, 1, -12}, // 0x39 '9' + {315, 4, 9, 5, 2, -8}, // 0x3A ':' + {320, 5, 12, 5, 1, -8}, // 0x3B ';' + {328, 9, 9, 11, 2, -8}, // 0x3C '<' + {339, 10, 4, 11, 1, -5}, // 0x3D '=' + {344, 9, 9, 11, 1, -7}, // 0x3E '>' + {355, 9, 13, 10, 3, -12}, // 0x3F '?' + {370, 18, 16, 18, 1, -12}, // 0x40 '@' + {406, 12, 13, 12, 0, -12}, // 0x41 'A' + {426, 12, 13, 12, 1, -12}, // 0x42 'B' + {446, 12, 13, 13, 2, -12}, // 0x43 'C' + {466, 13, 13, 13, 1, -12}, // 0x44 'D' + {488, 12, 13, 12, 1, -12}, // 0x45 'E' + {508, 12, 13, 11, 1, -12}, // 0x46 'F' + {528, 13, 13, 14, 2, -12}, // 0x47 'G' + {550, 13, 13, 13, 1, -12}, // 0x48 'H' + {572, 4, 13, 5, 2, -12}, // 0x49 'I' + {579, 10, 13, 9, 1, -12}, // 0x4A 'J' + {596, 14, 13, 12, 1, -12}, // 0x4B 'K' + {619, 9, 13, 10, 1, -12}, // 0x4C 'L' + {634, 16, 13, 15, 1, -12}, // 0x4D 'M' + {660, 13, 13, 13, 1, -12}, // 0x4E 'N' + {682, 13, 13, 14, 2, -12}, // 0x4F 'O' + {704, 12, 13, 12, 1, -12}, // 0x50 'P' + {724, 13, 14, 14, 2, -12}, // 0x51 'Q' + {747, 13, 13, 13, 1, -12}, // 0x52 'R' + {769, 12, 13, 12, 1, -12}, // 0x53 'S' + {789, 10, 13, 11, 3, -12}, // 0x54 'T' + {806, 12, 13, 13, 2, -12}, // 0x55 'U' + {826, 11, 13, 12, 3, -12}, // 0x56 'V' + {844, 16, 13, 17, 3, -12}, // 0x57 'W' + {870, 14, 13, 12, 0, -12}, // 0x58 'X' + {893, 12, 13, 12, 3, -12}, // 0x59 'Y' + {913, 12, 13, 11, 1, -12}, // 0x5A 'Z' + {933, 7, 17, 5, 0, -12}, // 0x5B '[' + {948, 2, 13, 5, 3, -12}, // 0x5C '\' + {952, 7, 17, 5, 0, -12}, // 0x5D ']' + {967, 7, 7, 8, 2, -12}, // 0x5E '^' + {974, 11, 1, 10, -1, 3}, // 0x5F '_' + {976, 3, 3, 6, 3, -12}, // 0x60 '`' + {978, 10, 10, 10, 1, -9}, // 0x61 'a' + {991, 10, 13, 10, 1, -12}, // 0x62 'b' + {1008, 9, 10, 9, 1, -9}, // 0x63 'c' + {1020, 11, 13, 10, 1, -12}, // 0x64 'd' + {1038, 10, 10, 10, 1, -9}, // 0x65 'e' + {1051, 6, 13, 5, 1, -12}, // 0x66 'f' + {1061, 10, 14, 10, 0, -9}, // 0x67 'g' + {1079, 10, 13, 10, 1, -12}, // 0x68 'h' + {1096, 4, 13, 4, 1, -12}, // 0x69 'i' + {1103, 6, 17, 4, -1, -12}, // 0x6A 'j' + {1116, 9, 13, 9, 1, -12}, // 0x6B 'k' + {1131, 4, 13, 4, 1, -12}, // 0x6C 'l' + {1138, 15, 10, 15, 1, -9}, // 0x6D 'm' + {1157, 10, 11, 10, 1, -10}, // 0x6E 'n' + {1171, 10, 10, 10, 1, -9}, // 0x6F 'o' + {1184, 11, 14, 10, 0, -9}, // 0x70 'p' + {1204, 10, 14, 10, 1, -9}, // 0x71 'q' + {1222, 7, 10, 6, 1, -9}, // 0x72 'r' + {1231, 8, 10, 9, 1, -9}, // 0x73 's' + {1241, 4, 12, 5, 2, -11}, // 0x74 't' + {1247, 9, 10, 10, 2, -9}, // 0x75 'u' + {1259, 9, 10, 9, 2, -9}, // 0x76 'v' + {1271, 13, 10, 13, 2, -9}, // 0x77 'w' + {1288, 11, 10, 9, 0, -9}, // 0x78 'x' + {1302, 11, 14, 9, 0, -9}, // 0x79 'y' + {1322, 9, 10, 9, 1, -9}, // 0x7A 'z' + {1334, 5, 17, 6, 2, -12}, // 0x7B '{' + {1345, 4, 17, 5, 1, -12}, // 0x7C '|' + {1354, 5, 17, 6, 0, -12}, // 0x7D '}' + {1365, 9, 3, 11, 2, -7}}; // 0x7E '~' + +const GFXfont FreeSansOblique9pt7b PROGMEM = { + (uint8_t *)FreeSansOblique9pt7bBitmaps, + (GFXglyph *)FreeSansOblique9pt7bGlyphs, 0x20, 0x7E, 22}; + +// Approx. 2041 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerif12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif12pt7b.h new file mode 100644 index 0000000..052857a --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif12pt7b.h @@ -0,0 +1,258 @@ +const uint8_t FreeSerif12pt7bBitmaps[] PROGMEM = { + 0xFF, 0xFE, 0xA8, 0x3F, 0xCF, 0x3C, 0xF3, 0x8A, 0x20, 0x0C, 0x40, 0xC4, + 0x08, 0x40, 0x8C, 0x08, 0xC7, 0xFF, 0x18, 0x81, 0x88, 0x10, 0x81, 0x08, + 0xFF, 0xE1, 0x18, 0x31, 0x03, 0x10, 0x31, 0x02, 0x10, 0x04, 0x07, 0xC6, + 0x5B, 0x12, 0xC4, 0xB1, 0x0F, 0x41, 0xF0, 0x1E, 0x01, 0xE0, 0x58, 0x13, + 0x84, 0xE1, 0x3C, 0x4F, 0x96, 0x3F, 0x01, 0x00, 0x00, 0x04, 0x03, 0x83, + 0x03, 0x9F, 0x81, 0xC2, 0x20, 0x60, 0x90, 0x38, 0x24, 0x0C, 0x12, 0x03, + 0x0D, 0x00, 0xC6, 0x47, 0x9E, 0x23, 0x10, 0x09, 0x84, 0x04, 0xE1, 0x03, + 0x30, 0x40, 0x8C, 0x20, 0x43, 0x08, 0x10, 0xC4, 0x08, 0x1E, 0x00, 0x03, + 0xC0, 0x02, 0x30, 0x03, 0x08, 0x01, 0x84, 0x00, 0xC4, 0x00, 0x7C, 0xF8, + 0x1C, 0x38, 0x1E, 0x08, 0x33, 0x0C, 0x31, 0xC4, 0x10, 0x74, 0x18, 0x3A, + 0x0C, 0x0E, 0x07, 0x03, 0x83, 0xC3, 0xE2, 0x7E, 0x3E, 0xFF, 0xA0, 0x04, + 0x21, 0x08, 0x61, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC1, 0x04, 0x18, 0x20, + 0x40, 0x81, 0x81, 0x02, 0x04, 0x18, 0x20, 0x83, 0x0C, 0x30, 0xC3, 0x0C, + 0x30, 0x86, 0x10, 0x84, 0x20, 0x30, 0xB3, 0xD7, 0x54, 0x38, 0x7C, 0xD3, + 0x30, 0x30, 0x10, 0x04, 0x00, 0x80, 0x10, 0x02, 0x00, 0x41, 0xFF, 0xC1, + 0x00, 0x20, 0x04, 0x00, 0x80, 0x10, 0x00, 0xDF, 0x95, 0x00, 0xFC, 0xFC, + 0x06, 0x0C, 0x10, 0x60, 0xC1, 0x06, 0x0C, 0x10, 0x60, 0xC1, 0x06, 0x0C, + 0x10, 0x60, 0xC0, 0x1E, 0x0C, 0xC6, 0x19, 0x86, 0xC0, 0xB0, 0x3C, 0x0F, + 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, + 0x00, 0x11, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, + 0x0C, 0xFC, 0x1E, 0x18, 0xC4, 0x1A, 0x06, 0x01, 0x80, 0x60, 0x10, 0x0C, + 0x02, 0x01, 0x00, 0xC0, 0x60, 0x30, 0x18, 0x1F, 0xF8, 0x1E, 0x18, 0xE8, + 0x18, 0x06, 0x01, 0x00, 0x80, 0xF0, 0x7E, 0x03, 0xC0, 0x70, 0x0C, 0x03, + 0x00, 0xC0, 0x6E, 0x11, 0xF8, 0x01, 0x00, 0xC0, 0x70, 0x2C, 0x0B, 0x04, + 0xC2, 0x30, 0x8C, 0x43, 0x20, 0xC8, 0x33, 0xFF, 0x03, 0x00, 0xC0, 0x30, + 0x0C, 0x00, 0x03, 0xF1, 0x00, 0x40, 0x18, 0x0F, 0x80, 0xF8, 0x0E, 0x01, + 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x20, 0x1B, 0x8C, 0x7C, 0x00, 0x01, + 0xC3, 0xC1, 0xC0, 0xC0, 0x70, 0x18, 0x0E, 0xF3, 0xCE, 0xC1, 0xF0, 0x3C, + 0x0F, 0x03, 0xC0, 0xD8, 0x36, 0x08, 0xC6, 0x1E, 0x00, 0x3F, 0xD0, 0x38, + 0x08, 0x06, 0x01, 0x80, 0x40, 0x10, 0x0C, 0x02, 0x00, 0x80, 0x20, 0x10, + 0x04, 0x01, 0x00, 0x80, 0x20, 0x1F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0xF8, + 0x67, 0x30, 0xF0, 0x1E, 0x09, 0xE6, 0x3B, 0x07, 0xC0, 0xF0, 0x3C, 0x0D, + 0x86, 0x1F, 0x00, 0x1E, 0x08, 0xC6, 0x1B, 0x02, 0xC0, 0xF0, 0x3C, 0x0F, + 0x03, 0xE0, 0xDC, 0x73, 0xEC, 0x06, 0x01, 0x80, 0xC0, 0x70, 0x38, 0x38, + 0x18, 0x00, 0xFC, 0x00, 0x3F, 0xCC, 0xC0, 0x00, 0x00, 0x06, 0x77, 0x12, + 0x40, 0x00, 0x00, 0x07, 0x01, 0xE0, 0x78, 0x1E, 0x07, 0x00, 0xC0, 0x0F, + 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x00, 0x10, 0xFF, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x0E, 0x00, 0x3C, 0x00, 0xF0, + 0x03, 0xC0, 0x0F, 0x00, 0x30, 0x0E, 0x07, 0x81, 0xE0, 0x78, 0x0E, 0x00, + 0x00, 0x00, 0x7C, 0x86, 0x83, 0xC3, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x08, + 0x10, 0x10, 0x00, 0x00, 0x30, 0x30, 0x30, 0x03, 0xF0, 0x06, 0x06, 0x06, + 0x00, 0x86, 0x00, 0x26, 0x0E, 0xD3, 0x0C, 0xC7, 0x0C, 0x63, 0x84, 0x31, + 0xC6, 0x18, 0xE3, 0x08, 0x71, 0x8C, 0x4C, 0xC6, 0x46, 0x3D, 0xC1, 0x80, + 0x00, 0x30, 0x10, 0x07, 0xF0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x2E, 0x00, 0x13, 0x00, 0x19, 0xC0, 0x08, 0x60, 0x04, 0x38, + 0x04, 0x0C, 0x03, 0xFF, 0x03, 0x03, 0x81, 0x00, 0xE1, 0x80, 0x70, 0xC0, + 0x3D, 0xF0, 0x3F, 0xFF, 0x83, 0x0C, 0x30, 0x63, 0x06, 0x30, 0x63, 0x06, + 0x30, 0xC3, 0xF0, 0x30, 0xE3, 0x06, 0x30, 0x33, 0x03, 0x30, 0x33, 0x07, + 0x30, 0xEF, 0xFC, 0x07, 0xE2, 0x38, 0x3C, 0xC0, 0x3B, 0x00, 0x36, 0x00, + 0x38, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x03, + 0x00, 0x06, 0x00, 0x06, 0x00, 0x47, 0x03, 0x03, 0xF8, 0xFF, 0xC0, 0x30, + 0x78, 0x30, 0x1C, 0x30, 0x0E, 0x30, 0x06, 0x30, 0x03, 0x30, 0x03, 0x30, + 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x06, 0x30, 0x06, 0x30, + 0x0C, 0x30, 0x78, 0xFF, 0xC0, 0xFF, 0xFC, 0xC0, 0x33, 0x00, 0x4C, 0x00, + 0x30, 0x00, 0xC0, 0x43, 0x03, 0x0F, 0xFC, 0x30, 0x30, 0xC0, 0x43, 0x00, + 0x0C, 0x00, 0x30, 0x08, 0xC0, 0x23, 0x03, 0xBF, 0xFE, 0xFF, 0xFC, 0xC0, + 0x33, 0x00, 0x4C, 0x00, 0x30, 0x00, 0xC0, 0x43, 0x03, 0x0F, 0xFC, 0x30, + 0x30, 0xC0, 0x43, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x3F, + 0x00, 0x07, 0xE4, 0x1C, 0x3C, 0x30, 0x0C, 0x60, 0x0C, 0x60, 0x04, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x3F, 0xC0, 0x0C, 0xC0, 0x0C, 0xC0, 0x0C, 0x60, + 0x0C, 0x60, 0x0C, 0x30, 0x0C, 0x1C, 0x1C, 0x07, 0xE0, 0xFC, 0x3F, 0x30, + 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x3F, + 0xFC, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, + 0x0C, 0x30, 0x0C, 0xFC, 0x3F, 0xFC, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, + 0xC3, 0x0C, 0x30, 0xC3, 0x3F, 0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xC8, 0xF0, 0xFC, 0xFE, 0x30, + 0x38, 0x30, 0x20, 0x30, 0x40, 0x30, 0x80, 0x33, 0x00, 0x36, 0x00, 0x3E, + 0x00, 0x37, 0x00, 0x33, 0x80, 0x31, 0xC0, 0x30, 0xE0, 0x30, 0x70, 0x30, + 0x38, 0x30, 0x3C, 0xFC, 0x7F, 0xFC, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, + 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, + 0xC0, 0x01, 0x80, 0x03, 0x00, 0x26, 0x00, 0x8C, 0x07, 0x7F, 0xFE, 0xF8, + 0x01, 0xE7, 0x00, 0x70, 0xE0, 0x0E, 0x1E, 0x03, 0xC2, 0xC0, 0x58, 0x5C, + 0x1B, 0x09, 0x82, 0x61, 0x38, 0x4C, 0x27, 0x11, 0x84, 0x72, 0x30, 0x8E, + 0xC6, 0x10, 0xD0, 0xC2, 0x1E, 0x18, 0x41, 0x83, 0x1C, 0x30, 0x67, 0xC4, + 0x3F, 0xF0, 0x1F, 0x78, 0x0E, 0x3C, 0x04, 0x3E, 0x04, 0x2E, 0x04, 0x27, + 0x04, 0x23, 0x84, 0x23, 0xC4, 0x21, 0xE4, 0x20, 0xE4, 0x20, 0x74, 0x20, + 0x3C, 0x20, 0x1C, 0x20, 0x0C, 0x70, 0x0C, 0xF8, 0x04, 0x07, 0xC0, 0x30, + 0x60, 0xC0, 0x63, 0x00, 0x66, 0x00, 0xD8, 0x00, 0xF0, 0x01, 0xE0, 0x03, + 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1B, 0x00, 0x66, 0x00, 0xC6, 0x03, 0x06, + 0x0C, 0x03, 0xE0, 0xFF, 0x83, 0x0E, 0x30, 0x73, 0x03, 0x30, 0x33, 0x03, + 0x30, 0x63, 0x0E, 0x3F, 0x83, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, + 0x30, 0x0F, 0xC0, 0x0F, 0xE0, 0x18, 0x30, 0x30, 0x18, 0x60, 0x0C, 0x60, + 0x0C, 0xC0, 0x06, 0xC0, 0x06, 0xC0, 0x06, 0xC0, 0x06, 0xC0, 0x06, 0xC0, + 0x06, 0x60, 0x0C, 0x60, 0x0C, 0x30, 0x18, 0x18, 0x30, 0x07, 0xC0, 0x03, + 0xC0, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1F, 0xFF, 0x80, 0x61, 0xC0, 0xC1, + 0xC1, 0x81, 0x83, 0x03, 0x06, 0x06, 0x0C, 0x1C, 0x18, 0x70, 0x3F, 0x80, + 0x67, 0x00, 0xC7, 0x01, 0x8F, 0x03, 0x0F, 0x06, 0x0E, 0x0C, 0x0E, 0x7E, + 0x0F, 0x1F, 0x46, 0x19, 0x81, 0x30, 0x27, 0x02, 0xF0, 0x0F, 0x00, 0xF8, + 0x07, 0xC0, 0x38, 0x03, 0xC0, 0x34, 0x06, 0x80, 0xDC, 0x32, 0x7C, 0xFF, + 0xFF, 0x86, 0x0E, 0x0C, 0x1C, 0x18, 0x10, 0x30, 0x00, 0x60, 0x00, 0xC0, + 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, + 0x60, 0x00, 0xC0, 0x07, 0xE0, 0xFC, 0x1F, 0x30, 0x0E, 0x30, 0x04, 0x30, + 0x04, 0x30, 0x04, 0x30, 0x04, 0x30, 0x04, 0x30, 0x04, 0x30, 0x04, 0x30, + 0x04, 0x30, 0x04, 0x30, 0x04, 0x30, 0x04, 0x18, 0x08, 0x1C, 0x18, 0x07, + 0xE0, 0xFE, 0x0F, 0x9C, 0x03, 0x0E, 0x01, 0x83, 0x00, 0x81, 0xC0, 0x40, + 0x60, 0x40, 0x38, 0x20, 0x0C, 0x30, 0x07, 0x10, 0x01, 0x98, 0x00, 0xE8, + 0x00, 0x34, 0x00, 0x1E, 0x00, 0x06, 0x00, 0x03, 0x00, 0x01, 0x00, 0xFC, + 0xFC, 0x3D, 0xE1, 0xC0, 0x63, 0x83, 0x01, 0x86, 0x0E, 0x04, 0x1C, 0x18, + 0x10, 0x70, 0x70, 0x80, 0xC3, 0xC2, 0x03, 0x8B, 0x08, 0x06, 0x6E, 0x40, + 0x1D, 0x19, 0x00, 0x74, 0x78, 0x00, 0xE1, 0xE0, 0x03, 0x83, 0x80, 0x0E, + 0x0C, 0x00, 0x10, 0x10, 0x00, 0x40, 0x40, 0x7F, 0x1F, 0x9E, 0x03, 0x07, + 0x03, 0x01, 0xC3, 0x00, 0x71, 0x00, 0x19, 0x00, 0x0F, 0x00, 0x03, 0x80, + 0x01, 0xE0, 0x01, 0xB0, 0x01, 0x9C, 0x00, 0x87, 0x00, 0x81, 0xC0, 0x80, + 0xE0, 0xC0, 0x79, 0xF8, 0x7F, 0xFE, 0x1F, 0x78, 0x0C, 0x38, 0x08, 0x1C, + 0x18, 0x0E, 0x10, 0x06, 0x20, 0x07, 0x60, 0x03, 0xC0, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, + 0xE0, 0x7F, 0xFB, 0x00, 0xC8, 0x07, 0x20, 0x38, 0x01, 0xC0, 0x07, 0x00, + 0x38, 0x01, 0xC0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x38, 0x05, + 0xC0, 0x3E, 0x01, 0xBF, 0xFE, 0xFE, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, + 0x8C, 0x63, 0x18, 0xC6, 0x31, 0xF0, 0xC1, 0x81, 0x03, 0x06, 0x04, 0x0C, + 0x18, 0x10, 0x30, 0x60, 0x40, 0xC1, 0x81, 0x03, 0x06, 0xF8, 0xC6, 0x31, + 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC7, 0xF0, 0x0C, 0x07, + 0x01, 0x60, 0xD8, 0x23, 0x18, 0xC4, 0x1B, 0x06, 0x80, 0xC0, 0xFF, 0xF0, + 0xC7, 0x0C, 0x30, 0x3E, 0x31, 0x8C, 0x30, 0x0C, 0x03, 0x07, 0xC6, 0x33, + 0x0C, 0xC3, 0x31, 0xC7, 0xB8, 0x20, 0x38, 0x06, 0x01, 0x80, 0x60, 0x18, + 0x06, 0xF1, 0xC6, 0x61, 0xD8, 0x36, 0x0D, 0x83, 0x60, 0xD8, 0x26, 0x19, + 0x84, 0x3E, 0x00, 0x1E, 0x23, 0x63, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE1, + 0x72, 0x3C, 0x00, 0x80, 0xE0, 0x18, 0x06, 0x01, 0x80, 0x61, 0xD8, 0x8E, + 0x61, 0xB0, 0x6C, 0x1B, 0x06, 0xC1, 0xB0, 0x6E, 0x19, 0xCE, 0x3D, 0xC0, + 0x1E, 0x08, 0xE4, 0x1B, 0xFE, 0xC0, 0x30, 0x0C, 0x03, 0x81, 0x60, 0x9C, + 0x41, 0xE0, 0x0F, 0x08, 0xC4, 0x06, 0x03, 0x01, 0x81, 0xF0, 0x60, 0x30, + 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0xFC, 0x00, 0x1F, 0x03, + 0x1F, 0x60, 0xC6, 0x0C, 0x60, 0xC3, 0x18, 0x1F, 0x02, 0x00, 0x40, 0x07, + 0xFC, 0x40, 0x24, 0x02, 0xC0, 0x2C, 0x04, 0xE0, 0x83, 0xF0, 0x30, 0x1E, + 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0D, 0xE1, 0xCE, 0x30, 0xC6, 0x18, + 0xC3, 0x18, 0x63, 0x0C, 0x61, 0x8C, 0x31, 0x86, 0x79, 0xE0, 0x31, 0x80, + 0x00, 0x09, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xDF, 0x0C, 0x30, 0x00, 0x00, + 0x31, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xF2, 0xF0, + 0x20, 0x1C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0xFB, 0x08, 0x62, + 0x0C, 0x81, 0xE0, 0x3E, 0x06, 0xE0, 0xCE, 0x18, 0xC3, 0x0E, 0xF3, 0xE0, + 0x13, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0xF8, 0xF7, + 0x8F, 0x0E, 0x3C, 0xE3, 0x0C, 0x18, 0xC3, 0x06, 0x30, 0xC1, 0x8C, 0x30, + 0x63, 0x0C, 0x18, 0xC3, 0x06, 0x30, 0xC1, 0x8C, 0x30, 0x67, 0x9E, 0x3C, + 0xF7, 0x87, 0x18, 0xC3, 0x18, 0x63, 0x0C, 0x61, 0x8C, 0x31, 0x86, 0x30, + 0xC6, 0x19, 0xE7, 0x80, 0x1E, 0x18, 0xE4, 0x1B, 0x03, 0xC0, 0xF0, 0x3C, + 0x0F, 0x03, 0x60, 0x9C, 0x41, 0xE0, 0x77, 0x87, 0x18, 0xC3, 0x98, 0x33, + 0x06, 0x60, 0xCC, 0x19, 0x83, 0x30, 0xC7, 0x10, 0xDC, 0x18, 0x03, 0x00, + 0x60, 0x0C, 0x07, 0xE0, 0x1E, 0x8C, 0xE6, 0x1B, 0x06, 0xC1, 0xB0, 0x6C, + 0x1B, 0x06, 0xE1, 0x98, 0xE3, 0xD8, 0x06, 0x01, 0x80, 0x60, 0x18, 0x1F, + 0x37, 0x7B, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x7C, 0x7B, + 0x0E, 0x1C, 0x1E, 0x0F, 0x07, 0xC3, 0x87, 0x8A, 0xE0, 0x21, 0x8F, 0x98, + 0x61, 0x86, 0x18, 0x61, 0x86, 0x19, 0x38, 0xE3, 0x98, 0x66, 0x19, 0x86, + 0x61, 0x98, 0x66, 0x19, 0x86, 0x61, 0x9C, 0xE3, 0xDC, 0xF8, 0xEE, 0x08, + 0xC1, 0x18, 0x41, 0x88, 0x32, 0x03, 0x40, 0x68, 0x06, 0x00, 0xC0, 0x10, + 0x00, 0xF3, 0xE7, 0x61, 0x83, 0x70, 0xC2, 0x30, 0xC2, 0x30, 0xC4, 0x19, + 0x64, 0x19, 0x68, 0x0E, 0x38, 0x0E, 0x38, 0x0C, 0x30, 0x04, 0x10, 0xFB, + 0xC6, 0x30, 0x64, 0x0F, 0x00, 0xC0, 0x0C, 0x03, 0xC0, 0x98, 0x21, 0x8C, + 0x3B, 0xCF, 0x80, 0xF8, 0xEE, 0x08, 0xC1, 0x18, 0x41, 0x88, 0x31, 0x03, + 0x40, 0x68, 0x06, 0x00, 0xC0, 0x08, 0x02, 0x00, 0x40, 0x10, 0x1E, 0x03, + 0x80, 0x7F, 0x90, 0xE0, 0x30, 0x18, 0x0E, 0x03, 0x01, 0xC0, 0xE0, 0x30, + 0x5C, 0x3F, 0xF8, 0x19, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0xB0, 0x63, 0x18, + 0xC6, 0x31, 0x8C, 0x61, 0x80, 0xFF, 0xFF, 0x80, 0xC3, 0x18, 0xC6, 0x31, + 0x8C, 0x63, 0x06, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xCC, 0x00, 0x38, 0x06, + 0x62, 0x41, 0xC0}; + +const GFXglyph FreeSerif12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 6, 0, 1}, // 0x20 ' ' + {0, 2, 16, 8, 3, -15}, // 0x21 '!' + {4, 6, 6, 10, 1, -15}, // 0x22 '"' + {9, 12, 16, 12, 0, -15}, // 0x23 '#' + {33, 10, 18, 12, 1, -16}, // 0x24 '$' + {56, 18, 17, 20, 1, -16}, // 0x25 '%' + {95, 17, 16, 19, 1, -15}, // 0x26 '&' + {129, 2, 6, 5, 1, -15}, // 0x27 ''' + {131, 6, 20, 8, 1, -15}, // 0x28 '(' + {146, 6, 20, 8, 1, -15}, // 0x29 ')' + {161, 8, 10, 12, 3, -14}, // 0x2A '*' + {171, 11, 11, 14, 1, -10}, // 0x2B '+' + {187, 3, 6, 6, 2, -2}, // 0x2C ',' + {190, 6, 1, 8, 1, -5}, // 0x2D '-' + {191, 2, 3, 6, 2, -2}, // 0x2E '.' + {192, 7, 17, 7, 0, -16}, // 0x2F '/' + {207, 10, 17, 12, 1, -16}, // 0x30 '0' + {229, 6, 17, 12, 3, -16}, // 0x31 '1' + {242, 10, 15, 12, 1, -14}, // 0x32 '2' + {261, 10, 16, 12, 1, -15}, // 0x33 '3' + {281, 10, 16, 12, 1, -15}, // 0x34 '4' + {301, 10, 17, 12, 1, -16}, // 0x35 '5' + {323, 10, 17, 12, 1, -16}, // 0x36 '6' + {345, 10, 16, 12, 0, -15}, // 0x37 '7' + {365, 10, 17, 12, 1, -16}, // 0x38 '8' + {387, 10, 18, 12, 1, -16}, // 0x39 '9' + {410, 2, 12, 6, 2, -11}, // 0x3A ':' + {413, 4, 15, 6, 2, -11}, // 0x3B ';' + {421, 12, 13, 14, 1, -12}, // 0x3C '<' + {441, 12, 6, 14, 1, -8}, // 0x3D '=' + {450, 12, 13, 14, 1, -11}, // 0x3E '>' + {470, 8, 17, 11, 2, -16}, // 0x3F '?' + {487, 17, 16, 21, 2, -15}, // 0x40 '@' + {521, 17, 16, 17, 0, -15}, // 0x41 'A' + {555, 12, 16, 15, 1, -15}, // 0x42 'B' + {579, 15, 16, 16, 1, -15}, // 0x43 'C' + {609, 16, 16, 17, 0, -15}, // 0x44 'D' + {641, 14, 16, 15, 0, -15}, // 0x45 'E' + {669, 14, 16, 14, 0, -15}, // 0x46 'F' + {697, 16, 16, 17, 1, -15}, // 0x47 'G' + {729, 16, 16, 17, 0, -15}, // 0x48 'H' + {761, 6, 16, 8, 1, -15}, // 0x49 'I' + {773, 8, 16, 9, 0, -15}, // 0x4A 'J' + {789, 16, 16, 17, 1, -15}, // 0x4B 'K' + {821, 15, 16, 15, 0, -15}, // 0x4C 'L' + {851, 19, 16, 21, 1, -15}, // 0x4D 'M' + {889, 16, 16, 17, 1, -15}, // 0x4E 'N' + {921, 15, 16, 17, 1, -15}, // 0x4F 'O' + {951, 12, 16, 14, 0, -15}, // 0x50 'P' + {975, 16, 20, 17, 1, -15}, // 0x51 'Q' + {1015, 15, 16, 16, 0, -15}, // 0x52 'R' + {1045, 11, 16, 13, 0, -15}, // 0x53 'S' + {1067, 15, 16, 15, 0, -15}, // 0x54 'T' + {1097, 16, 16, 17, 1, -15}, // 0x55 'U' + {1129, 17, 16, 17, 0, -15}, // 0x56 'V' + {1163, 22, 16, 23, 0, -15}, // 0x57 'W' + {1207, 17, 16, 17, 0, -15}, // 0x58 'X' + {1241, 16, 16, 17, 0, -15}, // 0x59 'Y' + {1273, 14, 16, 15, 1, -15}, // 0x5A 'Z' + {1301, 5, 20, 8, 2, -15}, // 0x5B '[' + {1314, 7, 17, 7, 0, -16}, // 0x5C '\' + {1329, 5, 20, 8, 1, -15}, // 0x5D ']' + {1342, 10, 9, 11, 1, -15}, // 0x5E '^' + {1354, 12, 1, 12, 0, 3}, // 0x5F '_' + {1356, 5, 4, 6, 0, -15}, // 0x60 '`' + {1359, 10, 11, 10, 1, -10}, // 0x61 'a' + {1373, 10, 17, 12, 1, -16}, // 0x62 'b' + {1395, 8, 11, 11, 1, -10}, // 0x63 'c' + {1406, 10, 17, 12, 1, -16}, // 0x64 'd' + {1428, 10, 11, 11, 1, -10}, // 0x65 'e' + {1442, 9, 17, 9, 0, -16}, // 0x66 'f' + {1462, 12, 16, 11, 0, -10}, // 0x67 'g' + {1486, 11, 17, 12, 0, -16}, // 0x68 'h' + {1510, 5, 16, 7, 0, -15}, // 0x69 'i' + {1520, 6, 21, 8, 0, -15}, // 0x6A 'j' + {1536, 11, 17, 12, 1, -16}, // 0x6B 'k' + {1560, 5, 17, 6, 0, -16}, // 0x6C 'l' + {1571, 18, 11, 19, 0, -10}, // 0x6D 'm' + {1596, 11, 11, 12, 0, -10}, // 0x6E 'n' + {1612, 10, 11, 12, 1, -10}, // 0x6F 'o' + {1626, 11, 16, 12, 0, -10}, // 0x70 'p' + {1648, 10, 16, 12, 1, -10}, // 0x71 'q' + {1668, 8, 11, 8, 0, -10}, // 0x72 'r' + {1679, 7, 11, 9, 1, -10}, // 0x73 's' + {1689, 6, 13, 7, 1, -12}, // 0x74 't' + {1699, 10, 11, 12, 1, -10}, // 0x75 'u' + {1713, 11, 11, 11, 0, -10}, // 0x76 'v' + {1729, 16, 11, 16, 0, -10}, // 0x77 'w' + {1751, 11, 11, 12, 0, -10}, // 0x78 'x' + {1767, 11, 16, 11, 0, -10}, // 0x79 'y' + {1789, 10, 11, 10, 0, -10}, // 0x7A 'z' + {1803, 5, 21, 12, 2, -16}, // 0x7B '{' + {1817, 1, 17, 5, 2, -16}, // 0x7C '|' + {1820, 5, 21, 12, 5, -15}, // 0x7D '}' + {1834, 12, 3, 12, 0, -6}}; // 0x7E '~' + +const GFXfont FreeSerif12pt7b PROGMEM = {(uint8_t *)FreeSerif12pt7bBitmaps, + (GFXglyph *)FreeSerif12pt7bGlyphs, + 0x20, 0x7E, 29}; + +// Approx. 2511 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerif18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif18pt7b.h new file mode 100644 index 0000000..4eeb155 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif18pt7b.h @@ -0,0 +1,428 @@ +const uint8_t FreeSerif18pt7bBitmaps[] PROGMEM = { + 0x6F, 0xFF, 0xFF, 0xFE, 0x66, 0x66, 0x66, 0x64, 0x40, 0x00, 0x6F, 0xF6, + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x46, 0x42, 0x42, 0x42, 0x03, 0x06, 0x01, + 0x83, 0x00, 0xC1, 0x80, 0x61, 0xC0, 0x30, 0xC0, 0x38, 0x60, 0x18, 0x30, + 0xFF, 0xFF, 0x7F, 0xFF, 0x83, 0x06, 0x01, 0x86, 0x00, 0xC3, 0x00, 0xC1, + 0x87, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x30, 0x0C, 0x18, 0x06, 0x18, 0x06, + 0x0C, 0x03, 0x06, 0x01, 0x83, 0x00, 0xC1, 0x80, 0x60, 0xC0, 0x02, 0x00, + 0x10, 0x03, 0xE0, 0x64, 0xE6, 0x23, 0x61, 0x1B, 0x08, 0x58, 0x42, 0xE2, + 0x03, 0x90, 0x1F, 0x80, 0x7E, 0x00, 0xFC, 0x01, 0xF0, 0x0F, 0xC0, 0x4E, + 0x02, 0x38, 0x10, 0xE0, 0x87, 0x04, 0x3C, 0x21, 0xE1, 0x1B, 0xC9, 0xCF, + 0xFC, 0x1F, 0x80, 0x10, 0x00, 0x80, 0x07, 0x80, 0x20, 0x0F, 0xF0, 0x70, + 0x0F, 0x07, 0xD0, 0x0F, 0x02, 0x18, 0x07, 0x01, 0x18, 0x07, 0x00, 0x8C, + 0x03, 0x80, 0x4C, 0x01, 0x80, 0x44, 0x00, 0xC0, 0x26, 0x00, 0x60, 0x22, + 0x0F, 0x30, 0x33, 0x1F, 0xCC, 0x73, 0x1E, 0x37, 0xF1, 0x8E, 0x19, 0xE1, + 0x8E, 0x04, 0x00, 0x86, 0x02, 0x00, 0xC7, 0x01, 0x00, 0xC3, 0x80, 0x80, + 0x61, 0x80, 0x80, 0x60, 0xC0, 0x40, 0x30, 0x60, 0x40, 0x30, 0x38, 0xE0, + 0x30, 0x0F, 0xE0, 0x18, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0x61, 0x80, 0x00, 0x60, 0x60, 0x00, 0x30, 0x30, 0x00, 0x18, 0x18, + 0x00, 0x0C, 0x0C, 0x00, 0x06, 0x0C, 0x00, 0x03, 0x8E, 0x00, 0x01, 0xCE, + 0x00, 0x00, 0x7C, 0x3F, 0xC0, 0x38, 0x07, 0x80, 0x3E, 0x03, 0x80, 0x77, + 0x01, 0x80, 0x73, 0xC0, 0x80, 0xF0, 0xF0, 0xC0, 0x70, 0x7C, 0xC0, 0x78, + 0x1E, 0x40, 0x3C, 0x07, 0xC0, 0x1E, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x0F, + 0xC0, 0xFF, 0x0D, 0xF0, 0xC7, 0xFC, 0x7F, 0xC1, 0xFC, 0x1F, 0x80, 0x3C, + 0x00, 0xFF, 0xFE, 0x92, 0x40, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xC0, + 0xC0, 0x60, 0x70, 0x30, 0x18, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, + 0x70, 0x38, 0x0C, 0x06, 0x03, 0x80, 0xC0, 0x60, 0x18, 0x0C, 0x03, 0x00, + 0xC0, 0x30, 0x0C, 0x80, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x60, 0x18, 0x0C, + 0x07, 0x01, 0x80, 0xC0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, + 0xE0, 0x60, 0x30, 0x38, 0x18, 0x0C, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x00, 0x0C, 0x00, 0xC0, 0x0C, 0x0C, 0x46, 0xE4, 0xF7, 0x5E, 0x1F, 0x00, + 0xC0, 0x17, 0x8E, 0x4E, 0xE4, 0xFC, 0xC6, 0x0C, 0x00, 0xC0, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x6F, 0xFF, + 0x11, 0x24, 0x80, 0xFF, 0xFF, 0x6F, 0xF6, 0x00, 0xC0, 0x60, 0x18, 0x06, + 0x03, 0x80, 0xC0, 0x30, 0x1C, 0x06, 0x01, 0x80, 0xE0, 0x30, 0x0C, 0x07, + 0x01, 0x80, 0x60, 0x38, 0x0C, 0x03, 0x01, 0xC0, 0x60, 0x18, 0x0E, 0x03, + 0x00, 0x03, 0xE0, 0x0E, 0x70, 0x1C, 0x38, 0x38, 0x1C, 0x38, 0x1C, 0x78, + 0x1E, 0x70, 0x0E, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, + 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0x70, 0x0E, 0x70, + 0x0E, 0x78, 0x1E, 0x38, 0x1C, 0x38, 0x1C, 0x1C, 0x38, 0x0C, 0x30, 0x03, + 0xC0, 0x06, 0x03, 0x83, 0xE3, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, + 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, + 0xE0, 0x38, 0x0E, 0x03, 0x81, 0xE1, 0xFF, 0x07, 0xC0, 0x1F, 0xF0, 0x3F, + 0xF8, 0x70, 0xF8, 0x60, 0x3C, 0xC0, 0x3C, 0x80, 0x1C, 0x00, 0x1C, 0x00, + 0x1C, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x00, 0x30, 0x00, 0x60, 0x00, + 0xC0, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x01, 0x10, + 0x02, 0x3F, 0xFE, 0x7F, 0xFC, 0xFF, 0xFC, 0x0F, 0xC0, 0xFF, 0x0C, 0x3C, + 0x80, 0xE4, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x04, 0x00, 0x40, 0x04, 0x00, + 0xF8, 0x1F, 0xE0, 0x0F, 0x00, 0x1C, 0x00, 0xE0, 0x03, 0x00, 0x18, 0x00, + 0xC0, 0x06, 0x00, 0x60, 0x03, 0x78, 0x73, 0xFF, 0x0F, 0xC0, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x70, 0x00, 0xF0, 0x00, 0xB0, 0x01, 0x30, 0x03, 0x30, + 0x06, 0x30, 0x04, 0x30, 0x08, 0x30, 0x18, 0x30, 0x10, 0x30, 0x20, 0x30, + 0x60, 0x30, 0xC0, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x7F, 0xC3, + 0xFE, 0x1F, 0xE1, 0x80, 0x08, 0x00, 0xC0, 0x07, 0xC0, 0x7F, 0x81, 0xFF, + 0x00, 0xFC, 0x01, 0xE0, 0x07, 0x80, 0x1C, 0x00, 0x60, 0x03, 0x00, 0x18, + 0x00, 0xC0, 0x06, 0x00, 0x60, 0x07, 0x78, 0x73, 0xFF, 0x0F, 0xC0, 0x00, + 0x0E, 0x00, 0xF8, 0x03, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x3C, + 0x00, 0x7C, 0x00, 0x79, 0xF0, 0x7F, 0xFC, 0xF8, 0x3C, 0xF0, 0x1E, 0xF0, + 0x1F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0x70, 0x0F, 0x78, + 0x0F, 0x78, 0x0E, 0x3C, 0x1E, 0x1E, 0x3C, 0x0F, 0xF8, 0x07, 0xE0, 0x3F, + 0xFD, 0xFF, 0xF7, 0xFF, 0xF0, 0x06, 0x80, 0x18, 0x00, 0x60, 0x03, 0x00, + 0x0C, 0x00, 0x30, 0x01, 0x80, 0x06, 0x00, 0x18, 0x00, 0xE0, 0x03, 0x00, + 0x0C, 0x00, 0x70, 0x01, 0x80, 0x06, 0x00, 0x38, 0x00, 0xC0, 0x03, 0x00, + 0x1C, 0x00, 0x60, 0x00, 0x0F, 0x83, 0xFC, 0x70, 0xE6, 0x07, 0xC0, 0x3C, + 0x03, 0xC0, 0x3E, 0x03, 0x70, 0x67, 0x8C, 0x3D, 0x81, 0xF0, 0x0F, 0x81, + 0x7C, 0x21, 0xE6, 0x0E, 0xC0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, + 0x06, 0x70, 0xE3, 0xFC, 0x0F, 0x80, 0x07, 0xC0, 0x1F, 0xF0, 0x3C, 0x78, + 0x38, 0x3C, 0x78, 0x1E, 0x70, 0x1E, 0xF0, 0x0E, 0xF0, 0x0F, 0xF0, 0x0F, + 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF8, 0x0F, 0x78, 0x0F, 0x3C, 0x3F, + 0x1F, 0xEE, 0x0F, 0x9E, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x38, 0x00, 0x78, + 0x00, 0xF0, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x70, 0x00, 0x6F, 0xF6, + 0x00, 0x00, 0x00, 0x00, 0x06, 0xFF, 0x60, 0x67, 0xBC, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0xEF, 0x78, 0x42, 0x22, 0x20, 0x00, 0x00, 0xC0, + 0x00, 0xF0, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF0, 0x03, 0xF0, + 0x03, 0xF0, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, + 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xC0, + 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x80, + 0x00, 0x3C, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x07, 0xC0, 0x00, 0x7C, + 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x07, 0xC0, 0x00, 0xF0, 0x00, 0xFC, + 0x00, 0xFC, 0x00, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF8, 0x00, 0xF0, + 0x00, 0x30, 0x00, 0x00, 0x1F, 0x81, 0xFF, 0x18, 0x7D, 0x81, 0xEC, 0x07, + 0xF0, 0x3F, 0x81, 0xE0, 0x0F, 0x00, 0x70, 0x03, 0x80, 0x38, 0x01, 0x80, + 0x08, 0x00, 0xC0, 0x04, 0x00, 0x20, 0x02, 0x00, 0x10, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x3C, 0x01, 0xE0, 0x07, 0x00, 0x00, 0x7F, 0x00, + 0x01, 0xFF, 0xC0, 0x07, 0x80, 0xF0, 0x0F, 0x00, 0x38, 0x1C, 0x00, 0x1C, + 0x38, 0x00, 0x0C, 0x38, 0x00, 0x06, 0x70, 0x1E, 0x02, 0x70, 0x3F, 0xE3, + 0xF0, 0x71, 0xE1, 0xE0, 0xE0, 0xC1, 0xE0, 0xC0, 0xC1, 0xE0, 0xC1, 0xC1, + 0xE1, 0x81, 0xC1, 0xE1, 0x81, 0x83, 0xE1, 0x83, 0x82, 0xE1, 0x83, 0x86, + 0x71, 0xC7, 0x8C, 0x70, 0xF9, 0xF8, 0x38, 0xF0, 0xF0, 0x3C, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x07, 0x80, 0x70, 0x03, 0xFF, 0xE0, 0x00, 0x7F, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x5C, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x8E, 0x00, + 0x01, 0x8F, 0x00, 0x01, 0x87, 0x00, 0x03, 0x07, 0x80, 0x03, 0x03, 0x80, + 0x02, 0x03, 0xC0, 0x06, 0x03, 0xC0, 0x07, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, + 0x0C, 0x01, 0xE0, 0x18, 0x00, 0xF0, 0x18, 0x00, 0xF0, 0x30, 0x00, 0x78, + 0x30, 0x00, 0x78, 0x70, 0x00, 0x7C, 0xFC, 0x01, 0xFF, 0xFF, 0xFC, 0x03, + 0xFF, 0xF8, 0x1E, 0x0F, 0xC1, 0xE0, 0x3C, 0x1E, 0x01, 0xE1, 0xE0, 0x1E, + 0x1E, 0x01, 0xE1, 0xE0, 0x1E, 0x1E, 0x03, 0xC1, 0xE0, 0x78, 0x1F, 0xFE, + 0x01, 0xFF, 0xF0, 0x1E, 0x07, 0xC1, 0xE0, 0x1E, 0x1E, 0x00, 0xF1, 0xE0, + 0x0F, 0x1E, 0x00, 0xF1, 0xE0, 0x0F, 0x1E, 0x00, 0xF1, 0xE0, 0x1E, 0x1E, + 0x07, 0xE3, 0xFF, 0xF8, 0xFF, 0xFE, 0x00, 0x00, 0xFE, 0x08, 0x0F, 0xFF, + 0x60, 0xFC, 0x1F, 0x87, 0xC0, 0x1E, 0x3C, 0x00, 0x38, 0xF0, 0x00, 0x67, + 0x80, 0x01, 0x9E, 0x00, 0x02, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x0F, + 0x00, 0x02, 0x1F, 0x00, 0x38, 0x3F, 0x03, 0x80, 0x7F, 0xFC, 0x00, 0x3F, + 0x80, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0x78, 0x3F, 0x80, 0xF0, 0x0F, + 0x81, 0xE0, 0x0F, 0x83, 0xC0, 0x0F, 0x07, 0x80, 0x0F, 0x0F, 0x00, 0x1E, + 0x1E, 0x00, 0x1E, 0x3C, 0x00, 0x3C, 0x78, 0x00, 0x78, 0xF0, 0x00, 0xF1, + 0xE0, 0x01, 0xE3, 0xC0, 0x03, 0xC7, 0x80, 0x07, 0x8F, 0x00, 0x1E, 0x1E, + 0x00, 0x3C, 0x3C, 0x00, 0xF0, 0x78, 0x01, 0xE0, 0xF0, 0x0F, 0x81, 0xE0, + 0x7E, 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x87, 0xFF, + 0xF8, 0x3C, 0x01, 0x83, 0xC0, 0x08, 0x3C, 0x00, 0x83, 0xC0, 0x00, 0x3C, + 0x00, 0x03, 0xC0, 0x00, 0x3C, 0x02, 0x03, 0xC0, 0x60, 0x3F, 0xFE, 0x03, + 0xFF, 0xE0, 0x3C, 0x06, 0x03, 0xC0, 0x20, 0x3C, 0x00, 0x03, 0xC0, 0x00, + 0x3C, 0x00, 0x03, 0xC0, 0x01, 0x3C, 0x00, 0x23, 0xC0, 0x06, 0x3C, 0x01, + 0xE7, 0xFF, 0xFE, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xBF, 0xFF, 0xCF, 0x00, + 0x67, 0x80, 0x13, 0xC0, 0x09, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, + 0x02, 0x1E, 0x03, 0x0F, 0xFF, 0x87, 0xFF, 0xC3, 0xC0, 0x61, 0xE0, 0x10, + 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, + 0x03, 0xC0, 0x03, 0xF0, 0x03, 0xFC, 0x00, 0x00, 0xFE, 0x04, 0x07, 0xFF, + 0xB8, 0x1F, 0x03, 0xF0, 0xF8, 0x01, 0xE3, 0xE0, 0x01, 0xC7, 0x80, 0x01, + 0x9E, 0x00, 0x01, 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x03, + 0xC0, 0x00, 0x07, 0x80, 0x07, 0xFF, 0x00, 0x07, 0xDE, 0x00, 0x07, 0xBC, + 0x00, 0x0F, 0x78, 0x00, 0x1E, 0x78, 0x00, 0x3C, 0xF0, 0x00, 0x78, 0xF0, + 0x00, 0xF1, 0xF0, 0x01, 0xE1, 0xF0, 0x03, 0xC1, 0xF8, 0x1F, 0x00, 0xFF, + 0xFC, 0x00, 0x3F, 0x80, 0xFF, 0x03, 0xFD, 0xF8, 0x07, 0xE3, 0xC0, 0x0F, + 0x0F, 0x00, 0x3C, 0x3C, 0x00, 0xF0, 0xF0, 0x03, 0xC3, 0xC0, 0x0F, 0x0F, + 0x00, 0x3C, 0x3C, 0x00, 0xF0, 0xF0, 0x03, 0xC3, 0xFF, 0xFF, 0x0F, 0xFF, + 0xFC, 0x3C, 0x00, 0xF0, 0xF0, 0x03, 0xC3, 0xC0, 0x0F, 0x0F, 0x00, 0x3C, + 0x3C, 0x00, 0xF0, 0xF0, 0x03, 0xC3, 0xC0, 0x0F, 0x0F, 0x00, 0x3C, 0x3C, + 0x00, 0xF1, 0xF8, 0x07, 0xEF, 0xF0, 0x3F, 0xC0, 0xFF, 0xBF, 0x0F, 0x07, + 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, + 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC3, 0xF3, 0xFE, 0x0F, 0xF0, + 0x7E, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xC6, 0x38, 0xF3, 0x8F, 0xF0, 0x7C, 0x00, 0xFF, 0x07, 0xFC, + 0xFC, 0x03, 0xC0, 0xF0, 0x07, 0x01, 0xE0, 0x1C, 0x03, 0xC0, 0x60, 0x07, + 0x81, 0x80, 0x0F, 0x06, 0x00, 0x1E, 0x18, 0x00, 0x3C, 0x60, 0x00, 0x79, + 0x80, 0x00, 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x03, 0xDF, 0x00, 0x07, 0x8F, + 0x00, 0x0F, 0x0F, 0x00, 0x1E, 0x0F, 0x00, 0x3C, 0x0F, 0x00, 0x78, 0x0F, + 0x00, 0xF0, 0x1F, 0x01, 0xE0, 0x1F, 0x03, 0xC0, 0x1F, 0x0F, 0xC0, 0x3F, + 0x3F, 0xC1, 0xFF, 0x80, 0xFF, 0x00, 0x0F, 0xC0, 0x00, 0xF0, 0x00, 0x1E, + 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, + 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, 0x00, 0x78, + 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x07, 0x80, 0x04, 0xF0, + 0x01, 0x1E, 0x00, 0x63, 0xC0, 0x3C, 0xFF, 0xFF, 0xBF, 0xFF, 0xE0, 0xFC, + 0x00, 0x03, 0xF9, 0xF0, 0x00, 0x1F, 0x87, 0x80, 0x01, 0xF8, 0x3E, 0x00, + 0x0F, 0xC1, 0xF0, 0x00, 0x5E, 0x0B, 0xC0, 0x06, 0xF0, 0x5E, 0x00, 0x37, + 0x82, 0x78, 0x03, 0x3C, 0x13, 0xC0, 0x19, 0xE0, 0x8F, 0x01, 0x8F, 0x04, + 0x78, 0x0C, 0x78, 0x21, 0xE0, 0xC3, 0xC1, 0x0F, 0x06, 0x1E, 0x08, 0x3C, + 0x60, 0xF0, 0x41, 0xE3, 0x07, 0x82, 0x07, 0xB0, 0x3C, 0x10, 0x3D, 0x81, + 0xE0, 0x81, 0xF8, 0x0F, 0x04, 0x07, 0xC0, 0x78, 0x20, 0x3C, 0x03, 0xC1, + 0x00, 0xE0, 0x1E, 0x1C, 0x06, 0x01, 0xFB, 0xF8, 0x10, 0x1F, 0xE0, 0xFC, + 0x00, 0xFE, 0x78, 0x00, 0x70, 0x78, 0x00, 0x40, 0xF8, 0x00, 0x81, 0xF8, + 0x01, 0x02, 0xF8, 0x02, 0x04, 0xF8, 0x04, 0x08, 0xF0, 0x08, 0x11, 0xF0, + 0x10, 0x21, 0xF0, 0x20, 0x41, 0xF0, 0x40, 0x81, 0xF0, 0x81, 0x01, 0xF1, + 0x02, 0x01, 0xE2, 0x04, 0x03, 0xE4, 0x08, 0x03, 0xE8, 0x10, 0x03, 0xF0, + 0x20, 0x03, 0xE0, 0x40, 0x03, 0xC0, 0x80, 0x03, 0x81, 0x00, 0x07, 0x07, + 0x00, 0x06, 0x3F, 0x80, 0x04, 0x00, 0x00, 0xFE, 0x00, 0x07, 0xFF, 0x00, + 0x3E, 0x0F, 0x80, 0xF0, 0x07, 0x83, 0xC0, 0x07, 0x87, 0x80, 0x07, 0x1E, + 0x00, 0x0F, 0x3C, 0x00, 0x1E, 0xF0, 0x00, 0x1F, 0xE0, 0x00, 0x3F, 0xC0, + 0x00, 0x7F, 0x80, 0x00, 0xFF, 0x00, 0x01, 0xFE, 0x00, 0x03, 0xFC, 0x00, + 0x07, 0xF8, 0x00, 0x0F, 0x78, 0x00, 0x3C, 0xF0, 0x00, 0x78, 0xE0, 0x01, + 0xE1, 0xE0, 0x03, 0xC1, 0xE0, 0x0F, 0x01, 0xF0, 0x7C, 0x00, 0xFF, 0xE0, + 0x00, 0x7F, 0x00, 0xFF, 0xF8, 0x1F, 0xFF, 0x83, 0xC1, 0xF0, 0xF0, 0x1E, + 0x3C, 0x07, 0xCF, 0x00, 0xF3, 0xC0, 0x3C, 0xF0, 0x0F, 0x3C, 0x03, 0xCF, + 0x01, 0xF3, 0xC0, 0x78, 0xF0, 0x7C, 0x3F, 0xFE, 0x0F, 0xFE, 0x03, 0xC0, + 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, + 0x3C, 0x00, 0x1F, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x07, 0xFF, + 0x00, 0x3E, 0x0F, 0x80, 0xF0, 0x07, 0x83, 0xC0, 0x07, 0x87, 0x80, 0x0F, + 0x1E, 0x00, 0x0F, 0x3C, 0x00, 0x1E, 0xF0, 0x00, 0x1D, 0xE0, 0x00, 0x3F, + 0xC0, 0x00, 0x7F, 0x80, 0x00, 0xFF, 0x00, 0x01, 0xFE, 0x00, 0x03, 0xFC, + 0x00, 0x07, 0xF8, 0x00, 0x0F, 0x70, 0x00, 0x1C, 0xF0, 0x00, 0x79, 0xE0, + 0x00, 0xF1, 0xE0, 0x03, 0xC1, 0xC0, 0x07, 0x01, 0xC0, 0x1C, 0x01, 0xE0, + 0xF0, 0x00, 0x7F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0xC0, 0xFF, 0xF0, + 0x03, 0xFF, 0xF0, 0x0F, 0x07, 0xC0, 0x78, 0x1E, 0x03, 0xC0, 0x78, 0x1E, + 0x03, 0xC0, 0xF0, 0x1E, 0x07, 0x80, 0xF0, 0x3C, 0x07, 0x81, 0xE0, 0x78, + 0x0F, 0x0F, 0x80, 0x7F, 0xF8, 0x03, 0xFE, 0x00, 0x1E, 0x78, 0x00, 0xF1, + 0xE0, 0x07, 0x87, 0x80, 0x3C, 0x3C, 0x01, 0xE0, 0xF0, 0x0F, 0x03, 0xC0, + 0x78, 0x0F, 0x03, 0xC0, 0x7C, 0x3F, 0x01, 0xF3, 0xFC, 0x07, 0xE0, 0x07, + 0x84, 0x1F, 0xFC, 0x3C, 0x3E, 0x30, 0x0E, 0x70, 0x06, 0x70, 0x06, 0x70, + 0x02, 0x78, 0x00, 0x7C, 0x00, 0x3F, 0x00, 0x1F, 0xC0, 0x0F, 0xE0, 0x03, + 0xF8, 0x00, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x80, 0x0F, 0x80, 0x0F, 0xC0, + 0x0F, 0xE0, 0x0F, 0x70, 0x1E, 0x78, 0x3C, 0x4F, 0xF8, 0x43, 0xF0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0x7C, 0x0F, 0x03, 0x80, 0xF0, 0x10, + 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, + 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, + 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x0F, + 0x00, 0x00, 0xF0, 0x00, 0x1F, 0x80, 0x03, 0xFC, 0x00, 0xFF, 0x01, 0xFD, + 0xF8, 0x01, 0xC3, 0xC0, 0x02, 0x0F, 0x00, 0x08, 0x3C, 0x00, 0x20, 0xF0, + 0x00, 0x83, 0xC0, 0x02, 0x0F, 0x00, 0x08, 0x3C, 0x00, 0x20, 0xF0, 0x00, + 0x83, 0xC0, 0x02, 0x0F, 0x00, 0x08, 0x3C, 0x00, 0x20, 0xF0, 0x00, 0x83, + 0xC0, 0x02, 0x0F, 0x00, 0x08, 0x3C, 0x00, 0x20, 0xF0, 0x00, 0x81, 0xE0, + 0x04, 0x07, 0x80, 0x30, 0x0F, 0x81, 0x80, 0x1F, 0xFC, 0x00, 0x1F, 0xC0, + 0x00, 0xFF, 0xC0, 0x7F, 0x3E, 0x00, 0x1E, 0x1E, 0x00, 0x0C, 0x0E, 0x00, + 0x18, 0x0F, 0x00, 0x18, 0x07, 0x00, 0x10, 0x07, 0x80, 0x30, 0x07, 0x80, + 0x30, 0x03, 0xC0, 0x60, 0x03, 0xC0, 0x60, 0x01, 0xE0, 0x40, 0x01, 0xE0, + 0xC0, 0x00, 0xF0, 0xC0, 0x00, 0xF1, 0x80, 0x00, 0x71, 0x80, 0x00, 0x7B, + 0x00, 0x00, 0x3B, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0xFF, 0x9F, + 0xF0, 0x3F, 0x9F, 0x03, 0xE0, 0x07, 0x07, 0x80, 0xF0, 0x03, 0x03, 0xC0, + 0x78, 0x01, 0x80, 0xE0, 0x1E, 0x00, 0x80, 0x78, 0x0F, 0x00, 0xC0, 0x1C, + 0x03, 0x80, 0x60, 0x0F, 0x01, 0xE0, 0x20, 0x07, 0x81, 0xF0, 0x30, 0x01, + 0xC0, 0xBC, 0x18, 0x00, 0xF0, 0xDE, 0x08, 0x00, 0x78, 0x67, 0x0C, 0x00, + 0x1E, 0x23, 0xC4, 0x00, 0x0F, 0x31, 0xE6, 0x00, 0x03, 0x90, 0x7B, 0x00, + 0x01, 0xF8, 0x3D, 0x00, 0x00, 0xFC, 0x0F, 0x80, 0x00, 0x3C, 0x07, 0xC0, + 0x00, 0x1E, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0xE0, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x01, 0x80, 0x10, 0x00, 0x00, 0x80, 0x08, 0x00, 0x7F, 0xE0, 0xFF, + 0x0F, 0xC0, 0x1E, 0x03, 0xE0, 0x0E, 0x00, 0xF0, 0x06, 0x00, 0x3C, 0x06, + 0x00, 0x0F, 0x06, 0x00, 0x07, 0x86, 0x00, 0x01, 0xE6, 0x00, 0x00, 0x7B, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x03, + 0xF0, 0x00, 0x03, 0x78, 0x00, 0x01, 0x9E, 0x00, 0x01, 0x87, 0x80, 0x01, + 0x83, 0xE0, 0x01, 0x80, 0xF0, 0x01, 0x80, 0x3C, 0x01, 0x80, 0x1F, 0x01, + 0xC0, 0x07, 0xC1, 0xE0, 0x03, 0xF3, 0xFE, 0x0F, 0xFE, 0xFF, 0xC0, 0xFF, + 0x7E, 0x00, 0x1C, 0x1E, 0x00, 0x18, 0x1F, 0x00, 0x30, 0x0F, 0x00, 0x60, + 0x07, 0x80, 0x60, 0x03, 0xC0, 0xC0, 0x03, 0xE1, 0x80, 0x01, 0xE1, 0x80, + 0x00, 0xF3, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, + 0x00, 0x7E, 0x00, 0x01, 0xFF, 0x80, 0x3F, 0xFF, 0xF1, 0xFF, 0xFF, 0x9C, + 0x00, 0x78, 0xC0, 0x07, 0x84, 0x00, 0x38, 0x00, 0x03, 0xC0, 0x00, 0x3C, + 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, + 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, + 0x07, 0x00, 0x00, 0x78, 0x00, 0x47, 0x80, 0x06, 0x78, 0x00, 0x33, 0x80, + 0x07, 0x3F, 0xFF, 0xFB, 0xFF, 0xFF, 0xC0, 0xFF, 0x83, 0x06, 0x0C, 0x18, + 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, + 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0xF0, 0xC0, 0x18, 0x06, 0x01, + 0x80, 0x70, 0x0C, 0x03, 0x00, 0xE0, 0x18, 0x06, 0x01, 0xC0, 0x30, 0x0C, + 0x03, 0x80, 0x60, 0x18, 0x07, 0x00, 0xC0, 0x30, 0x0E, 0x01, 0x80, 0x60, + 0x1C, 0x03, 0xFE, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, + 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, + 0x0C, 0x1F, 0xF0, 0x03, 0x80, 0x0F, 0x00, 0x1F, 0x00, 0x76, 0x00, 0xCE, + 0x03, 0x8C, 0x06, 0x1C, 0x1C, 0x18, 0x30, 0x30, 0xE0, 0x31, 0x80, 0x67, + 0x00, 0x6C, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xC0, 0xE0, 0x70, + 0x18, 0x0C, 0x03, 0x1F, 0x03, 0x8C, 0x38, 0x31, 0xC1, 0x8E, 0x0C, 0x00, + 0x60, 0x0F, 0x01, 0x98, 0x30, 0xC3, 0x86, 0x38, 0x31, 0xC1, 0x8E, 0x0C, + 0x78, 0xE5, 0xFB, 0xCF, 0x0C, 0x00, 0x00, 0x38, 0x00, 0xF8, 0x00, 0x38, + 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x39, + 0xF0, 0x3B, 0xFC, 0x3C, 0x3E, 0x38, 0x0E, 0x38, 0x0F, 0x38, 0x07, 0x38, + 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x06, 0x38, 0x0E, 0x38, + 0x0C, 0x3C, 0x1C, 0x1F, 0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x7F, 0xE3, 0x87, + 0xD8, 0x0F, 0x60, 0x1B, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, + 0x0E, 0x00, 0x3C, 0x01, 0x78, 0x19, 0xFF, 0xC3, 0xFE, 0x03, 0xE0, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x7C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, + 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x07, 0x9C, 0x1F, 0xDC, 0x38, 0x7C, 0x70, + 0x3C, 0x70, 0x1C, 0x60, 0x1C, 0xE0, 0x1C, 0xE0, 0x1C, 0xE0, 0x1C, 0xE0, + 0x1C, 0xE0, 0x1C, 0xF0, 0x1C, 0x70, 0x1C, 0x7C, 0x3E, 0x3F, 0xDF, 0x0F, + 0x90, 0x0F, 0x81, 0xFF, 0x08, 0x3C, 0x80, 0xE7, 0xFF, 0x7F, 0xFF, 0x00, + 0x18, 0x00, 0xC0, 0x07, 0x00, 0x38, 0x03, 0xE0, 0x37, 0x83, 0x3F, 0xF0, + 0xFF, 0x03, 0xF0, 0x01, 0xF0, 0x3F, 0xC3, 0x8E, 0x18, 0x00, 0xC0, 0x0E, + 0x00, 0x70, 0x03, 0x80, 0x1C, 0x03, 0xFE, 0x1F, 0xF0, 0x38, 0x01, 0xC0, + 0x0E, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, + 0xC0, 0x0E, 0x00, 0x70, 0x07, 0xC0, 0xFF, 0x80, 0x0F, 0xC0, 0x1F, 0xFF, + 0x38, 0xFF, 0x70, 0x70, 0x70, 0x70, 0x70, 0x30, 0x70, 0x30, 0x70, 0x30, + 0x38, 0x20, 0x1C, 0x60, 0x0F, 0x80, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, + 0x7F, 0xE0, 0x3F, 0xFC, 0x1F, 0xFE, 0x20, 0x06, 0x40, 0x02, 0xC0, 0x02, + 0xC0, 0x04, 0xF0, 0x18, 0x7F, 0xF0, 0x1F, 0x80, 0x00, 0x00, 0x38, 0x00, + 0xF8, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, + 0x38, 0x00, 0x38, 0xF0, 0x3B, 0xF8, 0x3E, 0x3C, 0x3C, 0x1C, 0x38, 0x1C, + 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, + 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x7C, 0x3E, 0xFE, 0x7F, 0x18, 0x3C, + 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x7C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x3C, 0xFF, 0x03, 0x03, + 0xC1, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC3, 0xE0, 0x70, + 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, + 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x37, 0x3B, 0xF8, 0xF8, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, + 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x3F, 0x8E, 0x0F, 0x07, 0x06, 0x03, + 0x86, 0x01, 0xC4, 0x00, 0xE4, 0x00, 0x7E, 0x00, 0x3F, 0x80, 0x1D, 0xC0, + 0x0E, 0x70, 0x07, 0x1C, 0x03, 0x8F, 0x01, 0xC3, 0xC0, 0xE0, 0xF0, 0xF8, + 0x3C, 0xFE, 0x7F, 0x80, 0x00, 0x1C, 0x7C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x3C, 0xFF, 0x38, 0xF0, 0x7C, 0x3E, 0xFE, 0x7F, 0x83, + 0xE3, 0xF0, 0xE0, 0xE0, 0x70, 0x1C, 0x38, 0x1C, 0x07, 0x0E, 0x07, 0x01, + 0xC3, 0x81, 0xC0, 0x70, 0xE0, 0x70, 0x1C, 0x38, 0x1C, 0x07, 0x0E, 0x07, + 0x01, 0xC3, 0x81, 0xC0, 0x70, 0xE0, 0x70, 0x1C, 0x38, 0x1C, 0x07, 0x0E, + 0x07, 0x01, 0xC3, 0x81, 0xE0, 0x73, 0xF9, 0xFC, 0x7F, 0x38, 0xF0, 0xFB, + 0xF8, 0x3E, 0x3C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, + 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, + 0x1C, 0x78, 0x3C, 0xFE, 0x7F, 0x07, 0xE0, 0x1F, 0xF8, 0x3C, 0x7C, 0x78, + 0x3E, 0x70, 0x1E, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, + 0x0F, 0xF8, 0x0F, 0x78, 0x0E, 0x7C, 0x1C, 0x3E, 0x3C, 0x0F, 0xF0, 0x07, + 0xC0, 0x18, 0xF0, 0xFB, 0xFC, 0x3E, 0x1E, 0x38, 0x0E, 0x38, 0x0F, 0x38, + 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x06, 0x38, + 0x0E, 0x38, 0x0C, 0x3E, 0x1C, 0x3B, 0xF8, 0x39, 0xE0, 0x38, 0x00, 0x38, + 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x7C, 0x00, 0xFF, + 0x00, 0x07, 0xC4, 0x1F, 0xEC, 0x3C, 0x3C, 0x70, 0x1C, 0x70, 0x1C, 0x60, + 0x1C, 0xE0, 0x1C, 0xE0, 0x1C, 0xE0, 0x1C, 0xE0, 0x1C, 0xE0, 0x1C, 0xF0, + 0x1C, 0x70, 0x1C, 0x78, 0x3C, 0x3F, 0xDC, 0x1F, 0x1C, 0x00, 0x1C, 0x00, + 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x3E, 0x00, + 0xFF, 0x19, 0xFF, 0x7C, 0xF3, 0x9C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, + 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x1F, 0x07, 0xF0, 0x3E, + 0x58, 0x7C, 0x0F, 0x03, 0xC0, 0x7C, 0x07, 0x80, 0xF8, 0x1F, 0x81, 0xF8, + 0x1E, 0x03, 0xC0, 0xF0, 0x3E, 0x1A, 0x7C, 0x10, 0x30, 0x70, 0xFE, 0xFE, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x79, + 0x7E, 0x3C, 0xF8, 0x7C, 0x38, 0x3C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, + 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, + 0x38, 0x1C, 0x38, 0x1C, 0x3C, 0x7C, 0x1F, 0xDF, 0x0F, 0x18, 0xFE, 0x1F, + 0x7C, 0x06, 0x38, 0x04, 0x1C, 0x04, 0x1C, 0x0C, 0x0E, 0x08, 0x0E, 0x18, + 0x07, 0x10, 0x07, 0x10, 0x07, 0x20, 0x03, 0xA0, 0x03, 0xE0, 0x01, 0xC0, + 0x01, 0xC0, 0x00, 0x80, 0x00, 0x80, 0xFC, 0x7F, 0x1F, 0x78, 0x3C, 0x06, + 0x38, 0x1C, 0x04, 0x38, 0x1C, 0x04, 0x1C, 0x1C, 0x0C, 0x1C, 0x0E, 0x08, + 0x1C, 0x1E, 0x18, 0x0E, 0x17, 0x10, 0x0E, 0x37, 0x10, 0x07, 0x23, 0x30, + 0x07, 0x63, 0xA0, 0x07, 0x43, 0xE0, 0x03, 0xC1, 0xC0, 0x03, 0x81, 0xC0, + 0x01, 0x80, 0x80, 0x01, 0x00, 0x80, 0x7F, 0x7E, 0x1E, 0x0C, 0x07, 0x8C, + 0x01, 0xC4, 0x00, 0x76, 0x00, 0x3E, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x03, + 0xE0, 0x01, 0x70, 0x01, 0x1C, 0x01, 0x8F, 0x01, 0x83, 0x80, 0x80, 0xE0, + 0xC0, 0x79, 0xF0, 0xFF, 0xFE, 0x0F, 0x7C, 0x06, 0x38, 0x06, 0x1C, 0x04, + 0x1C, 0x0C, 0x0E, 0x0C, 0x0E, 0x08, 0x0F, 0x18, 0x07, 0x10, 0x07, 0x90, + 0x03, 0xB0, 0x03, 0xA0, 0x01, 0xE0, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0x80, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x03, 0x00, 0x7E, 0x00, + 0x7C, 0x00, 0x78, 0x00, 0x7F, 0xF9, 0xFF, 0xE6, 0x07, 0x10, 0x38, 0x00, + 0xE0, 0x07, 0x00, 0x38, 0x01, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xE0, 0x07, + 0x01, 0x38, 0x0D, 0xC0, 0x3F, 0xFF, 0xBF, 0xFE, 0x07, 0x0E, 0x1C, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x60, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1C, + 0x0E, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x70, 0x38, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x06, 0x06, + 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x38, + 0x70, 0xE0, 0x3E, 0x00, 0x7F, 0x87, 0xE3, 0xFE, 0x00, 0x7C}; + +const GFXglyph FreeSerif18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 9, 0, 1}, // 0x20 ' ' + {0, 4, 24, 12, 5, -23}, // 0x21 '!' + {12, 8, 9, 14, 3, -23}, // 0x22 '"' + {21, 17, 23, 17, 0, -22}, // 0x23 '#' + {70, 13, 27, 17, 2, -24}, // 0x24 '$' + {114, 25, 23, 29, 2, -22}, // 0x25 '%' + {186, 25, 25, 27, 1, -24}, // 0x26 '&' + {265, 3, 9, 7, 2, -23}, // 0x27 ''' + {269, 9, 30, 12, 2, -23}, // 0x28 '(' + {303, 9, 30, 12, 1, -22}, // 0x29 ')' + {337, 12, 14, 18, 3, -23}, // 0x2A '*' + {358, 16, 18, 20, 2, -17}, // 0x2B '+' + {394, 4, 9, 9, 2, -3}, // 0x2C ',' + {399, 8, 2, 12, 1, -8}, // 0x2D '-' + {401, 4, 4, 9, 2, -3}, // 0x2E '.' + {403, 10, 24, 10, 0, -23}, // 0x2F '/' + {433, 16, 24, 18, 1, -23}, // 0x30 '0' + {481, 10, 24, 18, 3, -23}, // 0x31 '1' + {511, 16, 24, 17, 1, -23}, // 0x32 '2' + {559, 13, 24, 17, 2, -23}, // 0x33 '3' + {598, 16, 23, 18, 0, -22}, // 0x34 '4' + {644, 13, 24, 17, 2, -23}, // 0x35 '5' + {683, 16, 24, 18, 1, -23}, // 0x36 '6' + {731, 14, 23, 18, 1, -22}, // 0x37 '7' + {772, 12, 25, 18, 2, -24}, // 0x38 '8' + {810, 16, 26, 17, 1, -24}, // 0x39 '9' + {862, 4, 17, 9, 2, -16}, // 0x3A ':' + {871, 5, 22, 9, 2, -16}, // 0x3B ';' + {885, 18, 18, 20, 1, -17}, // 0x3C '<' + {926, 18, 9, 20, 1, -12}, // 0x3D '=' + {947, 18, 18, 20, 1, -17}, // 0x3E '>' + {988, 13, 25, 16, 2, -24}, // 0x3F '?' + {1029, 24, 25, 30, 3, -24}, // 0x40 '@' + {1104, 24, 23, 25, 1, -22}, // 0x41 'A' + {1173, 20, 23, 22, 1, -22}, // 0x42 'B' + {1231, 22, 24, 23, 1, -23}, // 0x43 'C' + {1297, 23, 23, 25, 1, -22}, // 0x44 'D' + {1364, 20, 23, 21, 2, -22}, // 0x45 'E' + {1422, 17, 23, 20, 2, -22}, // 0x46 'F' + {1471, 23, 24, 25, 1, -23}, // 0x47 'G' + {1540, 22, 23, 25, 2, -22}, // 0x48 'H' + {1604, 9, 23, 11, 2, -22}, // 0x49 'I' + {1630, 12, 23, 13, 0, -22}, // 0x4A 'J' + {1665, 23, 23, 25, 2, -22}, // 0x4B 'K' + {1732, 19, 23, 21, 2, -22}, // 0x4C 'L' + {1787, 29, 23, 31, 1, -22}, // 0x4D 'M' + {1871, 23, 23, 25, 1, -22}, // 0x4E 'N' + {1938, 23, 24, 25, 1, -23}, // 0x4F 'O' + {2007, 18, 23, 20, 1, -22}, // 0x50 'P' + {2059, 23, 30, 25, 1, -23}, // 0x51 'Q' + {2146, 21, 23, 23, 2, -22}, // 0x52 'R' + {2207, 16, 24, 19, 1, -23}, // 0x53 'S' + {2255, 20, 23, 21, 1, -22}, // 0x54 'T' + {2313, 22, 23, 25, 2, -22}, // 0x55 'U' + {2377, 24, 23, 25, 0, -22}, // 0x56 'V' + {2446, 33, 23, 33, 0, -22}, // 0x57 'W' + {2541, 25, 23, 25, 0, -22}, // 0x58 'X' + {2613, 24, 23, 25, 1, -22}, // 0x59 'Y' + {2682, 21, 23, 21, 0, -22}, // 0x5A 'Z' + {2743, 7, 28, 12, 3, -22}, // 0x5B '[' + {2768, 10, 24, 10, 0, -23}, // 0x5C '\' + {2798, 7, 28, 12, 2, -22}, // 0x5D ']' + {2823, 15, 13, 16, 1, -22}, // 0x5E '^' + {2848, 18, 2, 17, 0, 3}, // 0x5F '_' + {2853, 8, 6, 9, 1, -23}, // 0x60 '`' + {2859, 13, 16, 15, 2, -15}, // 0x61 'a' + {2885, 16, 25, 17, 1, -24}, // 0x62 'b' + {2935, 14, 16, 16, 1, -15}, // 0x63 'c' + {2963, 16, 25, 17, 1, -24}, // 0x64 'd' + {3013, 13, 16, 16, 1, -15}, // 0x65 'e' + {3039, 13, 25, 13, 0, -24}, // 0x66 'f' + {3080, 16, 24, 16, 1, -15}, // 0x67 'g' + {3128, 16, 25, 17, 1, -24}, // 0x68 'h' + {3178, 8, 24, 10, 0, -23}, // 0x69 'i' + {3202, 9, 32, 12, 0, -23}, // 0x6A 'j' + {3238, 17, 25, 18, 1, -24}, // 0x6B 'k' + {3292, 8, 25, 9, 0, -24}, // 0x6C 'l' + {3317, 26, 16, 27, 1, -15}, // 0x6D 'm' + {3369, 16, 16, 17, 1, -15}, // 0x6E 'n' + {3401, 16, 16, 17, 1, -15}, // 0x6F 'o' + {3433, 16, 24, 17, 1, -15}, // 0x70 'p' + {3481, 16, 24, 17, 1, -15}, // 0x71 'q' + {3529, 11, 16, 12, 1, -15}, // 0x72 'r' + {3551, 10, 16, 13, 1, -15}, // 0x73 's' + {3571, 8, 19, 10, 2, -18}, // 0x74 't' + {3590, 16, 16, 17, 1, -15}, // 0x75 'u' + {3622, 16, 16, 16, 0, -15}, // 0x76 'v' + {3654, 24, 16, 24, 0, -15}, // 0x77 'w' + {3702, 17, 16, 17, 0, -15}, // 0x78 'x' + {3736, 16, 24, 16, 0, -15}, // 0x79 'y' + {3784, 14, 16, 15, 0, -15}, // 0x7A 'z' + {3812, 8, 30, 17, 3, -23}, // 0x7B '{' + {3842, 2, 24, 7, 2, -23}, // 0x7C '|' + {3848, 8, 30, 17, 6, -22}, // 0x7D '}' + {3878, 16, 4, 17, 1, -10}}; // 0x7E '~' + +const GFXfont FreeSerif18pt7b PROGMEM = {(uint8_t *)FreeSerif18pt7bBitmaps, + (GFXglyph *)FreeSerif18pt7bGlyphs, + 0x20, 0x7E, 42}; + +// Approx. 4558 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerif24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif24pt7b.h new file mode 100644 index 0000000..5cd6d14 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif24pt7b.h @@ -0,0 +1,689 @@ +const uint8_t FreeSerif24pt7bBitmaps[] PROGMEM = { + 0x77, 0xBF, 0xFF, 0xFF, 0xFF, 0xFB, 0x9C, 0xE7, 0x39, 0xCE, 0x61, 0x08, + 0x42, 0x10, 0x84, 0x00, 0x00, 0xEF, 0xFF, 0xEE, 0x60, 0x6F, 0x0F, 0xF0, + 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0x60, 0x66, 0x06, 0x60, 0x66, 0x06, 0x60, + 0x66, 0x06, 0x00, 0xE0, 0x70, 0x01, 0xC0, 0xE0, 0x03, 0x81, 0xC0, 0x07, + 0x03, 0x80, 0x0E, 0x06, 0x00, 0x18, 0x0C, 0x00, 0x30, 0x38, 0x00, 0xE0, + 0x70, 0x01, 0xC0, 0xE0, 0x03, 0x81, 0xC1, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, + 0xF0, 0x18, 0x0C, 0x00, 0x70, 0x38, 0x00, 0xE0, 0x70, 0x01, 0xC0, 0xE0, + 0x03, 0x81, 0xC0, 0x07, 0x03, 0x80, 0x0C, 0x06, 0x07, 0xFF, 0xFF, 0xEF, + 0xFF, 0xFF, 0xC0, 0xE0, 0x70, 0x01, 0xC0, 0xE0, 0x03, 0x81, 0xC0, 0x06, + 0x03, 0x80, 0x0C, 0x06, 0x00, 0x38, 0x1C, 0x00, 0x70, 0x38, 0x00, 0xE0, + 0x70, 0x01, 0xC0, 0xE0, 0x03, 0x81, 0xC0, 0x00, 0x00, 0x40, 0x00, 0x08, + 0x00, 0x01, 0x00, 0x01, 0xFC, 0x01, 0xE4, 0xF8, 0x70, 0x87, 0x9C, 0x10, + 0x77, 0x02, 0x06, 0xE0, 0x40, 0xDC, 0x08, 0x0B, 0x81, 0x00, 0x78, 0x20, + 0x07, 0x84, 0x00, 0xFC, 0x80, 0x0F, 0xF0, 0x00, 0xFE, 0x00, 0x07, 0xF0, + 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x3F, 0xC0, 0x05, 0xFC, 0x00, 0x8F, + 0x80, 0x10, 0xF8, 0x02, 0x0F, 0x00, 0x40, 0xF0, 0x08, 0x1E, 0x01, 0x03, + 0xE0, 0x20, 0x7C, 0x04, 0x0F, 0xC0, 0x83, 0xBC, 0x10, 0xE3, 0xE2, 0x78, + 0x3F, 0xFE, 0x00, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x01, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x01, 0xE0, 0x03, 0xC7, 0x81, 0xE0, + 0x03, 0xC0, 0x7F, 0x60, 0x03, 0xC0, 0x20, 0x70, 0x01, 0xE0, 0x10, 0x30, + 0x01, 0xE0, 0x08, 0x38, 0x00, 0xE0, 0x04, 0x18, 0x00, 0xF0, 0x02, 0x1C, + 0x00, 0x78, 0x02, 0x0C, 0x00, 0x38, 0x01, 0x0E, 0x00, 0x1C, 0x01, 0x86, + 0x00, 0x0E, 0x00, 0x86, 0x00, 0x07, 0x00, 0x87, 0x03, 0xE1, 0x80, 0xC3, + 0x07, 0xFC, 0xE1, 0xC3, 0x87, 0xC6, 0x3F, 0xC1, 0x87, 0x81, 0x8F, 0x81, + 0xC7, 0x80, 0x40, 0x00, 0xC3, 0xC0, 0x20, 0x00, 0xE3, 0xC0, 0x10, 0x00, + 0x61, 0xC0, 0x08, 0x00, 0x61, 0xE0, 0x04, 0x00, 0x70, 0xF0, 0x06, 0x00, + 0x30, 0x70, 0x02, 0x00, 0x38, 0x38, 0x03, 0x00, 0x18, 0x1C, 0x01, 0x00, + 0x1C, 0x0E, 0x01, 0x80, 0x0C, 0x07, 0x01, 0x80, 0x0E, 0x01, 0xC3, 0x80, + 0x06, 0x00, 0x7F, 0x80, 0x06, 0x00, 0x1F, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x70, 0xE0, 0x00, + 0x00, 0xE0, 0x60, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x01, 0xC0, 0x30, 0x00, + 0x01, 0xC0, 0x30, 0x00, 0x01, 0xC0, 0x30, 0x00, 0x01, 0xC0, 0x70, 0x00, + 0x01, 0xE0, 0xE0, 0x00, 0x01, 0xE1, 0xC0, 0x00, 0x00, 0xF3, 0x80, 0x00, + 0x00, 0xFF, 0x0F, 0xFC, 0x00, 0xFC, 0x03, 0xF0, 0x00, 0xF8, 0x01, 0xE0, + 0x01, 0xFC, 0x01, 0xC0, 0x07, 0x7C, 0x01, 0xC0, 0x0F, 0x3E, 0x01, 0x80, + 0x1E, 0x3E, 0x03, 0x00, 0x3C, 0x1F, 0x03, 0x00, 0x7C, 0x1F, 0x06, 0x00, + 0x78, 0x0F, 0x86, 0x00, 0x78, 0x07, 0xCC, 0x00, 0xF8, 0x07, 0xE8, 0x00, + 0xF8, 0x03, 0xF8, 0x00, 0xF8, 0x01, 0xF0, 0x00, 0xF8, 0x01, 0xF8, 0x00, + 0xFC, 0x00, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0x7E, 0x03, 0xBF, 0x86, + 0x7F, 0x0F, 0x1F, 0xFE, 0x3F, 0xFC, 0x0F, 0xF8, 0x0F, 0xE0, 0x03, 0xF0, + 0x6F, 0xFF, 0xFF, 0x66, 0x66, 0x66, 0x00, 0x10, 0x02, 0x00, 0xC0, 0x18, + 0x03, 0x00, 0x60, 0x0E, 0x00, 0xC0, 0x1C, 0x03, 0x80, 0x38, 0x03, 0x80, + 0x78, 0x07, 0x00, 0x70, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, + 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x07, 0x00, 0x70, 0x07, 0x80, + 0x38, 0x03, 0x80, 0x38, 0x01, 0xC0, 0x0C, 0x00, 0xC0, 0x06, 0x00, 0x30, + 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0xC0, 0x06, 0x00, 0x30, 0x01, 0x80, + 0x0C, 0x00, 0x60, 0x07, 0x00, 0x30, 0x03, 0x80, 0x1C, 0x01, 0xC0, 0x1C, + 0x01, 0xE0, 0x0E, 0x00, 0xE0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x1E, + 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x38, 0x03, 0x00, 0x70, 0x0E, 0x00, 0xC0, + 0x18, 0x03, 0x00, 0x40, 0x08, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x43, 0x86, 0xE1, 0x0F, 0xF1, 0x1F, 0xF9, 0x3E, 0x3D, 0x78, 0x07, 0xC0, + 0x01, 0x00, 0x07, 0xC0, 0x19, 0x30, 0xF9, 0x1E, 0xF1, 0x0F, 0xE1, 0x07, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x38, 0x00, 0x00, + 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, + 0x00, 0x00, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, + 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x73, 0xEF, 0xFF, 0x7C, 0x10, 0x42, 0x08, 0xC6, 0x00, + 0xFF, 0xFF, 0xFC, 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0x03, + 0x80, 0x0E, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x38, 0x00, 0xE0, 0x03, + 0x80, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x0E, 0x00, 0x38, 0x01, 0xE0, 0x07, + 0x00, 0x1C, 0x00, 0xF0, 0x03, 0x80, 0x0E, 0x00, 0x78, 0x01, 0xC0, 0x07, + 0x00, 0x3C, 0x00, 0xE0, 0x03, 0x80, 0x1E, 0x00, 0x70, 0x01, 0xC0, 0x0F, + 0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x0E, 0x1C, 0x00, 0x70, 0x38, 0x03, + 0x80, 0x70, 0x1E, 0x01, 0xE0, 0xF0, 0x03, 0x83, 0xC0, 0x0F, 0x0F, 0x00, + 0x3C, 0x7C, 0x00, 0xF9, 0xE0, 0x01, 0xE7, 0x80, 0x07, 0xBE, 0x00, 0x1F, + 0xF8, 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x80, 0x07, 0xFE, 0x00, 0x1F, 0xF8, + 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x80, 0x07, 0xFE, 0x00, 0x1F, 0xF8, 0x00, + 0x7F, 0xE0, 0x01, 0xF7, 0x80, 0x07, 0x9E, 0x00, 0x1E, 0x7C, 0x00, 0xF8, + 0xF0, 0x03, 0xC3, 0xC0, 0x0F, 0x07, 0x00, 0x38, 0x1E, 0x01, 0xE0, 0x38, + 0x07, 0x00, 0x70, 0x38, 0x00, 0xE1, 0xC0, 0x00, 0xFC, 0x00, 0x00, 0x80, + 0x1C, 0x03, 0xE0, 0x7F, 0x0C, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, + 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, + 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, + 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3F, + 0x0F, 0xFF, 0x01, 0xF8, 0x00, 0x3F, 0xF0, 0x07, 0xFF, 0xE0, 0x70, 0x3F, + 0x83, 0x00, 0x7C, 0x30, 0x01, 0xF1, 0x00, 0x0F, 0x98, 0x00, 0x3C, 0x80, + 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0x80, 0x00, 0x1C, + 0x00, 0x01, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, + 0x70, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, + 0x03, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x43, 0x00, + 0x02, 0x30, 0x00, 0x23, 0xFF, 0xFF, 0x3F, 0xFF, 0xF3, 0xFF, 0xFF, 0x80, + 0x03, 0xF8, 0x03, 0xFF, 0x01, 0x83, 0xE0, 0x80, 0x3C, 0x40, 0x0F, 0x10, + 0x01, 0xC8, 0x00, 0x70, 0x00, 0x1C, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, + 0x80, 0x00, 0xC0, 0x00, 0x78, 0x00, 0x7F, 0x80, 0x7F, 0xF0, 0x01, 0xFE, + 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x01, 0xC0, + 0x00, 0x70, 0x00, 0x1C, 0x00, 0x07, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, + 0x30, 0x00, 0x0C, 0x70, 0x06, 0x3F, 0x07, 0x0F, 0xFF, 0x00, 0xFF, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x38, 0x00, 0x01, 0xC0, 0x00, 0x1E, 0x00, 0x01, + 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xDC, 0x00, 0x0C, 0xE0, 0x00, 0x47, 0x00, + 0x06, 0x38, 0x00, 0x61, 0xC0, 0x06, 0x0E, 0x00, 0x30, 0x70, 0x03, 0x03, + 0x80, 0x30, 0x1C, 0x01, 0x80, 0xE0, 0x18, 0x07, 0x01, 0x80, 0x38, 0x08, + 0x01, 0xC0, 0xC0, 0x0E, 0x0C, 0x00, 0x70, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE0, 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, 0x38, 0x00, 0x01, 0xC0, 0x00, + 0x0E, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x40, 0x7F, 0xF8, 0x1F, 0xFE, 0x03, 0xFF, 0xC0, 0xC0, 0x00, 0x18, 0x00, + 0x06, 0x00, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0x07, 0xF8, 0x00, 0xFF, 0xC0, + 0x3F, 0xFE, 0x00, 0xFF, 0xE0, 0x01, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0x7C, + 0x00, 0x07, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x1C, + 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, 0x0E, 0x00, 0x01, 0xC0, 0x00, 0x30, + 0x00, 0x0E, 0x00, 0x01, 0x80, 0x00, 0x71, 0xE0, 0x1C, 0x3F, 0x07, 0x07, + 0xFF, 0x80, 0x3F, 0x80, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x0F, + 0x80, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, + 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, + 0x07, 0xC7, 0xE0, 0x3E, 0xFF, 0xC3, 0xF8, 0x3F, 0x1F, 0x80, 0x7C, 0xF8, + 0x03, 0xF7, 0xC0, 0x0F, 0xBE, 0x00, 0x7F, 0xF0, 0x01, 0xFF, 0x80, 0x0F, + 0xFC, 0x00, 0x7F, 0xE0, 0x03, 0xFF, 0x00, 0x1F, 0x78, 0x00, 0xFB, 0xE0, + 0x07, 0x9F, 0x00, 0x3C, 0x78, 0x03, 0xE3, 0xE0, 0x1E, 0x0F, 0x81, 0xE0, + 0x3E, 0x1E, 0x00, 0xFF, 0xE0, 0x00, 0xFC, 0x00, 0x3F, 0xFF, 0xF3, 0xFF, + 0xFF, 0x3F, 0xFF, 0xE7, 0x00, 0x0E, 0x40, 0x00, 0xEC, 0x00, 0x1C, 0x80, + 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x03, 0x80, + 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x0E, 0x00, 0x00, 0xE0, + 0x00, 0x0E, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x38, + 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x1E, 0x00, 0x01, + 0xC0, 0x00, 0x03, 0xF0, 0x03, 0xFF, 0x03, 0xC1, 0xE0, 0xC0, 0x1C, 0x70, + 0x07, 0x18, 0x00, 0xEE, 0x00, 0x3B, 0x80, 0x0E, 0xF0, 0x03, 0xBC, 0x00, + 0xE7, 0x80, 0x71, 0xF0, 0x38, 0x3E, 0x1C, 0x07, 0xEE, 0x00, 0xFE, 0x00, + 0x1F, 0xC0, 0x03, 0xF8, 0x03, 0xFF, 0x01, 0xC7, 0xE0, 0xE0, 0xFC, 0x70, + 0x0F, 0x98, 0x01, 0xEE, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x01, 0xF8, 0x00, + 0x7E, 0x00, 0x1F, 0xC0, 0x07, 0x70, 0x03, 0x9E, 0x00, 0xE3, 0xE0, 0xF0, + 0x7F, 0xF0, 0x07, 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x3F, 0xF0, 0x03, 0xC3, + 0xE0, 0x3C, 0x0F, 0x83, 0xC0, 0x3C, 0x3E, 0x00, 0xF1, 0xE0, 0x07, 0xCF, + 0x00, 0x3E, 0xF8, 0x00, 0xF7, 0xC0, 0x07, 0xFE, 0x00, 0x3F, 0xF0, 0x01, + 0xFF, 0x80, 0x0F, 0xFC, 0x00, 0x7F, 0xF0, 0x03, 0xEF, 0x80, 0x1F, 0x7C, + 0x00, 0xF9, 0xF0, 0x0F, 0xC7, 0xE1, 0xFC, 0x1F, 0xF9, 0xE0, 0x3F, 0x1F, + 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x78, 0x00, 0x07, 0xC0, 0x00, + 0x7C, 0x00, 0x03, 0xC0, 0x00, 0x3C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, + 0x0F, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x00, 0x77, 0xFF, 0xF7, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xBF, 0xFF, 0xB8, 0x39, 0xF7, + 0xDF, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0xEF, + 0xFF, 0x7C, 0x10, 0x42, 0x08, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x07, + 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xE0, + 0x00, 0x3F, 0x80, 0x00, 0xFE, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF8, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x07, + 0xF8, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0xC0, 0x00, 0xFF, + 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFE, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, + 0x0F, 0xFE, 0x0C, 0x1F, 0x88, 0x03, 0xEC, 0x01, 0xF7, 0x00, 0x7F, 0xC0, + 0x3F, 0xE0, 0x1F, 0x70, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xC0, 0x01, 0xE0, + 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x70, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, + 0x00, 0x18, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x03, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x3F, + 0xFF, 0x00, 0x00, 0xFC, 0x07, 0xC0, 0x01, 0xE0, 0x00, 0xE0, 0x07, 0xC0, + 0x00, 0x30, 0x0F, 0x00, 0x00, 0x18, 0x1E, 0x00, 0x00, 0x0C, 0x1E, 0x00, + 0x00, 0x04, 0x3C, 0x00, 0xF8, 0x06, 0x3C, 0x01, 0xFD, 0xC2, 0x78, 0x03, + 0xC7, 0xC3, 0x78, 0x07, 0x07, 0x81, 0xF0, 0x0E, 0x03, 0x81, 0xF0, 0x0E, + 0x03, 0x81, 0xF0, 0x1C, 0x07, 0x81, 0xF0, 0x1C, 0x07, 0x01, 0xF0, 0x38, + 0x07, 0x01, 0xF0, 0x38, 0x07, 0x03, 0xF0, 0x38, 0x0F, 0x02, 0xF0, 0x38, + 0x0E, 0x02, 0xF0, 0x38, 0x1E, 0x04, 0x78, 0x38, 0x1E, 0x0C, 0x78, 0x1C, + 0x6E, 0x18, 0x38, 0x1F, 0xC7, 0xF0, 0x3C, 0x0F, 0x03, 0xE0, 0x1E, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x60, 0x00, 0xFC, 0x03, 0xE0, 0x00, 0x3F, + 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0D, + 0xF0, 0x00, 0x00, 0x0D, 0xF0, 0x00, 0x00, 0x18, 0xF0, 0x00, 0x00, 0x18, + 0xF8, 0x00, 0x00, 0x38, 0x78, 0x00, 0x00, 0x30, 0x7C, 0x00, 0x00, 0x30, + 0x7C, 0x00, 0x00, 0x60, 0x3E, 0x00, 0x00, 0x60, 0x3E, 0x00, 0x00, 0xE0, + 0x1E, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x01, 0x80, + 0x0F, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0x00, + 0x07, 0xC0, 0x07, 0x00, 0x07, 0xC0, 0x06, 0x00, 0x03, 0xE0, 0x06, 0x00, + 0x03, 0xE0, 0x0E, 0x00, 0x01, 0xF0, 0x0C, 0x00, 0x01, 0xF0, 0x1C, 0x00, + 0x01, 0xF8, 0x3C, 0x00, 0x01, 0xF8, 0x7E, 0x00, 0x01, 0xFC, 0xFF, 0x80, + 0x0F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0x80, 0x1F, 0x01, 0xF8, + 0x03, 0xE0, 0x0F, 0x80, 0x7C, 0x00, 0xF8, 0x0F, 0x80, 0x1F, 0x81, 0xF0, + 0x01, 0xF0, 0x3E, 0x00, 0x3E, 0x07, 0xC0, 0x07, 0xC0, 0xF8, 0x00, 0xF8, + 0x1F, 0x00, 0x1F, 0x03, 0xE0, 0x07, 0xC0, 0x7C, 0x01, 0xF0, 0x0F, 0x80, + 0xFC, 0x01, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xC0, 0x07, 0xC0, 0x7F, 0x00, + 0xF8, 0x01, 0xF0, 0x1F, 0x00, 0x1F, 0x03, 0xE0, 0x03, 0xE0, 0x7C, 0x00, + 0x3E, 0x0F, 0x80, 0x07, 0xC1, 0xF0, 0x00, 0xF8, 0x3E, 0x00, 0x1F, 0x07, + 0xC0, 0x03, 0xE0, 0xF8, 0x00, 0xF8, 0x1F, 0x00, 0x1F, 0x03, 0xE0, 0x07, + 0xC0, 0x7C, 0x07, 0xF0, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x00, 0x00, + 0x1F, 0xF0, 0x20, 0x07, 0xFF, 0xEE, 0x01, 0xF8, 0x1F, 0xE0, 0x3E, 0x00, + 0x7E, 0x07, 0x80, 0x01, 0xE0, 0xF0, 0x00, 0x1E, 0x1F, 0x00, 0x00, 0xE3, + 0xE0, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x67, 0xC0, 0x00, 0x02, 0x7C, 0x00, + 0x00, 0x27, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x80, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x02, 0x0F, 0x80, 0x00, 0xE0, 0x7E, 0x00, 0x1C, 0x03, 0xF8, 0x0F, 0x00, + 0x0F, 0xFF, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, + 0xFF, 0xF8, 0x00, 0x3E, 0x03, 0xFC, 0x00, 0x7C, 0x00, 0xFC, 0x00, 0xF8, + 0x00, 0x7E, 0x01, 0xF0, 0x00, 0x7E, 0x03, 0xE0, 0x00, 0x7C, 0x07, 0xC0, + 0x00, 0x7C, 0x0F, 0x80, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xF8, 0x3E, 0x00, + 0x01, 0xF0, 0x7C, 0x00, 0x03, 0xF0, 0xF8, 0x00, 0x03, 0xE1, 0xF0, 0x00, + 0x07, 0xC3, 0xE0, 0x00, 0x0F, 0x87, 0xC0, 0x00, 0x1F, 0x0F, 0x80, 0x00, + 0x3E, 0x1F, 0x00, 0x00, 0x7C, 0x3E, 0x00, 0x00, 0xF8, 0x7C, 0x00, 0x01, + 0xF0, 0xF8, 0x00, 0x07, 0xC1, 0xF0, 0x00, 0x0F, 0x83, 0xE0, 0x00, 0x1E, + 0x07, 0xC0, 0x00, 0x7C, 0x0F, 0x80, 0x01, 0xF0, 0x1F, 0x00, 0x03, 0xE0, + 0x3E, 0x00, 0x1F, 0x80, 0x7C, 0x00, 0x7C, 0x00, 0xF8, 0x0F, 0xF0, 0x07, + 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x07, + 0xFF, 0xFF, 0xE0, 0x7C, 0x00, 0x1C, 0x0F, 0x80, 0x01, 0x81, 0xF0, 0x00, + 0x30, 0x3E, 0x00, 0x02, 0x07, 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x20, 0x0F, 0x80, 0x04, + 0x01, 0xF0, 0x01, 0x80, 0x3E, 0x00, 0x70, 0x07, 0xFF, 0xFE, 0x00, 0xFF, + 0xFF, 0xC0, 0x1F, 0x00, 0x38, 0x03, 0xE0, 0x03, 0x00, 0x7C, 0x00, 0x20, + 0x0F, 0x80, 0x04, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0x00, 0xF8, 0x00, 0x03, 0x1F, 0x00, 0x00, 0x43, 0xE0, 0x00, 0x18, + 0x7C, 0x00, 0x07, 0x0F, 0x80, 0x01, 0xC1, 0xF0, 0x00, 0xF8, 0x7F, 0xFF, + 0xFF, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x1F, + 0x00, 0x07, 0x1F, 0x00, 0x03, 0x1F, 0x00, 0x03, 0x1F, 0x00, 0x01, 0x1F, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x00, 0x08, 0x1F, 0x00, 0x08, 0x1F, 0x00, 0x18, 0x1F, 0x00, 0x38, 0x1F, + 0xFF, 0xF8, 0x1F, 0xFF, 0xF8, 0x1F, 0x00, 0x38, 0x1F, 0x00, 0x18, 0x1F, + 0x00, 0x08, 0x1F, 0x00, 0x08, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x3F, 0x80, 0x00, 0xFF, + 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x08, 0x00, 0xFF, 0xFE, 0x70, 0x07, 0xE0, + 0x1F, 0xE0, 0x1F, 0x00, 0x0F, 0xC0, 0x78, 0x00, 0x07, 0x81, 0xE0, 0x00, + 0x07, 0x07, 0xC0, 0x00, 0x0E, 0x1F, 0x00, 0x00, 0x0C, 0x3E, 0x00, 0x00, + 0x08, 0xF8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x01, 0xFD, 0xF0, 0x00, 0x01, 0xF3, + 0xE0, 0x00, 0x03, 0xE7, 0xC0, 0x00, 0x07, 0xCF, 0x80, 0x00, 0x0F, 0x8F, + 0x80, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x7C, 0x3E, + 0x00, 0x00, 0xF8, 0x7C, 0x00, 0x01, 0xF0, 0x7C, 0x00, 0x03, 0xE0, 0xFC, + 0x00, 0x07, 0xC0, 0xFC, 0x00, 0x0F, 0x80, 0x7C, 0x00, 0x3F, 0x00, 0x7F, + 0x01, 0xFC, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x0F, 0xF8, 0x00, 0xFF, 0xE0, + 0x1F, 0xFC, 0xFE, 0x00, 0x1F, 0xC1, 0xF0, 0x00, 0x3E, 0x07, 0xC0, 0x00, + 0xF8, 0x1F, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0x3E, + 0x07, 0xC0, 0x00, 0xF8, 0x1F, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x0F, 0x81, + 0xF0, 0x00, 0x3E, 0x07, 0xC0, 0x00, 0xF8, 0x1F, 0x00, 0x03, 0xE0, 0x7C, + 0x00, 0x0F, 0x81, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xF8, 0x1F, 0x00, + 0x03, 0xE0, 0x7C, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0x3E, 0x07, 0xC0, 0x00, + 0xF8, 0x1F, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x0F, 0x81, 0xF0, 0x00, 0x3E, + 0x07, 0xC0, 0x00, 0xF8, 0x1F, 0x00, 0x03, 0xE0, 0x7C, 0x00, 0x0F, 0x81, + 0xF0, 0x00, 0x3E, 0x07, 0xC0, 0x00, 0xF8, 0x1F, 0x00, 0x03, 0xE0, 0xFE, + 0x00, 0x1F, 0xCF, 0xFE, 0x01, 0xFF, 0xC0, 0xFF, 0xF8, 0xFE, 0x03, 0xE0, + 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, + 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, + 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, + 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x0F, 0xE3, 0xFF, 0xE0, 0x0F, 0xFF, + 0x80, 0xFE, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, + 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, + 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, + 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, + 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3C, 0x0E, 0x1E, 0x0F, 0x8F, 0x07, + 0xCF, 0x01, 0xFF, 0x00, 0x7E, 0x00, 0xFF, 0xF8, 0x3F, 0xFC, 0x3F, 0xC0, + 0x07, 0xE0, 0x0F, 0x80, 0x07, 0x80, 0x0F, 0x80, 0x07, 0x00, 0x0F, 0x80, + 0x0E, 0x00, 0x0F, 0x80, 0x1C, 0x00, 0x0F, 0x80, 0x38, 0x00, 0x0F, 0x80, + 0x70, 0x00, 0x0F, 0x80, 0xE0, 0x00, 0x0F, 0x81, 0xC0, 0x00, 0x0F, 0x83, + 0x80, 0x00, 0x0F, 0x87, 0x00, 0x00, 0x0F, 0x9E, 0x00, 0x00, 0x0F, 0xBC, + 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x0F, 0xDF, + 0x80, 0x00, 0x0F, 0x8F, 0xC0, 0x00, 0x0F, 0x87, 0xE0, 0x00, 0x0F, 0x83, + 0xF0, 0x00, 0x0F, 0x81, 0xF8, 0x00, 0x0F, 0x80, 0xFC, 0x00, 0x0F, 0x80, + 0x7E, 0x00, 0x0F, 0x80, 0x3F, 0x00, 0x0F, 0x80, 0x3F, 0x80, 0x0F, 0x80, + 0x1F, 0x80, 0x0F, 0x80, 0x0F, 0xC0, 0x0F, 0x80, 0x07, 0xE0, 0x0F, 0x80, + 0x07, 0xF0, 0x1F, 0xC0, 0x07, 0xFC, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, + 0x00, 0x0F, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x7C, 0x00, 0x01, 0x1F, 0x00, 0x00, 0xC7, 0xC0, 0x00, 0x21, 0xF0, + 0x00, 0x18, 0x7C, 0x00, 0x0E, 0x1F, 0x00, 0x1F, 0x8F, 0xFF, 0xFF, 0xCF, + 0xFF, 0xFF, 0xF0, 0xFF, 0x80, 0x00, 0x03, 0xFE, 0x7F, 0x80, 0x00, 0x07, + 0xF0, 0x3F, 0x00, 0x00, 0x1F, 0xC0, 0x7E, 0x00, 0x00, 0x3F, 0x80, 0xFE, + 0x00, 0x00, 0xFF, 0x01, 0xFC, 0x00, 0x01, 0xBE, 0x03, 0x7C, 0x00, 0x03, + 0x7C, 0x06, 0xF8, 0x00, 0x0E, 0xF8, 0x0D, 0xF8, 0x00, 0x19, 0xF0, 0x19, + 0xF0, 0x00, 0x73, 0xE0, 0x33, 0xF0, 0x00, 0xC7, 0xC0, 0x63, 0xE0, 0x03, + 0x8F, 0x80, 0xC7, 0xE0, 0x06, 0x1F, 0x01, 0x87, 0xC0, 0x1C, 0x3E, 0x03, + 0x0F, 0xC0, 0x30, 0x7C, 0x06, 0x0F, 0x80, 0x60, 0xF8, 0x0C, 0x1F, 0x81, + 0x81, 0xF0, 0x18, 0x1F, 0x03, 0x03, 0xE0, 0x30, 0x3F, 0x0C, 0x07, 0xC0, + 0x60, 0x3E, 0x18, 0x0F, 0x80, 0xC0, 0x7C, 0x70, 0x1F, 0x01, 0x80, 0x7C, + 0xC0, 0x3E, 0x03, 0x00, 0xFB, 0x80, 0x7C, 0x06, 0x00, 0xFE, 0x00, 0xF8, + 0x0C, 0x01, 0xFC, 0x01, 0xF0, 0x18, 0x03, 0xF0, 0x03, 0xE0, 0x30, 0x03, + 0xE0, 0x07, 0xC0, 0x60, 0x07, 0x80, 0x0F, 0x81, 0xE0, 0x07, 0x00, 0x1F, + 0x07, 0xE0, 0x0C, 0x00, 0xFF, 0x3F, 0xF0, 0x08, 0x07, 0xFF, 0x80, 0xFF, + 0x00, 0x03, 0xFF, 0x3F, 0x80, 0x00, 0xFC, 0x1F, 0xC0, 0x00, 0x78, 0x0F, + 0xC0, 0x00, 0x30, 0x0F, 0xE0, 0x00, 0x30, 0x0F, 0xF0, 0x00, 0x30, 0x0D, + 0xF8, 0x00, 0x30, 0x0D, 0xFC, 0x00, 0x30, 0x0C, 0xFC, 0x00, 0x30, 0x0C, + 0x7E, 0x00, 0x30, 0x0C, 0x3F, 0x00, 0x30, 0x0C, 0x1F, 0x80, 0x30, 0x0C, + 0x1F, 0xC0, 0x30, 0x0C, 0x0F, 0xE0, 0x30, 0x0C, 0x07, 0xE0, 0x30, 0x0C, + 0x03, 0xF0, 0x30, 0x0C, 0x01, 0xF8, 0x30, 0x0C, 0x01, 0xFC, 0x30, 0x0C, + 0x00, 0xFE, 0x30, 0x0C, 0x00, 0x7E, 0x30, 0x0C, 0x00, 0x3F, 0x30, 0x0C, + 0x00, 0x1F, 0xB0, 0x0C, 0x00, 0x0F, 0xF0, 0x0C, 0x00, 0x0F, 0xF0, 0x0C, + 0x00, 0x07, 0xF0, 0x0C, 0x00, 0x03, 0xF0, 0x0C, 0x00, 0x01, 0xF0, 0x0C, + 0x00, 0x00, 0xF0, 0x1E, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x70, 0xFF, + 0xC0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1F, 0xE0, 0x00, 0x03, + 0xFF, 0xF0, 0x00, 0x1F, 0x03, 0xE0, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0x80, + 0x07, 0xC0, 0x7C, 0x00, 0x0F, 0x01, 0xE0, 0x00, 0x1E, 0x0F, 0x80, 0x00, + 0x7C, 0x3C, 0x00, 0x00, 0xF1, 0xF0, 0x00, 0x03, 0xE7, 0xC0, 0x00, 0x0F, + 0x9E, 0x00, 0x00, 0x1E, 0xF8, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x01, 0xFF, + 0x80, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x7F, 0xE0, + 0x00, 0x01, 0xFF, 0x80, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x1F, 0xF8, 0x00, + 0x00, 0x7D, 0xF0, 0x00, 0x03, 0xE7, 0xC0, 0x00, 0x0F, 0x9F, 0x00, 0x00, + 0x3E, 0x3C, 0x00, 0x00, 0xF0, 0xF8, 0x00, 0x07, 0xC1, 0xE0, 0x00, 0x1E, + 0x07, 0xC0, 0x00, 0xF8, 0x0F, 0x80, 0x07, 0xC0, 0x1F, 0x00, 0x3E, 0x00, + 0x1F, 0x03, 0xE0, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0xFF, + 0xFF, 0x00, 0x7F, 0xFF, 0x80, 0x7C, 0x1F, 0xC0, 0xF8, 0x07, 0xC1, 0xF0, + 0x07, 0xC3, 0xE0, 0x0F, 0x87, 0xC0, 0x0F, 0x8F, 0x80, 0x1F, 0x1F, 0x00, + 0x3E, 0x3E, 0x00, 0x7C, 0x7C, 0x00, 0xF8, 0xF8, 0x01, 0xF1, 0xF0, 0x07, + 0xC3, 0xE0, 0x0F, 0x87, 0xC0, 0x3E, 0x0F, 0x81, 0xF8, 0x1F, 0xFF, 0xC0, + 0x3F, 0xFE, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, + 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xF0, + 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFF, 0xF8, + 0x00, 0x07, 0xC0, 0xF8, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x7C, + 0x03, 0xE0, 0x00, 0x7C, 0x07, 0x80, 0x00, 0x78, 0x1F, 0x00, 0x00, 0xF8, + 0x3C, 0x00, 0x00, 0xF0, 0xF8, 0x00, 0x01, 0xF1, 0xF0, 0x00, 0x03, 0xE3, + 0xC0, 0x00, 0x03, 0xCF, 0x80, 0x00, 0x07, 0xDF, 0x00, 0x00, 0x0F, 0xBE, + 0x00, 0x00, 0x1F, 0x7C, 0x00, 0x00, 0x3E, 0xF8, 0x00, 0x00, 0x7D, 0xF0, + 0x00, 0x00, 0xFB, 0xE0, 0x00, 0x01, 0xF7, 0xC0, 0x00, 0x03, 0xEF, 0x80, + 0x00, 0x07, 0xCF, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x3E, 0x3E, 0x00, + 0x00, 0x7C, 0x3C, 0x00, 0x01, 0xF0, 0x7C, 0x00, 0x03, 0xE0, 0x78, 0x00, + 0x0F, 0x80, 0x78, 0x00, 0x1E, 0x00, 0x78, 0x00, 0x78, 0x00, 0x7C, 0x03, + 0xE0, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x1F, + 0x03, 0xF8, 0x01, 0xF0, 0x0F, 0x80, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x03, + 0xE0, 0x1F, 0x00, 0x3E, 0x01, 0xF0, 0x03, 0xE0, 0x1F, 0x00, 0x3E, 0x01, + 0xF0, 0x03, 0xE0, 0x1F, 0x00, 0x3E, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, + 0x7C, 0x01, 0xF0, 0x0F, 0x80, 0x1F, 0x07, 0xF0, 0x01, 0xFF, 0xFC, 0x00, + 0x1F, 0xFE, 0x00, 0x01, 0xF1, 0xF0, 0x00, 0x1F, 0x1F, 0x80, 0x01, 0xF0, + 0xF8, 0x00, 0x1F, 0x07, 0xC0, 0x01, 0xF0, 0x3E, 0x00, 0x1F, 0x03, 0xF0, + 0x01, 0xF0, 0x1F, 0x80, 0x1F, 0x00, 0xFC, 0x01, 0xF0, 0x07, 0xC0, 0x1F, + 0x00, 0x7E, 0x01, 0xF0, 0x03, 0xF0, 0x1F, 0x00, 0x1F, 0x83, 0xF8, 0x00, + 0xFC, 0xFF, 0xF0, 0x0F, 0xF0, 0x03, 0xF0, 0x20, 0x7F, 0xF3, 0x07, 0xC1, + 0xF8, 0x78, 0x03, 0xC3, 0x80, 0x0E, 0x3C, 0x00, 0x31, 0xE0, 0x01, 0xCF, + 0x00, 0x06, 0x7C, 0x00, 0x33, 0xE0, 0x01, 0x9F, 0x80, 0x00, 0x7E, 0x00, + 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0xE0, 0x00, 0xFF, 0xC0, 0x01, + 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x0F, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0x7F, + 0x00, 0x01, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, + 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xEF, 0x00, 0x1F, 0x3C, 0x01, 0xF1, + 0xF8, 0x1F, 0x0C, 0xFF, 0xF0, 0x40, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x7C, 0x07, 0xF0, 0x0F, 0x80, 0x3C, 0x01, 0xF0, + 0x07, 0x00, 0x3E, 0x00, 0x60, 0x07, 0xC0, 0x08, 0x00, 0xF8, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, + 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x7F, 0xFC, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0x3F, 0xE0, 0x00, + 0xFC, 0x0F, 0x80, 0x00, 0x78, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, + 0x30, 0x0F, 0x80, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x20, 0x07, 0xC0, 0x00, + 0x60, 0x07, 0xC0, 0x00, 0x60, 0x03, 0xE0, 0x00, 0xC0, 0x03, 0xF0, 0x01, + 0xC0, 0x01, 0xFC, 0x07, 0x80, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0xFF, 0xF8, 0x01, 0xFF, 0x3F, 0xC0, 0x00, 0x7E, 0x0F, 0x80, 0x00, + 0x3C, 0x0F, 0xC0, 0x00, 0x38, 0x07, 0xC0, 0x00, 0x38, 0x07, 0xC0, 0x00, + 0x30, 0x03, 0xE0, 0x00, 0x70, 0x03, 0xE0, 0x00, 0x60, 0x01, 0xF0, 0x00, + 0x60, 0x01, 0xF0, 0x00, 0xE0, 0x01, 0xF8, 0x00, 0xC0, 0x00, 0xF8, 0x01, + 0xC0, 0x00, 0xF8, 0x01, 0x80, 0x00, 0x7C, 0x01, 0x80, 0x00, 0x7C, 0x03, + 0x80, 0x00, 0x3E, 0x03, 0x00, 0x00, 0x3E, 0x07, 0x00, 0x00, 0x1F, 0x06, + 0x00, 0x00, 0x1F, 0x06, 0x00, 0x00, 0x1F, 0x8E, 0x00, 0x00, 0x0F, 0x8C, + 0x00, 0x00, 0x0F, 0x9C, 0x00, 0x00, 0x07, 0xD8, 0x00, 0x00, 0x07, 0xD8, + 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xF1, 0xFF, + 0xF0, 0x1F, 0xF3, 0xF8, 0x07, 0xF8, 0x00, 0x7C, 0x1F, 0x80, 0x3F, 0x00, + 0x03, 0x80, 0xF8, 0x01, 0xF0, 0x00, 0x30, 0x0F, 0x80, 0x1F, 0x00, 0x03, + 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x30, 0x07, 0xC0, 0x0F, 0x80, 0x06, 0x00, + 0x3E, 0x00, 0x7C, 0x00, 0x60, 0x03, 0xE0, 0x07, 0xC0, 0x06, 0x00, 0x3E, + 0x00, 0x7C, 0x00, 0xC0, 0x01, 0xF0, 0x07, 0xE0, 0x0C, 0x00, 0x1F, 0x00, + 0xFE, 0x01, 0xC0, 0x01, 0xF0, 0x0D, 0xE0, 0x18, 0x00, 0x0F, 0x80, 0xDF, + 0x01, 0x80, 0x00, 0xF8, 0x19, 0xF0, 0x30, 0x00, 0x07, 0xC1, 0x8F, 0x83, + 0x00, 0x00, 0x7C, 0x38, 0xF8, 0x30, 0x00, 0x07, 0xC3, 0x0F, 0x86, 0x00, + 0x00, 0x3E, 0x30, 0x7C, 0x60, 0x00, 0x03, 0xE7, 0x07, 0xCE, 0x00, 0x00, + 0x3E, 0x60, 0x3E, 0xC0, 0x00, 0x01, 0xF6, 0x03, 0xEC, 0x00, 0x00, 0x1F, + 0xE0, 0x3F, 0xC0, 0x00, 0x01, 0xFC, 0x01, 0xF8, 0x00, 0x00, 0x0F, 0xC0, + 0x1F, 0x80, 0x00, 0x00, 0xF8, 0x01, 0xF8, 0x00, 0x00, 0x0F, 0x80, 0x0F, + 0x00, 0x00, 0x00, 0x78, 0x00, 0xF0, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x70, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x7F, 0xFE, 0x03, 0xFF, 0x8F, 0xF8, 0x00, + 0x7E, 0x01, 0xFC, 0x00, 0x1C, 0x00, 0x7E, 0x00, 0x1C, 0x00, 0x1F, 0x00, + 0x0C, 0x00, 0x07, 0xC0, 0x0E, 0x00, 0x03, 0xF0, 0x0E, 0x00, 0x00, 0xF8, + 0x0E, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, 0x1F, 0x86, 0x00, 0x00, 0x07, + 0xC7, 0x00, 0x00, 0x01, 0xF7, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x03, 0x9F, 0x00, 0x00, + 0x01, 0x8F, 0xC0, 0x00, 0x01, 0x83, 0xF0, 0x00, 0x01, 0xC0, 0xF8, 0x00, + 0x01, 0xC0, 0x7E, 0x00, 0x01, 0xC0, 0x1F, 0x80, 0x01, 0xC0, 0x07, 0xC0, + 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xE0, 0x00, 0xFC, 0x00, 0xE0, 0x00, 0x7F, + 0x00, 0xF0, 0x00, 0x1F, 0x80, 0xFC, 0x00, 0x1F, 0xF3, 0xFF, 0x80, 0x7F, + 0xFE, 0xFF, 0xF8, 0x03, 0xFF, 0x3F, 0xE0, 0x00, 0x7C, 0x1F, 0xC0, 0x00, + 0x78, 0x0F, 0xC0, 0x00, 0x70, 0x07, 0xE0, 0x00, 0x60, 0x03, 0xF0, 0x00, + 0xE0, 0x01, 0xF0, 0x01, 0xC0, 0x01, 0xF8, 0x01, 0x80, 0x00, 0xFC, 0x03, + 0x80, 0x00, 0x7C, 0x07, 0x00, 0x00, 0x7E, 0x06, 0x00, 0x00, 0x3F, 0x0E, + 0x00, 0x00, 0x1F, 0x1C, 0x00, 0x00, 0x1F, 0x98, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x07, 0xF0, + 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xF8, + 0xF0, 0x00, 0x3E, 0x38, 0x00, 0x0F, 0x86, 0x00, 0x03, 0xF0, 0xC0, 0x00, + 0x7C, 0x10, 0x00, 0x1F, 0x02, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF8, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF8, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x7E, + 0x00, 0x01, 0x0F, 0x80, 0x00, 0x63, 0xF0, 0x00, 0x0C, 0xFC, 0x00, 0x03, + 0xBF, 0x00, 0x00, 0xE7, 0xC0, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, + 0xFF, 0xF0, 0xFF, 0xF0, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, + 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, + 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, + 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0F, 0x07, 0xFC, 0xE0, 0x01, 0xC0, + 0x07, 0x00, 0x1C, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80, 0x07, 0x00, 0x1C, + 0x00, 0x70, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x1C, 0x00, 0x70, 0x01, + 0xC0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, + 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x01, 0xC0, 0x07, 0x00, 0x1E, 0x00, 0x38, + 0x00, 0xE0, 0x03, 0xC0, 0x07, 0xFF, 0x83, 0xC0, 0xE0, 0x70, 0x38, 0x1C, + 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, + 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, + 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x3F, 0xFC, + 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x03, 0xDC, + 0x00, 0x39, 0xC0, 0x07, 0x9E, 0x00, 0x70, 0xE0, 0x0F, 0x0F, 0x00, 0xE0, + 0x70, 0x1E, 0x07, 0x81, 0xC0, 0x38, 0x3C, 0x03, 0xC3, 0x80, 0x1C, 0x78, + 0x01, 0xE7, 0x00, 0x0E, 0xF0, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0, 0x3C, 0x0F, 0x81, 0xF0, 0x1E, 0x03, 0xC0, 0x38, 0x07, 0x03, + 0xF0, 0x07, 0x0E, 0x03, 0x81, 0xC1, 0xE0, 0x30, 0x78, 0x0E, 0x1E, 0x03, + 0x83, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x3E, 0x00, 0x73, 0x80, 0x70, 0xE0, + 0x70, 0x38, 0x38, 0x0E, 0x1C, 0x03, 0x8F, 0x00, 0xE3, 0xC0, 0x38, 0xF0, + 0x0E, 0x3E, 0x07, 0x8F, 0xC3, 0xE1, 0xFF, 0x3F, 0x1F, 0x07, 0x80, 0x06, + 0x00, 0x01, 0xF0, 0x00, 0x3F, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, + 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, + 0x00, 0x07, 0x80, 0x00, 0x3C, 0x7E, 0x01, 0xEF, 0xFC, 0x0F, 0xC3, 0xF0, + 0x7C, 0x07, 0x83, 0xC0, 0x3E, 0x1E, 0x00, 0xF0, 0xF0, 0x07, 0xC7, 0x80, + 0x1E, 0x3C, 0x00, 0xF1, 0xE0, 0x07, 0x8F, 0x00, 0x3C, 0x78, 0x01, 0xE3, + 0xC0, 0x0F, 0x1E, 0x00, 0x70, 0xF0, 0x03, 0x87, 0x80, 0x38, 0x3C, 0x01, + 0xC1, 0xE0, 0x1C, 0x0F, 0xC1, 0xC0, 0x1F, 0xFC, 0x00, 0x3F, 0x80, 0x01, + 0xFC, 0x00, 0xFF, 0xE0, 0x38, 0x3E, 0x0E, 0x03, 0xE3, 0x80, 0x7C, 0xE0, + 0x07, 0x18, 0x00, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, 0x03, 0x80, + 0x00, 0x70, 0x00, 0x0E, 0x00, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x1B, 0xC0, + 0x02, 0x7C, 0x01, 0x87, 0xE0, 0x60, 0x7F, 0xF8, 0x07, 0xFE, 0x00, 0x3F, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x0F, 0x80, 0x00, 0xFE, 0x00, 0x00, 0x78, + 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, + 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x7C, 0x78, 0x07, 0xFD, + 0xE0, 0x3C, 0x3F, 0x81, 0xC0, 0x3E, 0x0E, 0x00, 0xF8, 0x38, 0x01, 0xE1, + 0xE0, 0x07, 0x87, 0x00, 0x1E, 0x3C, 0x00, 0x78, 0xF0, 0x01, 0xE3, 0xC0, + 0x07, 0x8F, 0x00, 0x1E, 0x3C, 0x00, 0x78, 0xF0, 0x01, 0xE3, 0xE0, 0x07, + 0x87, 0x80, 0x1E, 0x1F, 0x00, 0x78, 0x3E, 0x03, 0xE0, 0xFC, 0x1F, 0xF0, + 0xFF, 0xDF, 0x00, 0xFC, 0x60, 0x03, 0xF8, 0x03, 0xFF, 0x01, 0xC1, 0xE0, + 0xC0, 0x3C, 0x70, 0x0F, 0x98, 0x01, 0xE7, 0xFF, 0xFB, 0xFF, 0xFE, 0xE0, + 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, 0x80, 0x00, 0xF0, 0x00, 0x3C, 0x00, + 0x1F, 0x00, 0x05, 0xE0, 0x02, 0x7C, 0x01, 0x8F, 0xC1, 0xC3, 0xFF, 0xE0, + 0x7F, 0xF0, 0x07, 0xF0, 0x00, 0x00, 0x7E, 0x00, 0xFF, 0xC0, 0xE3, 0xE0, + 0x60, 0x70, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x0F, 0x00, + 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x07, 0xFF, 0x83, 0xFF, 0xC0, 0x3C, + 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, + 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, + 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x3F, + 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x3F, 0xF0, 0x03, 0xC7, 0xFE, + 0x3C, 0x1F, 0xF1, 0xC0, 0x70, 0x1E, 0x03, 0xC0, 0xF0, 0x0E, 0x07, 0x80, + 0x70, 0x3C, 0x03, 0x81, 0xE0, 0x1C, 0x07, 0x80, 0xC0, 0x3E, 0x0E, 0x00, + 0x78, 0xE0, 0x01, 0xFC, 0x00, 0x18, 0x00, 0x01, 0x80, 0x00, 0x18, 0x00, + 0x01, 0xE0, 0x00, 0x0F, 0xFF, 0xC0, 0x3F, 0xFF, 0x80, 0xFF, 0xFE, 0x0C, + 0x00, 0x38, 0xC0, 0x00, 0x4C, 0x00, 0x02, 0x60, 0x00, 0x17, 0x00, 0x01, + 0x38, 0x00, 0x09, 0xE0, 0x00, 0x87, 0xC0, 0x38, 0x1F, 0xFF, 0x00, 0x3F, + 0xC0, 0x00, 0x06, 0x00, 0x00, 0xF8, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0x80, + 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, + 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x87, 0xE0, 0x1E, + 0x7F, 0xC0, 0x7B, 0x0F, 0x81, 0xF8, 0x1E, 0x07, 0x80, 0x3C, 0x1E, 0x00, + 0xF0, 0x78, 0x03, 0xC1, 0xE0, 0x0F, 0x07, 0x80, 0x3C, 0x1E, 0x00, 0xF0, + 0x78, 0x03, 0xC1, 0xE0, 0x0F, 0x07, 0x80, 0x3C, 0x1E, 0x00, 0xF0, 0x78, + 0x03, 0xC1, 0xE0, 0x0F, 0x07, 0x80, 0x3C, 0x1E, 0x00, 0xF0, 0x78, 0x03, + 0xC3, 0xF0, 0x1F, 0x9F, 0xF1, 0xFF, 0x0E, 0x03, 0xE0, 0x7C, 0x0F, 0x80, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x70, + 0x7E, 0x1F, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, + 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x07, + 0xE7, 0xFF, 0x00, 0xE0, 0x1F, 0x01, 0xF0, 0x1F, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x70, 0x3F, 0x07, + 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, + 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, + 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xE0, 0x0E, 0xE0, + 0xEF, 0x1C, 0xFF, 0x87, 0xE0, 0x06, 0x00, 0x00, 0x7C, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x80, + 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x78, 0x00, + 0x00, 0xF0, 0x7F, 0xE1, 0xE0, 0x3E, 0x03, 0xC0, 0x70, 0x07, 0x81, 0x80, + 0x0F, 0x06, 0x00, 0x1E, 0x18, 0x00, 0x3C, 0x60, 0x00, 0x79, 0x80, 0x00, + 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x03, 0xDE, 0x00, 0x07, 0x9E, 0x00, 0x0F, + 0x3E, 0x00, 0x1E, 0x3E, 0x00, 0x3C, 0x3E, 0x00, 0x78, 0x3C, 0x00, 0xF0, + 0x3C, 0x01, 0xE0, 0x7C, 0x03, 0xC0, 0x7C, 0x0F, 0xC0, 0xFE, 0x7F, 0xE3, + 0xFF, 0x03, 0x03, 0xE1, 0xFC, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, + 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, + 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, + 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0xE7, 0xFF, 0x1E, 0x1F, 0x01, + 0xF8, 0x1F, 0xCF, 0xF0, 0xFF, 0x80, 0xFF, 0x0F, 0x70, 0xF8, 0x0F, 0x81, + 0xF8, 0x0F, 0x01, 0xE0, 0x1E, 0x00, 0xF0, 0x3C, 0x03, 0xC0, 0x1E, 0x07, + 0x80, 0x78, 0x03, 0xC0, 0xF0, 0x0F, 0x00, 0x78, 0x1E, 0x01, 0xE0, 0x0F, + 0x03, 0xC0, 0x3C, 0x01, 0xE0, 0x78, 0x07, 0x80, 0x3C, 0x0F, 0x00, 0xF0, + 0x07, 0x81, 0xE0, 0x1E, 0x00, 0xF0, 0x3C, 0x03, 0xC0, 0x1E, 0x07, 0x80, + 0x78, 0x03, 0xC0, 0xF0, 0x0F, 0x00, 0x78, 0x1E, 0x01, 0xE0, 0x0F, 0x03, + 0xC0, 0x3C, 0x01, 0xE0, 0x78, 0x07, 0x80, 0x3C, 0x1F, 0x81, 0xF8, 0x0F, + 0xCF, 0xFC, 0xFF, 0xC7, 0xFE, 0x1E, 0x1F, 0x83, 0xF9, 0xFF, 0x03, 0xFC, + 0x3E, 0x07, 0xC0, 0x7C, 0x1E, 0x00, 0xF0, 0x78, 0x03, 0xC1, 0xE0, 0x0F, + 0x07, 0x80, 0x3C, 0x1E, 0x00, 0xF0, 0x78, 0x03, 0xC1, 0xE0, 0x0F, 0x07, + 0x80, 0x3C, 0x1E, 0x00, 0xF0, 0x78, 0x03, 0xC1, 0xE0, 0x0F, 0x07, 0x80, + 0x3C, 0x1E, 0x00, 0xF0, 0x78, 0x03, 0xC1, 0xE0, 0x0F, 0x0F, 0xC0, 0x7E, + 0x7F, 0xC3, 0xFC, 0x01, 0xFE, 0x00, 0x1F, 0xFE, 0x00, 0xF0, 0x7C, 0x0F, + 0x80, 0xF8, 0x3C, 0x01, 0xF1, 0xE0, 0x03, 0xE7, 0x80, 0x0F, 0xBE, 0x00, + 0x3F, 0xF8, 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x80, 0x07, 0xFE, 0x00, 0x1F, + 0xF8, 0x00, 0x7F, 0xF0, 0x01, 0xE7, 0xC0, 0x07, 0x9F, 0x80, 0x3E, 0x3E, + 0x00, 0xF0, 0x7C, 0x07, 0x80, 0xF8, 0x3C, 0x01, 0xFF, 0xE0, 0x00, 0xFC, + 0x00, 0x0E, 0x3F, 0x07, 0xF7, 0xFE, 0x07, 0xE0, 0xF8, 0x3E, 0x03, 0xE1, + 0xE0, 0x0F, 0x0F, 0x00, 0x7C, 0x78, 0x03, 0xE3, 0xC0, 0x0F, 0x1E, 0x00, + 0x78, 0xF0, 0x03, 0xC7, 0x80, 0x1E, 0x3C, 0x00, 0xF1, 0xE0, 0x07, 0x8F, + 0x00, 0x38, 0x78, 0x03, 0xC3, 0xC0, 0x1E, 0x1E, 0x00, 0xE0, 0xF8, 0x0E, + 0x07, 0xE0, 0xE0, 0x3D, 0xFE, 0x01, 0xE7, 0xC0, 0x0F, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, + 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x1F, 0x80, 0x03, 0xFF, 0x80, 0x00, 0x01, + 0xF8, 0x20, 0x3F, 0xF3, 0x03, 0xC1, 0xF8, 0x3C, 0x07, 0xC3, 0xC0, 0x1E, + 0x1C, 0x00, 0xF1, 0xE0, 0x07, 0x8E, 0x00, 0x3C, 0xF0, 0x01, 0xE7, 0x80, + 0x0F, 0x3C, 0x00, 0x79, 0xE0, 0x03, 0xCF, 0x00, 0x1E, 0x78, 0x00, 0xF3, + 0xE0, 0x07, 0x9F, 0x00, 0x3C, 0x7C, 0x01, 0xE3, 0xE0, 0x1F, 0x0F, 0xC1, + 0xF8, 0x3F, 0xF3, 0xC0, 0x7E, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, + 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, + 0xC0, 0x00, 0x1E, 0x00, 0x03, 0xF8, 0x00, 0x7F, 0xE0, 0x06, 0x3C, 0xFC, + 0xFE, 0xFA, 0x78, 0xF8, 0x71, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0F, 0x00, + 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, + 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x01, 0xF8, 0x0F, 0xFC, + 0x00, 0x1F, 0x91, 0x87, 0x98, 0x1D, 0xC0, 0x6E, 0x03, 0x70, 0x0B, 0xC0, + 0x5F, 0x80, 0x7E, 0x01, 0xFC, 0x07, 0xF0, 0x0F, 0xE0, 0x3F, 0x00, 0x7E, + 0x01, 0xF0, 0x07, 0xC0, 0x3E, 0x01, 0xF8, 0x0D, 0xE0, 0xC8, 0xF8, 0x00, + 0x04, 0x00, 0xC0, 0x0C, 0x01, 0xC0, 0x3C, 0x07, 0xFC, 0xFF, 0xC3, 0xC0, + 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xE2, + 0x1F, 0xC0, 0xF8, 0xFC, 0x0F, 0xE1, 0xF0, 0x0F, 0x83, 0xC0, 0x1E, 0x0F, + 0x00, 0x78, 0x3C, 0x01, 0xE0, 0xF0, 0x07, 0x83, 0xC0, 0x1E, 0x0F, 0x00, + 0x78, 0x3C, 0x01, 0xE0, 0xF0, 0x07, 0x83, 0xC0, 0x1E, 0x0F, 0x00, 0x78, + 0x3C, 0x01, 0xE0, 0xF0, 0x07, 0x83, 0xC0, 0x1E, 0x0F, 0x00, 0x78, 0x3C, + 0x01, 0xE0, 0xF8, 0x0F, 0x81, 0xF0, 0xFF, 0x03, 0xFE, 0x7F, 0x07, 0xE1, + 0xC0, 0xFF, 0x81, 0xFC, 0xFC, 0x01, 0xC1, 0xE0, 0x07, 0x07, 0x80, 0x18, + 0x0F, 0x00, 0x60, 0x3C, 0x01, 0x00, 0x78, 0x0C, 0x01, 0xE0, 0x30, 0x07, + 0x81, 0x80, 0x0F, 0x06, 0x00, 0x3C, 0x10, 0x00, 0x78, 0xC0, 0x01, 0xE3, + 0x00, 0x03, 0x98, 0x00, 0x0F, 0x60, 0x00, 0x3D, 0x00, 0x00, 0x7C, 0x00, + 0x01, 0xF0, 0x00, 0x03, 0x80, 0x00, 0x0E, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x40, 0x00, 0xFF, 0x8F, 0xF8, 0x3F, 0x7E, 0x07, 0xE0, 0x0E, 0x3E, 0x03, + 0xC0, 0x0C, 0x1E, 0x03, 0xE0, 0x0C, 0x1E, 0x01, 0xE0, 0x0C, 0x1E, 0x01, + 0xE0, 0x18, 0x0F, 0x00, 0xF0, 0x18, 0x0F, 0x01, 0xF0, 0x10, 0x07, 0x81, + 0xF0, 0x30, 0x07, 0x81, 0x78, 0x30, 0x07, 0x83, 0x78, 0x60, 0x03, 0xC3, + 0x38, 0x60, 0x03, 0xC6, 0x3C, 0x40, 0x01, 0xC6, 0x3C, 0xC0, 0x01, 0xEC, + 0x1E, 0xC0, 0x01, 0xEC, 0x1F, 0x80, 0x00, 0xF8, 0x0F, 0x80, 0x00, 0xF8, + 0x0F, 0x00, 0x00, 0x70, 0x0F, 0x00, 0x00, 0x70, 0x07, 0x00, 0x00, 0x60, + 0x06, 0x00, 0x00, 0x20, 0x02, 0x00, 0x7F, 0xE7, 0xF0, 0x7E, 0x0F, 0x00, + 0xF8, 0x38, 0x01, 0xE0, 0xC0, 0x07, 0xC6, 0x00, 0x0F, 0x30, 0x00, 0x1E, + 0xC0, 0x00, 0x7E, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x07, 0xC0, + 0x00, 0x3F, 0x00, 0x00, 0xDE, 0x00, 0x06, 0x7C, 0x00, 0x30, 0xF0, 0x01, + 0xC1, 0xE0, 0x06, 0x07, 0xC0, 0x30, 0x0F, 0x01, 0xC0, 0x1E, 0x0F, 0x00, + 0xFC, 0xFE, 0x07, 0xFC, 0xFF, 0xC0, 0xFC, 0xFC, 0x01, 0xE1, 0xE0, 0x03, + 0x07, 0x80, 0x18, 0x0F, 0x00, 0x60, 0x3C, 0x01, 0x80, 0x78, 0x0C, 0x01, + 0xE0, 0x30, 0x03, 0xC0, 0xC0, 0x0F, 0x06, 0x00, 0x3E, 0x18, 0x00, 0x78, + 0x40, 0x01, 0xF3, 0x00, 0x03, 0xCC, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0x80, + 0x00, 0x7C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x06, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x40, 0x00, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x60, + 0x00, 0x01, 0x80, 0x00, 0x0C, 0x00, 0x0F, 0xF0, 0x00, 0x7F, 0x80, 0x01, + 0xFC, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7F, 0xFF, 0x9F, 0xFF, 0xE6, 0x00, + 0xF1, 0x00, 0x78, 0x40, 0x3E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0xE0, + 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, + 0xE0, 0x01, 0xF0, 0x04, 0x78, 0x01, 0x3E, 0x00, 0xDF, 0x00, 0x37, 0x80, + 0x1F, 0xFF, 0xFE, 0xFF, 0xFF, 0x80, 0x01, 0xE0, 0x78, 0x1C, 0x07, 0x80, + 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, + 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x70, 0x1C, 0x0E, 0x00, 0x70, + 0x07, 0x00, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, + 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x01, 0xC0, + 0x1E, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, 0x00, 0x70, 0x0F, 0x00, 0xE0, 0x1C, 0x03, + 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, + 0x70, 0x0E, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x0E, 0x07, 0x01, 0xC0, 0x70, + 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, + 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x3C, 0x07, 0x03, 0xC0, 0xF0, 0x00, + 0x1F, 0x80, 0x00, 0xFF, 0x80, 0xC7, 0x0F, 0x87, 0xB8, 0x0F, 0xFC, 0x00, + 0x07, 0xC0}; + +const GFXglyph FreeSerif24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 12, 0, 1}, // 0x20 ' ' + {0, 5, 32, 16, 6, -31}, // 0x21 '!' + {20, 12, 12, 19, 4, -31}, // 0x22 '"' + {38, 23, 31, 23, 0, -30}, // 0x23 '#' + {128, 19, 37, 24, 2, -33}, // 0x24 '$' + {216, 33, 32, 39, 3, -30}, // 0x25 '%' + {348, 32, 33, 37, 2, -31}, // 0x26 '&' + {480, 4, 12, 9, 3, -31}, // 0x27 ''' + {486, 12, 40, 16, 2, -31}, // 0x28 '(' + {546, 12, 40, 16, 2, -30}, // 0x29 ')' + {606, 16, 19, 24, 4, -30}, // 0x2A '*' + {644, 23, 23, 27, 2, -22}, // 0x2B '+' + {711, 6, 11, 12, 2, -4}, // 0x2C ',' + {720, 11, 2, 16, 2, -10}, // 0x2D '-' + {723, 5, 5, 12, 3, -3}, // 0x2E '.' + {727, 14, 32, 14, 0, -30}, // 0x2F '/' + {783, 22, 33, 23, 1, -31}, // 0x30 '0' + {874, 13, 32, 24, 5, -31}, // 0x31 '1' + {926, 21, 31, 23, 1, -30}, // 0x32 '2' + {1008, 18, 32, 23, 2, -30}, // 0x33 '3' + {1080, 21, 31, 24, 1, -30}, // 0x34 '4' + {1162, 19, 33, 24, 2, -31}, // 0x35 '5' + {1241, 21, 33, 23, 2, -31}, // 0x36 '6' + {1328, 20, 31, 24, 1, -30}, // 0x37 '7' + {1406, 18, 33, 23, 3, -31}, // 0x38 '8' + {1481, 21, 33, 24, 1, -31}, // 0x39 '9' + {1568, 5, 22, 12, 4, -20}, // 0x3A ':' + {1582, 6, 27, 12, 3, -20}, // 0x3B ';' + {1603, 24, 25, 27, 1, -24}, // 0x3C '<' + {1678, 24, 11, 27, 1, -16}, // 0x3D '=' + {1711, 24, 25, 27, 2, -23}, // 0x3E '>' + {1786, 17, 32, 21, 3, -31}, // 0x3F '?' + {1854, 32, 33, 41, 4, -31}, // 0x40 '@' + {1986, 32, 32, 34, 1, -31}, // 0x41 'A' + {2114, 27, 31, 30, 0, -30}, // 0x42 'B' + {2219, 28, 33, 31, 2, -31}, // 0x43 'C' + {2335, 31, 31, 34, 1, -30}, // 0x44 'D' + {2456, 27, 31, 29, 2, -30}, // 0x45 'E' + {2561, 24, 31, 27, 2, -30}, // 0x46 'F' + {2654, 31, 33, 35, 2, -31}, // 0x47 'G' + {2782, 30, 31, 34, 2, -30}, // 0x48 'H' + {2899, 13, 31, 15, 1, -30}, // 0x49 'I' + {2950, 17, 32, 18, 0, -30}, // 0x4A 'J' + {3018, 32, 31, 33, 1, -30}, // 0x4B 'K' + {3142, 26, 31, 29, 2, -30}, // 0x4C 'L' + {3243, 39, 31, 41, 1, -30}, // 0x4D 'M' + {3395, 32, 32, 34, 1, -30}, // 0x4E 'N' + {3523, 30, 33, 34, 2, -31}, // 0x4F 'O' + {3647, 23, 31, 27, 2, -30}, // 0x50 'P' + {3737, 31, 40, 34, 2, -31}, // 0x51 'Q' + {3892, 28, 31, 31, 2, -30}, // 0x52 'R' + {4001, 21, 33, 25, 2, -31}, // 0x53 'S' + {4088, 27, 31, 28, 1, -30}, // 0x54 'T' + {4193, 32, 32, 34, 1, -30}, // 0x55 'U' + {4321, 32, 32, 33, 0, -30}, // 0x56 'V' + {4449, 44, 32, 45, 0, -30}, // 0x57 'W' + {4625, 33, 31, 34, 0, -30}, // 0x58 'X' + {4753, 32, 31, 33, 0, -30}, // 0x59 'Y' + {4877, 27, 31, 29, 1, -30}, // 0x5A 'Z' + {4982, 9, 38, 16, 4, -30}, // 0x5B '[' + {5025, 14, 32, 14, 0, -30}, // 0x5C '\' + {5081, 9, 38, 16, 3, -30}, // 0x5D ']' + {5124, 20, 17, 22, 1, -30}, // 0x5E '^' + {5167, 24, 2, 23, 0, 5}, // 0x5F '_' + {5173, 10, 8, 12, 1, -31}, // 0x60 '`' + {5183, 18, 21, 20, 1, -20}, // 0x61 'a' + {5231, 21, 32, 24, 1, -31}, // 0x62 'b' + {5315, 19, 21, 21, 1, -20}, // 0x63 'c' + {5365, 22, 32, 23, 1, -31}, // 0x64 'd' + {5453, 18, 21, 21, 1, -20}, // 0x65 'e' + {5501, 17, 33, 18, 0, -32}, // 0x66 'f' + {5572, 21, 31, 22, 1, -20}, // 0x67 'g' + {5654, 22, 32, 23, 0, -31}, // 0x68 'h' + {5742, 11, 32, 13, 0, -31}, // 0x69 'i' + {5786, 12, 42, 16, 0, -31}, // 0x6A 'j' + {5849, 23, 32, 24, 1, -31}, // 0x6B 'k' + {5941, 11, 32, 12, 0, -31}, // 0x6C 'l' + {5985, 35, 21, 37, 1, -20}, // 0x6D 'm' + {6077, 22, 21, 23, 0, -20}, // 0x6E 'n' + {6135, 22, 21, 23, 1, -20}, // 0x6F 'o' + {6193, 21, 31, 24, 1, -20}, // 0x70 'p' + {6275, 21, 31, 23, 1, -20}, // 0x71 'q' + {6357, 15, 21, 16, 1, -20}, // 0x72 'r' + {6397, 13, 21, 17, 2, -20}, // 0x73 's' + {6432, 12, 26, 13, 1, -25}, // 0x74 't' + {6471, 22, 21, 23, 1, -20}, // 0x75 'u' + {6529, 22, 22, 22, 0, -20}, // 0x76 'v' + {6590, 32, 22, 32, 0, -20}, // 0x77 'w' + {6678, 22, 21, 23, 0, -20}, // 0x78 'x' + {6736, 22, 31, 22, 0, -20}, // 0x79 'y' + {6822, 18, 21, 20, 1, -20}, // 0x7A 'z' + {6870, 11, 41, 23, 5, -31}, // 0x7B '{' + {6927, 3, 32, 9, 3, -30}, // 0x7C '|' + {6939, 11, 41, 23, 7, -31}, // 0x7D '}' + {6996, 22, 5, 23, 1, -13}}; // 0x7E '~' + +const GFXfont FreeSerif24pt7b PROGMEM = {(uint8_t *)FreeSerif24pt7bBitmaps, + (GFXglyph *)FreeSerif24pt7bGlyphs, + 0x20, 0x7E, 56}; + +// Approx. 7682 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerif9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif9pt7b.h new file mode 100644 index 0000000..7acf307 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerif9pt7b.h @@ -0,0 +1,194 @@ +const uint8_t FreeSerif9pt7bBitmaps[] PROGMEM = { + 0xFF, 0xEA, 0x03, 0xDE, 0xF7, 0x20, 0x11, 0x09, 0x04, 0x82, 0x4F, 0xF9, + 0x10, 0x89, 0xFF, 0x24, 0x12, 0x09, 0x0C, 0x80, 0x10, 0x7C, 0xD6, 0xD2, + 0xD0, 0xF0, 0x38, 0x1E, 0x17, 0x93, 0x93, 0xD6, 0x7C, 0x10, 0x38, 0x43, + 0x3C, 0x39, 0x21, 0x8A, 0x0C, 0x50, 0x65, 0x39, 0xCB, 0x20, 0xB9, 0x05, + 0x88, 0x4C, 0x44, 0x64, 0x21, 0xC0, 0x0E, 0x00, 0xC8, 0x06, 0x40, 0x32, + 0x01, 0xA0, 0x07, 0x78, 0x31, 0x87, 0x88, 0x46, 0x86, 0x34, 0x30, 0xC1, + 0xC7, 0x17, 0xCF, 0x00, 0xFE, 0x08, 0x88, 0x84, 0x63, 0x18, 0xC6, 0x10, + 0x82, 0x08, 0x20, 0x82, 0x08, 0x21, 0x0C, 0x63, 0x18, 0xC4, 0x22, 0x22, + 0x00, 0x63, 0x9A, 0xDC, 0x72, 0xB6, 0x08, 0x08, 0x04, 0x02, 0x01, 0x0F, + 0xF8, 0x40, 0x20, 0x10, 0x08, 0x00, 0xD8, 0xF0, 0xF0, 0x08, 0x84, 0x22, + 0x10, 0x8C, 0x42, 0x31, 0x00, 0x1C, 0x31, 0x98, 0xD8, 0x3C, 0x1E, 0x0F, + 0x07, 0x83, 0xC1, 0xE0, 0xD8, 0xC4, 0x61, 0xC0, 0x13, 0x8C, 0x63, 0x18, + 0xC6, 0x31, 0x8C, 0x67, 0x80, 0x3C, 0x4E, 0x86, 0x06, 0x06, 0x04, 0x0C, + 0x08, 0x10, 0x20, 0x41, 0xFE, 0x3C, 0xC6, 0x06, 0x04, 0x1C, 0x3E, 0x07, + 0x03, 0x03, 0x03, 0x06, 0xF8, 0x04, 0x18, 0x71, 0x64, 0xC9, 0xA3, 0x46, + 0xFE, 0x18, 0x30, 0x60, 0x0F, 0x10, 0x20, 0x3C, 0x0E, 0x07, 0x03, 0x03, + 0x03, 0x02, 0x04, 0xF8, 0x07, 0x1C, 0x30, 0x60, 0x60, 0xDC, 0xE6, 0xC3, + 0xC3, 0xC3, 0x43, 0x66, 0x3C, 0x7F, 0x82, 0x02, 0x02, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x10, 0x10, 0x3C, 0x8F, 0x1E, 0x3E, 0x4F, 0x06, 0x36, + 0xC7, 0x8F, 0x1B, 0x33, 0xC0, 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0xC3, + 0x63, 0x3F, 0x06, 0x06, 0x0C, 0x38, 0x60, 0xF0, 0x0F, 0xD8, 0x00, 0x03, + 0x28, 0x01, 0x87, 0x0E, 0x1C, 0x0C, 0x03, 0x80, 0x70, 0x0E, 0x00, 0x80, + 0xFF, 0x80, 0x00, 0x00, 0x0F, 0xF8, 0x80, 0x1C, 0x01, 0xC0, 0x1C, 0x01, + 0xC0, 0xE0, 0xE0, 0xE0, 0xC0, 0x00, 0x79, 0x1A, 0x18, 0x30, 0x60, 0x83, + 0x04, 0x10, 0x20, 0x40, 0x03, 0x00, 0x0F, 0x83, 0x8C, 0x60, 0x26, 0x02, + 0xC7, 0x9C, 0xC9, 0xD8, 0x9D, 0x99, 0xD9, 0x26, 0xEC, 0x60, 0x03, 0x04, + 0x0F, 0x80, 0x02, 0x00, 0x10, 0x01, 0xC0, 0x16, 0x00, 0x98, 0x04, 0xC0, + 0x43, 0x03, 0xF8, 0x20, 0x61, 0x03, 0x18, 0x1D, 0xE1, 0xF0, 0xFF, 0x86, + 0x1C, 0xC1, 0x98, 0x33, 0x0C, 0x7E, 0x0C, 0x31, 0x83, 0x30, 0x66, 0x0C, + 0xC3, 0x7F, 0xC0, 0x1F, 0x26, 0x1D, 0x81, 0xE0, 0x1C, 0x01, 0x80, 0x30, + 0x06, 0x00, 0xC0, 0x0C, 0x00, 0xC1, 0x8F, 0xC0, 0xFF, 0x03, 0x1C, 0x30, + 0x63, 0x07, 0x30, 0x33, 0x03, 0x30, 0x33, 0x03, 0x30, 0x33, 0x06, 0x30, + 0xCF, 0xF0, 0xFF, 0x98, 0x26, 0x01, 0x80, 0x61, 0x1F, 0xC6, 0x11, 0x80, + 0x60, 0x18, 0x16, 0x0F, 0xFE, 0xFF, 0xB0, 0x58, 0x0C, 0x06, 0x13, 0xF9, + 0x84, 0xC0, 0x60, 0x30, 0x18, 0x1E, 0x00, 0x1F, 0x23, 0x0E, 0x60, 0x26, + 0x00, 0xC0, 0x0C, 0x0F, 0xC0, 0x6C, 0x06, 0xC0, 0x66, 0x06, 0x30, 0x60, + 0xF8, 0xF1, 0xEC, 0x19, 0x83, 0x30, 0x66, 0x0C, 0xFF, 0x98, 0x33, 0x06, + 0x60, 0xCC, 0x19, 0x83, 0x78, 0xF0, 0xF6, 0x66, 0x66, 0x66, 0x66, 0x6F, + 0x3C, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x6D, 0xBC, 0xF3, 0xE6, 0x08, + 0x61, 0x06, 0x20, 0x64, 0x07, 0x80, 0x6C, 0x06, 0x60, 0x63, 0x06, 0x18, + 0x60, 0xCF, 0x3F, 0xF0, 0x18, 0x06, 0x01, 0x80, 0x60, 0x18, 0x06, 0x01, + 0x80, 0x60, 0x18, 0x16, 0x0B, 0xFE, 0xF0, 0x0E, 0x70, 0x38, 0xE0, 0x71, + 0xE1, 0x62, 0xC2, 0xC5, 0xC9, 0x89, 0x93, 0x13, 0x26, 0x23, 0x8C, 0x47, + 0x18, 0x84, 0x33, 0x88, 0xF0, 0xE0, 0xEE, 0x09, 0xC1, 0x2C, 0x25, 0xC4, + 0x9C, 0x91, 0x92, 0x1A, 0x41, 0xC8, 0x19, 0x03, 0x70, 0x20, 0x1F, 0x06, + 0x31, 0x83, 0x20, 0x2C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x68, 0x09, + 0x83, 0x18, 0xC1, 0xF0, 0xFE, 0x31, 0x98, 0x6C, 0x36, 0x1B, 0x19, 0xF8, + 0xC0, 0x60, 0x30, 0x18, 0x1E, 0x00, 0x1F, 0x06, 0x31, 0x83, 0x20, 0x2C, + 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x68, 0x19, 0x83, 0x18, 0xC0, 0xE0, + 0x0E, 0x00, 0xE0, 0x07, 0xFE, 0x0C, 0x61, 0x86, 0x30, 0xC6, 0x18, 0xC6, + 0x1F, 0x83, 0x70, 0x67, 0x0C, 0x71, 0x87, 0x78, 0x70, 0x1D, 0x31, 0x98, + 0x4C, 0x07, 0x80, 0xE0, 0x1C, 0x07, 0x01, 0xA0, 0xD8, 0xCB, 0xC0, 0xFF, + 0xF8, 0xCE, 0x18, 0x83, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, + 0xC0, 0x18, 0x07, 0x80, 0xF0, 0xEC, 0x09, 0x81, 0x30, 0x26, 0x04, 0xC0, + 0x98, 0x13, 0x02, 0x60, 0x4C, 0x08, 0xC2, 0x0F, 0x80, 0xF8, 0x77, 0x02, + 0x30, 0x23, 0x04, 0x18, 0x41, 0x84, 0x0C, 0x80, 0xC8, 0x07, 0x00, 0x70, + 0x02, 0x00, 0x20, 0xFB, 0xE7, 0xB0, 0xC0, 0x8C, 0x20, 0x86, 0x18, 0x41, + 0x8C, 0x40, 0xCB, 0x20, 0x65, 0x90, 0x1A, 0x70, 0x0E, 0x38, 0x03, 0x1C, + 0x01, 0x04, 0x00, 0x82, 0x00, 0xFC, 0xF9, 0x83, 0x06, 0x10, 0x19, 0x00, + 0xD0, 0x03, 0x00, 0x1C, 0x01, 0x30, 0x11, 0xC1, 0x86, 0x08, 0x19, 0xE3, + 0xF0, 0xF8, 0xF6, 0x06, 0x30, 0x41, 0x88, 0x1D, 0x00, 0xD0, 0x06, 0x00, + 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0xF0, 0x3F, 0xCC, 0x11, 0x06, 0x01, + 0x80, 0x70, 0x0C, 0x03, 0x00, 0xE0, 0x38, 0x06, 0x05, 0xC1, 0x7F, 0xE0, + 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB8, 0x82, 0x10, 0x82, 0x10, 0x86, 0x10, + 0x86, 0x10, 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xF8, 0x18, 0x1C, 0x34, 0x26, + 0x62, 0x42, 0xC1, 0xFF, 0x80, 0x84, 0x20, 0x79, 0x98, 0x30, 0xE6, 0xD9, + 0xB3, 0x3F, 0x20, 0x70, 0x18, 0x0C, 0x06, 0x03, 0x71, 0xCC, 0xC3, 0x61, + 0xB0, 0xD8, 0x6C, 0x63, 0xE0, 0x3C, 0xCF, 0x06, 0x0C, 0x18, 0x18, 0x9E, + 0x01, 0x03, 0x80, 0xC0, 0x60, 0x31, 0xD9, 0x9D, 0x86, 0xC3, 0x61, 0xB0, + 0xCC, 0x63, 0xF0, 0x3C, 0x46, 0xFE, 0xC0, 0xC0, 0xE1, 0x62, 0x3C, 0x1E, + 0x41, 0x83, 0x06, 0x1E, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x0F, 0x00, 0x3C, + 0x19, 0xF6, 0x31, 0x8C, 0x1E, 0x08, 0x04, 0x01, 0xFC, 0x40, 0xB0, 0x2E, + 0x11, 0xF8, 0x20, 0x70, 0x18, 0x0C, 0x06, 0x03, 0x71, 0xCC, 0xC6, 0x63, + 0x31, 0x98, 0xCC, 0x6F, 0x78, 0x60, 0x02, 0xE6, 0x66, 0x66, 0xF0, 0x18, + 0x00, 0x33, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8B, 0x80, 0x20, 0x70, 0x18, + 0x0C, 0x06, 0x03, 0x3D, 0x88, 0xD8, 0x78, 0x36, 0x19, 0x8C, 0x6F, 0x78, + 0x2E, 0x66, 0x66, 0x66, 0x66, 0x66, 0xF0, 0xEE, 0x71, 0xCE, 0x66, 0x31, + 0x98, 0xC6, 0x63, 0x19, 0x8C, 0x66, 0x31, 0xBD, 0xEF, 0xEE, 0x39, 0x98, + 0xCC, 0x66, 0x33, 0x19, 0x8D, 0xEF, 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1E, + 0x0D, 0x8C, 0x7C, 0xEE, 0x39, 0x98, 0x6C, 0x36, 0x1B, 0x0D, 0x8C, 0xFC, + 0x60, 0x30, 0x18, 0x1E, 0x00, 0x3D, 0x31, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, + 0x8C, 0xFE, 0x03, 0x01, 0x80, 0xC0, 0xF0, 0x6D, 0xC6, 0x18, 0x61, 0x86, + 0x3C, 0x76, 0x38, 0x58, 0x3E, 0x38, 0xFE, 0x27, 0x98, 0xC6, 0x31, 0x8C, + 0x38, 0xE7, 0x31, 0x98, 0xCC, 0x66, 0x33, 0x19, 0x8C, 0x7F, 0xF3, 0x61, + 0x22, 0x32, 0x14, 0x1C, 0x08, 0x08, 0xEF, 0x36, 0x61, 0x62, 0x22, 0x32, + 0x35, 0x41, 0x9C, 0x18, 0x81, 0x08, 0xF7, 0x12, 0x0E, 0x03, 0x01, 0xC1, + 0x21, 0x09, 0xCF, 0xF3, 0x61, 0x62, 0x32, 0x34, 0x14, 0x1C, 0x08, 0x08, + 0x08, 0x10, 0xE0, 0xFD, 0x18, 0x60, 0x83, 0x0C, 0x70, 0xFE, 0x19, 0x8C, + 0x63, 0x18, 0xC4, 0x61, 0x8C, 0x63, 0x18, 0xC3, 0xFF, 0xF0, 0xC3, 0x18, + 0xC6, 0x31, 0x84, 0x33, 0x18, 0xC6, 0x31, 0x98, 0x70, 0x24, 0xC1, 0xC0}; + +const GFXglyph FreeSerif9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 2, 12, 6, 2, -11}, // 0x21 '!' + {3, 5, 4, 7, 1, -11}, // 0x22 '"' + {6, 9, 12, 9, 0, -11}, // 0x23 '#' + {20, 8, 14, 9, 1, -12}, // 0x24 '$' + {34, 13, 12, 15, 1, -11}, // 0x25 '%' + {54, 13, 13, 14, 1, -12}, // 0x26 '&' + {76, 2, 4, 4, 1, -11}, // 0x27 ''' + {77, 5, 15, 6, 1, -11}, // 0x28 '(' + {87, 5, 15, 6, 0, -11}, // 0x29 ')' + {97, 6, 8, 9, 3, -11}, // 0x2A '*' + {103, 9, 9, 10, 0, -8}, // 0x2B '+' + {114, 2, 3, 4, 2, 0}, // 0x2C ',' + {115, 4, 1, 6, 1, -3}, // 0x2D '-' + {116, 2, 2, 5, 1, -1}, // 0x2E '.' + {117, 5, 12, 5, 0, -11}, // 0x2F '/' + {125, 9, 13, 9, 0, -12}, // 0x30 '0' + {140, 5, 13, 9, 2, -12}, // 0x31 '1' + {149, 8, 12, 9, 1, -11}, // 0x32 '2' + {161, 8, 12, 9, 0, -11}, // 0x33 '3' + {173, 7, 12, 9, 1, -11}, // 0x34 '4' + {184, 8, 12, 9, 0, -11}, // 0x35 '5' + {196, 8, 13, 9, 1, -12}, // 0x36 '6' + {209, 8, 12, 9, 0, -11}, // 0x37 '7' + {221, 7, 13, 9, 1, -12}, // 0x38 '8' + {233, 8, 14, 9, 1, -12}, // 0x39 '9' + {247, 2, 8, 5, 1, -7}, // 0x3A ':' + {249, 3, 10, 5, 1, -7}, // 0x3B ';' + {253, 9, 9, 10, 1, -8}, // 0x3C '<' + {264, 9, 5, 10, 1, -6}, // 0x3D '=' + {270, 10, 9, 10, 0, -8}, // 0x3E '>' + {282, 7, 13, 8, 1, -12}, // 0x3F '?' + {294, 12, 13, 16, 2, -12}, // 0x40 '@' + {314, 13, 12, 13, 0, -11}, // 0x41 'A' + {334, 11, 12, 11, 0, -11}, // 0x42 'B' + {351, 11, 12, 12, 1, -11}, // 0x43 'C' + {368, 12, 12, 13, 0, -11}, // 0x44 'D' + {386, 10, 12, 11, 1, -11}, // 0x45 'E' + {401, 9, 12, 10, 1, -11}, // 0x46 'F' + {415, 12, 12, 13, 1, -11}, // 0x47 'G' + {433, 11, 12, 13, 1, -11}, // 0x48 'H' + {450, 4, 12, 6, 1, -11}, // 0x49 'I' + {456, 6, 12, 7, 0, -11}, // 0x4A 'J' + {465, 12, 12, 13, 1, -11}, // 0x4B 'K' + {483, 10, 12, 11, 1, -11}, // 0x4C 'L' + {498, 15, 12, 16, 0, -11}, // 0x4D 'M' + {521, 11, 12, 13, 1, -11}, // 0x4E 'N' + {538, 11, 13, 13, 1, -12}, // 0x4F 'O' + {556, 9, 12, 10, 1, -11}, // 0x50 'P' + {570, 11, 16, 13, 1, -12}, // 0x51 'Q' + {592, 11, 12, 12, 1, -11}, // 0x52 'R' + {609, 9, 12, 10, 0, -11}, // 0x53 'S' + {623, 11, 12, 11, 0, -11}, // 0x54 'T' + {640, 11, 12, 13, 1, -11}, // 0x55 'U' + {657, 12, 12, 13, 0, -11}, // 0x56 'V' + {675, 17, 12, 17, 0, -11}, // 0x57 'W' + {701, 13, 12, 13, 0, -11}, // 0x58 'X' + {721, 12, 12, 13, 0, -11}, // 0x59 'Y' + {739, 11, 12, 11, 0, -11}, // 0x5A 'Z' + {756, 3, 15, 6, 2, -11}, // 0x5B '[' + {762, 5, 12, 5, 0, -11}, // 0x5C '\' + {770, 3, 15, 6, 1, -11}, // 0x5D ']' + {776, 8, 7, 8, 0, -11}, // 0x5E '^' + {783, 9, 1, 9, 0, 2}, // 0x5F '_' + {785, 4, 3, 5, 0, -11}, // 0x60 '`' + {787, 7, 8, 8, 1, -7}, // 0x61 'a' + {794, 9, 13, 9, 0, -12}, // 0x62 'b' + {809, 7, 8, 8, 0, -7}, // 0x63 'c' + {816, 9, 13, 9, 0, -12}, // 0x64 'd' + {831, 8, 8, 8, 0, -7}, // 0x65 'e' + {839, 7, 13, 7, 1, -12}, // 0x66 'f' + {851, 10, 12, 8, 0, -7}, // 0x67 'g' + {866, 9, 13, 9, 0, -12}, // 0x68 'h' + {881, 4, 11, 5, 1, -10}, // 0x69 'i' + {887, 5, 15, 6, 0, -10}, // 0x6A 'j' + {897, 9, 13, 9, 1, -12}, // 0x6B 'k' + {912, 4, 13, 5, 1, -12}, // 0x6C 'l' + {919, 14, 8, 14, 0, -7}, // 0x6D 'm' + {933, 9, 8, 9, 0, -7}, // 0x6E 'n' + {942, 9, 8, 9, 0, -7}, // 0x6F 'o' + {951, 9, 12, 9, 0, -7}, // 0x70 'p' + {965, 9, 12, 9, 0, -7}, // 0x71 'q' + {979, 6, 8, 6, 0, -7}, // 0x72 'r' + {985, 6, 8, 7, 1, -7}, // 0x73 's' + {991, 5, 9, 5, 0, -8}, // 0x74 't' + {997, 9, 8, 9, 0, -7}, // 0x75 'u' + {1006, 8, 8, 8, 0, -7}, // 0x76 'v' + {1014, 12, 8, 12, 0, -7}, // 0x77 'w' + {1026, 9, 8, 9, 0, -7}, // 0x78 'x' + {1035, 8, 12, 8, 0, -7}, // 0x79 'y' + {1047, 7, 8, 7, 1, -7}, // 0x7A 'z' + {1054, 5, 16, 9, 1, -12}, // 0x7B '{' + {1064, 1, 12, 4, 1, -11}, // 0x7C '|' + {1066, 5, 16, 9, 3, -11}, // 0x7D '}' + {1076, 9, 3, 9, 0, -5}}; // 0x7E '~' + +const GFXfont FreeSerif9pt7b PROGMEM = {(uint8_t *)FreeSerif9pt7bBitmaps, + (GFXglyph *)FreeSerif9pt7bGlyphs, 0x20, + 0x7E, 22}; + +// Approx. 1752 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold12pt7b.h new file mode 100644 index 0000000..7b4b0c9 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold12pt7b.h @@ -0,0 +1,270 @@ +const uint8_t FreeSerifBold12pt7bBitmaps[] PROGMEM = { + 0x7F, 0xFF, 0x77, 0x66, 0x22, 0x00, 0x6F, 0xF7, 0xE3, 0xF1, 0xF8, 0xFC, + 0x7E, 0x3A, 0x09, 0x04, 0x0C, 0x40, 0xCC, 0x0C, 0xC0, 0x8C, 0x18, 0xC7, + 0xFF, 0x18, 0xC1, 0x88, 0x19, 0x81, 0x98, 0xFF, 0xE3, 0x18, 0x31, 0x83, + 0x18, 0x33, 0x03, 0x30, 0x08, 0x01, 0x00, 0xFC, 0x24, 0xEC, 0x8D, 0x90, + 0xBA, 0x07, 0xC0, 0x7E, 0x07, 0xF0, 0x7F, 0x07, 0xF0, 0x9F, 0x11, 0xE2, + 0x3E, 0x46, 0xE9, 0xC7, 0xC0, 0x20, 0x04, 0x00, 0x1E, 0x0C, 0x0E, 0x7F, + 0x07, 0x10, 0x83, 0xC4, 0x40, 0xE1, 0x30, 0x38, 0x88, 0x0E, 0x26, 0x03, + 0x91, 0x1E, 0x78, 0x8E, 0x40, 0x27, 0x10, 0x11, 0xC4, 0x0C, 0xE1, 0x02, + 0x38, 0x81, 0x0E, 0x20, 0x43, 0x90, 0x20, 0x78, 0x03, 0xE0, 0x01, 0x9E, + 0x00, 0xE3, 0x80, 0x38, 0xE0, 0x0F, 0x30, 0x03, 0xF0, 0x00, 0x78, 0x7C, + 0x1F, 0x06, 0x1B, 0xE1, 0x1C, 0x7C, 0x8F, 0x1F, 0x23, 0xC3, 0xF0, 0xF8, + 0x7C, 0x3E, 0x0F, 0x97, 0xC7, 0xFC, 0xFE, 0x3E, 0xFF, 0xFE, 0x90, 0x00, + 0x31, 0x0C, 0x31, 0x86, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0x86, 0x18, 0x60, + 0xC1, 0x02, 0x04, 0x03, 0x06, 0x0C, 0x30, 0x61, 0x87, 0x1C, 0x71, 0xC7, + 0x1C, 0x71, 0x86, 0x38, 0xC2, 0x10, 0x80, 0x1C, 0x6E, 0xFA, 0xEF, 0xF1, + 0xC7, 0xFF, 0xAF, 0xBB, 0x1C, 0x04, 0x00, 0x06, 0x00, 0x60, 0x06, 0x00, + 0x60, 0x06, 0x0F, 0xFF, 0xFF, 0xF0, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, + 0x60, 0x6F, 0xF7, 0x11, 0x24, 0xFF, 0xFF, 0xC0, 0x6F, 0xF6, 0x03, 0x07, + 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x18, 0x18, 0x18, 0x30, 0x30, 0x30, 0x60, + 0x60, 0x60, 0xC0, 0x0E, 0x07, 0x71, 0xC7, 0x38, 0xEF, 0x1D, 0xE3, 0xFC, + 0x7F, 0x8F, 0xF1, 0xFE, 0x3F, 0xC7, 0xF8, 0xF7, 0x1C, 0xE3, 0x8E, 0xE0, + 0xF8, 0x06, 0x0F, 0x1F, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, + 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0xF9, 0xFF, 0x0F, 0x03, 0xFC, 0x7F, 0xC4, + 0x3E, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x06, 0x00, + 0xC1, 0x18, 0x13, 0xFE, 0x7F, 0xEF, 0xFE, 0x1F, 0x0C, 0xFA, 0x0F, 0x01, + 0xE0, 0x38, 0x0E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3C, 0x03, 0x80, 0x70, + 0x0D, 0xC1, 0xBC, 0x43, 0xF0, 0x03, 0x80, 0xE0, 0x78, 0x3E, 0x17, 0x89, + 0xE2, 0x79, 0x1E, 0x87, 0xA1, 0xEF, 0xFF, 0xFF, 0xFF, 0xC1, 0xE0, 0x78, + 0x1E, 0x3F, 0xE7, 0xF8, 0xFF, 0x10, 0x04, 0x00, 0xF8, 0x1F, 0xC7, 0xFC, + 0x1F, 0xC0, 0x78, 0x07, 0x00, 0x60, 0x0D, 0xC1, 0x3C, 0x43, 0xF0, 0x00, + 0xE0, 0xF0, 0x38, 0x1E, 0x07, 0x80, 0xF0, 0x3F, 0xE7, 0x9E, 0xF1, 0xFE, + 0x3F, 0xC7, 0xF8, 0xF7, 0x1E, 0xE3, 0x8E, 0x60, 0xF8, 0x7F, 0xEF, 0xFD, + 0xFF, 0xA0, 0x68, 0x0C, 0x03, 0x80, 0x60, 0x0C, 0x03, 0x00, 0x60, 0x0C, + 0x03, 0x00, 0x60, 0x1C, 0x03, 0x00, 0x60, 0x1F, 0x0E, 0x73, 0x87, 0x70, + 0xEF, 0x1D, 0xF3, 0x1F, 0x81, 0xF8, 0x1F, 0xCC, 0xFB, 0x8F, 0xF0, 0xFE, + 0x1F, 0xC3, 0x9C, 0xF1, 0xF8, 0x1F, 0x06, 0x71, 0xC7, 0x78, 0xEF, 0x1F, + 0xE3, 0xFC, 0x7F, 0x8F, 0x79, 0xE7, 0xFC, 0x0F, 0x01, 0xC0, 0x78, 0x1C, + 0x0F, 0x07, 0x00, 0x6F, 0xF6, 0x00, 0x06, 0xFF, 0x60, 0x6F, 0xF6, 0x00, + 0x06, 0xFF, 0x71, 0x22, 0xC0, 0x00, 0x04, 0x00, 0x70, 0x07, 0xC0, 0xFC, + 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0x1F, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F, + 0x00, 0x1F, 0x00, 0x1C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0x80, 0x0F, + 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x0F, 0x80, 0x1F, 0x01, 0xF0, + 0x1F, 0x03, 0xF0, 0x3E, 0x00, 0xE0, 0x02, 0x00, 0x00, 0x3E, 0x11, 0xEC, + 0x3F, 0x8F, 0xE3, 0xC0, 0xF0, 0x78, 0x18, 0x08, 0x02, 0x00, 0x00, 0x00, + 0x1C, 0x07, 0x81, 0xE0, 0x30, 0x03, 0xF0, 0x0E, 0x18, 0x18, 0x04, 0x30, + 0x66, 0x70, 0xDB, 0x61, 0x99, 0xE3, 0x19, 0xE3, 0x31, 0xE6, 0x31, 0xE6, + 0x31, 0xE6, 0xF2, 0x66, 0xB2, 0x73, 0x3C, 0x38, 0x00, 0x1E, 0x04, 0x03, + 0xF8, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x1B, 0xC0, 0x09, 0xE0, 0x0C, 0xF8, 0x04, 0x3C, 0x02, 0x1F, + 0x03, 0xFF, 0x81, 0x03, 0xC1, 0x80, 0xF0, 0x80, 0x7D, 0xF0, 0xFF, 0xFF, + 0xC0, 0xF3, 0xC3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xCE, 0x0F, + 0xF0, 0x3C, 0x70, 0xF0, 0xE3, 0xC3, 0xCF, 0x0F, 0x3C, 0x3C, 0xF0, 0xE3, + 0xC7, 0xBF, 0xF8, 0x07, 0xE2, 0x38, 0x7C, 0xE0, 0x3B, 0xC0, 0x37, 0x00, + 0x7E, 0x00, 0x7C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x03, + 0x80, 0x07, 0x80, 0x27, 0x00, 0xC7, 0x86, 0x03, 0xF0, 0xFF, 0xE0, 0x1E, + 0x1E, 0x0F, 0x07, 0x87, 0x81, 0xE3, 0xC0, 0xF1, 0xE0, 0x3C, 0xF0, 0x1E, + 0x78, 0x0F, 0x3C, 0x07, 0x9E, 0x03, 0xCF, 0x01, 0xE7, 0x80, 0xE3, 0xC0, + 0xF1, 0xE0, 0xF0, 0xF0, 0xE1, 0xFF, 0xC0, 0xFF, 0xFC, 0x78, 0x38, 0xF0, + 0x31, 0xE0, 0x23, 0xC4, 0x07, 0x88, 0x0F, 0x30, 0x1F, 0xE0, 0x3C, 0xC0, + 0x78, 0x80, 0xF1, 0x01, 0xE0, 0x23, 0xC0, 0x47, 0x81, 0x8F, 0x07, 0x7F, + 0xFE, 0xFF, 0xFC, 0xF0, 0x73, 0xC0, 0xCF, 0x01, 0x3C, 0x40, 0xF1, 0x03, + 0xCC, 0x0F, 0xF0, 0x3C, 0xC0, 0xF1, 0x03, 0xC4, 0x0F, 0x00, 0x3C, 0x00, + 0xF0, 0x03, 0xC0, 0x3F, 0xC0, 0x07, 0xE2, 0x1C, 0x3E, 0x38, 0x0E, 0x78, + 0x06, 0x70, 0x06, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, + 0x7F, 0xF0, 0x1E, 0x70, 0x1E, 0x78, 0x1E, 0x38, 0x1E, 0x1E, 0x1E, 0x07, + 0xF0, 0xFE, 0xFF, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78, + 0x3C, 0x78, 0x3C, 0x7F, 0xFC, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78, + 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0xFE, 0xFF, 0xFF, 0x3C, 0x3C, + 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, + 0xFF, 0x0F, 0xF0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, + 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0xE3, 0xCE, + 0x38, 0xE3, 0x83, 0xE0, 0xFE, 0x7F, 0x3C, 0x0E, 0x1E, 0x04, 0x0F, 0x04, + 0x07, 0x84, 0x03, 0xCC, 0x01, 0xEE, 0x00, 0xFF, 0x00, 0x7F, 0xC0, 0x3C, + 0xF0, 0x1E, 0x7C, 0x0F, 0x1F, 0x07, 0x87, 0xC3, 0xC1, 0xF1, 0xE0, 0x7D, + 0xFC, 0xFF, 0xFE, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, + 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x01, 0x78, + 0x0D, 0xE0, 0x67, 0x83, 0xBF, 0xFE, 0xFC, 0x01, 0xF3, 0xC0, 0x3E, 0x3E, + 0x03, 0xE2, 0xE0, 0x5E, 0x2F, 0x05, 0xE2, 0xF0, 0x5E, 0x27, 0x09, 0xE2, + 0x78, 0x9E, 0x23, 0x91, 0xE2, 0x3D, 0x1E, 0x23, 0xF1, 0xE2, 0x1E, 0x1E, + 0x21, 0xE1, 0xE2, 0x0C, 0x1E, 0x20, 0xC1, 0xEF, 0x88, 0x3F, 0xF8, 0x1E, + 0xF8, 0x18, 0xF8, 0x11, 0xF8, 0x22, 0xF8, 0x45, 0xF0, 0x89, 0xF1, 0x11, + 0xF2, 0x21, 0xF4, 0x41, 0xF8, 0x81, 0xF1, 0x01, 0xE2, 0x03, 0xC4, 0x03, + 0x8C, 0x03, 0x7C, 0x02, 0x07, 0xF0, 0x0F, 0x1E, 0x0E, 0x03, 0x8F, 0x01, + 0xE7, 0x00, 0x77, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, + 0x03, 0xFC, 0x01, 0xEE, 0x00, 0xE7, 0x80, 0xF1, 0xC0, 0x70, 0x70, 0x70, + 0x0F, 0xE0, 0xFF, 0x87, 0x9E, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, 0x78, + 0xF7, 0x9E, 0x7F, 0x87, 0x80, 0x78, 0x07, 0x80, 0x78, 0x07, 0x80, 0x78, + 0x0F, 0xE0, 0x07, 0xF0, 0x0F, 0x1E, 0x0E, 0x07, 0x8F, 0x01, 0xE7, 0x00, + 0xF7, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, + 0x01, 0xEE, 0x00, 0xE7, 0x00, 0xF1, 0xC0, 0x70, 0x70, 0x70, 0x1C, 0xF0, + 0x03, 0xE0, 0x01, 0xF8, 0x00, 0x3E, 0x00, 0x07, 0xE0, 0xFF, 0xE0, 0x3C, + 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x38, 0x3C, + 0x70, 0x3F, 0xC0, 0x3D, 0xE0, 0x3C, 0xF0, 0x3C, 0xF8, 0x3C, 0x78, 0x3C, + 0x3C, 0x3C, 0x3E, 0xFF, 0x1F, 0x1F, 0x27, 0x0E, 0x60, 0x6E, 0x06, 0xF0, + 0x2F, 0x80, 0x7F, 0x07, 0xFC, 0x1F, 0xE0, 0x7E, 0x01, 0xF8, 0x07, 0xC0, + 0x7C, 0x06, 0xF0, 0xC9, 0xF8, 0xFF, 0xFF, 0xC7, 0x9F, 0x0F, 0x1C, 0x1E, + 0x10, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, + 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x07, 0xF8, 0xFE, + 0x1E, 0xF0, 0x09, 0xE0, 0x13, 0xC0, 0x27, 0x80, 0x4F, 0x00, 0x9E, 0x01, + 0x3C, 0x02, 0x78, 0x04, 0xF0, 0x09, 0xE0, 0x13, 0xC0, 0x27, 0x80, 0x47, + 0x81, 0x07, 0x84, 0x07, 0xF0, 0xFF, 0x0F, 0x9E, 0x03, 0x0F, 0x00, 0x83, + 0xC0, 0x81, 0xE0, 0x40, 0xF8, 0x60, 0x3C, 0x20, 0x1E, 0x10, 0x07, 0x90, + 0x03, 0xC8, 0x00, 0xF4, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x0E, 0x00, 0x07, + 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0xFE, 0x7F, 0x9E, 0xF8, 0x3C, 0x08, + 0xF0, 0x78, 0x31, 0xE0, 0xF0, 0x41, 0xE0, 0xF0, 0x83, 0xC3, 0xE3, 0x07, + 0x85, 0xC4, 0x07, 0x93, 0xC8, 0x0F, 0x27, 0xB0, 0x0E, 0x47, 0x40, 0x1F, + 0x0F, 0x80, 0x3E, 0x1F, 0x00, 0x38, 0x1C, 0x00, 0x70, 0x38, 0x00, 0xE0, + 0x30, 0x00, 0x80, 0x40, 0xFF, 0x9F, 0x9F, 0x07, 0x07, 0x83, 0x03, 0xE3, + 0x00, 0xF9, 0x00, 0x3D, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xE0, 0x00, + 0xF8, 0x00, 0xBE, 0x00, 0x8F, 0x00, 0x83, 0xC0, 0xC1, 0xF0, 0xE0, 0xFD, + 0xF8, 0xFF, 0xFF, 0x1F, 0x7C, 0x06, 0x3C, 0x04, 0x3E, 0x0C, 0x1E, 0x08, + 0x0F, 0x10, 0x0F, 0x30, 0x07, 0xA0, 0x07, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x0F, 0xF0, 0x7F, 0xFC, + 0xE0, 0xF1, 0x83, 0xE2, 0x07, 0x84, 0x1E, 0x00, 0x7C, 0x00, 0xF0, 0x03, + 0xC0, 0x0F, 0x80, 0x1E, 0x00, 0x7C, 0x08, 0xF0, 0x13, 0xC0, 0x6F, 0x81, + 0x9E, 0x07, 0x7F, 0xFE, 0xFF, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, + 0x73, 0x9C, 0xE7, 0x39, 0xF0, 0xC0, 0x60, 0x60, 0x60, 0x30, 0x30, 0x30, + 0x18, 0x18, 0x18, 0x0C, 0x0C, 0x0C, 0x06, 0x06, 0x06, 0x03, 0xF9, 0xCE, + 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCF, 0xF0, 0x0C, + 0x07, 0x81, 0xE0, 0xCC, 0x33, 0x18, 0x66, 0x1B, 0x87, 0xC0, 0xC0, 0xFF, + 0xF0, 0xC7, 0x1C, 0x30, 0x1F, 0x0E, 0x71, 0xCF, 0x39, 0xE0, 0x3C, 0x1F, + 0x8E, 0xF3, 0x9E, 0xF3, 0xDE, 0x79, 0xFF, 0x80, 0xF8, 0x07, 0x80, 0x78, + 0x07, 0x80, 0x78, 0x07, 0xB8, 0x7D, 0xE7, 0x8E, 0x78, 0xF7, 0x8F, 0x78, + 0xF7, 0x8F, 0x78, 0xF7, 0x8E, 0x79, 0xC4, 0x78, 0x1F, 0x1D, 0xDC, 0xFE, + 0x7F, 0x07, 0x83, 0xC1, 0xE0, 0x78, 0x3C, 0x47, 0xC0, 0x03, 0xE0, 0x1E, + 0x01, 0xE0, 0x1E, 0x01, 0xE1, 0xDE, 0x7B, 0xE7, 0x1E, 0xF1, 0xEF, 0x1E, + 0xF1, 0xEF, 0x1E, 0xF1, 0xE7, 0x1E, 0x7B, 0xE1, 0xDF, 0x1F, 0x0C, 0x67, + 0x1B, 0xC7, 0xFF, 0xFC, 0x0F, 0x03, 0xC0, 0x78, 0x4E, 0x21, 0xF0, 0x1E, + 0x3B, 0x7B, 0x78, 0x78, 0xFC, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0xFC, 0x3E, 0x0E, 0x7F, 0xCE, 0x79, 0xEF, 0x3C, 0xE7, 0x0F, + 0xC1, 0x00, 0x60, 0x1C, 0x03, 0xFE, 0x7F, 0xE3, 0xFF, 0x80, 0xF0, 0x33, + 0xFC, 0xF8, 0x07, 0x80, 0x78, 0x07, 0x80, 0x78, 0x07, 0xB8, 0x7D, 0xE7, + 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xEF, + 0xFF, 0x31, 0xE7, 0x8C, 0x03, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, + 0xBF, 0x06, 0x0F, 0x0F, 0x06, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xCF, 0xCE, 0x7C, 0xF8, 0x03, + 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0xF9, 0xE1, 0x8F, 0x10, 0x79, + 0x03, 0xD8, 0x1F, 0xE0, 0xF7, 0x87, 0x9E, 0x3C, 0x71, 0xE3, 0xDF, 0xBF, + 0xF9, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0xBF, + 0xFB, 0xCF, 0x0F, 0xBE, 0x79, 0xE7, 0x8F, 0x3C, 0xF1, 0xE7, 0x9E, 0x3C, + 0xF3, 0xC7, 0x9E, 0x78, 0xF3, 0xCF, 0x1E, 0x79, 0xE3, 0xCF, 0x3C, 0x7B, + 0xFF, 0xDF, 0x80, 0xFB, 0x87, 0xDE, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, + 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0xFF, 0xF0, 0x1F, 0x07, 0x71, 0xC7, + 0x78, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0x8F, 0x71, 0xC7, 0x70, 0x7C, 0x00, + 0xFB, 0x87, 0xDE, 0x78, 0xE7, 0x8F, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, + 0x78, 0xE7, 0x9E, 0x7F, 0x87, 0x80, 0x78, 0x07, 0x80, 0x78, 0x0F, 0xC0, + 0x1E, 0x23, 0x9E, 0x71, 0xEF, 0x1E, 0xF1, 0xEF, 0x1E, 0xF1, 0xEF, 0x1E, + 0x71, 0xE7, 0x9E, 0x1F, 0xE0, 0x1E, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x3F, + 0xF9, 0xDF, 0xF7, 0xDD, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, + 0x0F, 0xC0, 0x3D, 0x43, 0xC3, 0xE0, 0xFC, 0x7E, 0x1F, 0x87, 0x83, 0xC2, + 0xBC, 0x08, 0x18, 0x38, 0x78, 0xFC, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x79, 0x3E, 0xFB, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, + 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x3F, 0xF0, 0xFC, 0xEF, 0x08, + 0xE1, 0x1E, 0x41, 0xC8, 0x3D, 0x03, 0xC0, 0x78, 0x0E, 0x00, 0xC0, 0x10, + 0x00, 0xFD, 0xF7, 0xBC, 0x71, 0x9E, 0x38, 0x87, 0x1E, 0x43, 0xCF, 0x40, + 0xEB, 0xA0, 0x7C, 0xF0, 0x1C, 0x70, 0x0E, 0x38, 0x06, 0x08, 0x01, 0x04, + 0x00, 0xFC, 0xF7, 0x84, 0x3C, 0x81, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x80, + 0xBC, 0x13, 0xC2, 0x1E, 0xFB, 0xF0, 0xFC, 0xEF, 0x08, 0xE1, 0x1E, 0x43, + 0xC8, 0x3A, 0x07, 0xC0, 0x78, 0x0E, 0x01, 0xC0, 0x18, 0x02, 0x00, 0x41, + 0xC8, 0x3A, 0x03, 0x80, 0xFF, 0xB1, 0xE8, 0x70, 0x3C, 0x1E, 0x07, 0x83, + 0xC1, 0xE0, 0x78, 0xBC, 0x2F, 0xF8, 0x07, 0x0E, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0xE0, 0x18, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1E, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xE0, 0x70, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x18, 0x07, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x70, 0xE0, 0x70, 0x1F, 0x8B, 0x3F, 0x01, 0xC0}; + +const GFXglyph FreeSerifBold12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 6, 0, 1}, // 0x20 ' ' + {0, 4, 16, 8, 2, -15}, // 0x21 '!' + {8, 9, 7, 13, 2, -15}, // 0x22 '"' + {16, 12, 16, 12, 0, -15}, // 0x23 '#' + {40, 11, 20, 12, 1, -17}, // 0x24 '$' + {68, 18, 16, 24, 3, -15}, // 0x25 '%' + {104, 18, 16, 20, 1, -15}, // 0x26 '&' + {140, 3, 7, 7, 2, -15}, // 0x27 ''' + {143, 6, 21, 8, 1, -16}, // 0x28 '(' + {159, 6, 21, 8, 1, -16}, // 0x29 ')' + {175, 9, 10, 12, 2, -15}, // 0x2A '*' + {187, 12, 12, 16, 2, -11}, // 0x2B '+' + {205, 4, 8, 6, 1, -3}, // 0x2C ',' + {209, 6, 3, 8, 1, -6}, // 0x2D '-' + {212, 4, 4, 6, 1, -3}, // 0x2E '.' + {214, 8, 17, 7, -1, -15}, // 0x2F '/' + {231, 11, 16, 12, 1, -15}, // 0x30 '0' + {253, 9, 16, 12, 1, -15}, // 0x31 '1' + {271, 12, 16, 12, 0, -15}, // 0x32 '2' + {295, 11, 16, 12, 1, -15}, // 0x33 '3' + {317, 10, 16, 12, 1, -15}, // 0x34 '4' + {337, 11, 16, 12, 1, -15}, // 0x35 '5' + {359, 11, 16, 12, 1, -15}, // 0x36 '6' + {381, 11, 16, 12, 0, -15}, // 0x37 '7' + {403, 11, 16, 12, 1, -15}, // 0x38 '8' + {425, 11, 16, 12, 1, -15}, // 0x39 '9' + {447, 4, 11, 8, 2, -10}, // 0x3A ':' + {453, 4, 15, 8, 2, -10}, // 0x3B ';' + {461, 14, 14, 16, 1, -12}, // 0x3C '<' + {486, 14, 8, 16, 1, -9}, // 0x3D '=' + {500, 14, 14, 16, 1, -12}, // 0x3E '>' + {525, 10, 16, 12, 1, -15}, // 0x3F '?' + {545, 16, 16, 22, 3, -15}, // 0x40 '@' + {577, 17, 16, 17, 0, -15}, // 0x41 'A' + {611, 14, 16, 16, 1, -15}, // 0x42 'B' + {639, 15, 16, 17, 1, -15}, // 0x43 'C' + {669, 17, 16, 18, 0, -15}, // 0x44 'D' + {703, 15, 16, 16, 1, -15}, // 0x45 'E' + {733, 14, 16, 15, 1, -15}, // 0x46 'F' + {761, 16, 16, 19, 1, -15}, // 0x47 'G' + {793, 16, 16, 19, 2, -15}, // 0x48 'H' + {825, 8, 16, 9, 1, -15}, // 0x49 'I' + {841, 12, 18, 12, 0, -15}, // 0x4A 'J' + {868, 17, 16, 19, 2, -15}, // 0x4B 'K' + {902, 14, 16, 16, 2, -15}, // 0x4C 'L' + {930, 20, 16, 23, 1, -15}, // 0x4D 'M' + {970, 15, 16, 17, 1, -15}, // 0x4E 'N' + {1000, 17, 16, 19, 1, -15}, // 0x4F 'O' + {1034, 12, 16, 15, 2, -15}, // 0x50 'P' + {1058, 17, 20, 19, 1, -15}, // 0x51 'Q' + {1101, 16, 16, 17, 1, -15}, // 0x52 'R' + {1133, 12, 16, 14, 1, -15}, // 0x53 'S' + {1157, 15, 16, 15, 0, -15}, // 0x54 'T' + {1187, 15, 16, 17, 1, -15}, // 0x55 'U' + {1217, 17, 17, 17, 0, -15}, // 0x56 'V' + {1254, 23, 16, 24, 0, -15}, // 0x57 'W' + {1300, 17, 16, 17, 0, -15}, // 0x58 'X' + {1334, 16, 16, 17, 1, -15}, // 0x59 'Y' + {1366, 15, 16, 16, 0, -15}, // 0x5A 'Z' + {1396, 5, 20, 8, 2, -15}, // 0x5B '[' + {1409, 8, 17, 7, -1, -15}, // 0x5C '\' + {1426, 5, 20, 8, 2, -15}, // 0x5D ']' + {1439, 10, 9, 14, 2, -15}, // 0x5E '^' + {1451, 12, 1, 12, 0, 4}, // 0x5F '_' + {1453, 5, 4, 8, 0, -16}, // 0x60 '`' + {1456, 11, 11, 12, 1, -10}, // 0x61 'a' + {1472, 12, 16, 13, 1, -15}, // 0x62 'b' + {1496, 9, 11, 10, 1, -10}, // 0x63 'c' + {1509, 12, 16, 13, 1, -15}, // 0x64 'd' + {1533, 10, 11, 11, 1, -10}, // 0x65 'e' + {1547, 8, 16, 9, 1, -15}, // 0x66 'f' + {1563, 11, 16, 12, 1, -10}, // 0x67 'g' + {1585, 12, 16, 13, 1, -15}, // 0x68 'h' + {1609, 6, 16, 7, 1, -15}, // 0x69 'i' + {1621, 8, 21, 10, 0, -15}, // 0x6A 'j' + {1642, 13, 16, 13, 1, -15}, // 0x6B 'k' + {1668, 6, 16, 7, 1, -15}, // 0x6C 'l' + {1680, 19, 11, 20, 1, -10}, // 0x6D 'm' + {1707, 12, 11, 13, 1, -10}, // 0x6E 'n' + {1724, 11, 11, 12, 1, -10}, // 0x6F 'o' + {1740, 12, 16, 13, 1, -10}, // 0x70 'p' + {1764, 12, 16, 13, 1, -10}, // 0x71 'q' + {1788, 10, 11, 10, 1, -10}, // 0x72 'r' + {1802, 8, 11, 10, 1, -10}, // 0x73 's' + {1813, 8, 15, 8, 1, -14}, // 0x74 't' + {1828, 12, 11, 14, 1, -10}, // 0x75 'u' + {1845, 11, 11, 12, 0, -10}, // 0x76 'v' + {1861, 17, 11, 17, 0, -10}, // 0x77 'w' + {1885, 12, 11, 12, 0, -10}, // 0x78 'x' + {1902, 11, 16, 12, 0, -10}, // 0x79 'y' + {1924, 10, 11, 11, 1, -10}, // 0x7A 'z' + {1938, 8, 21, 9, 0, -16}, // 0x7B '{' + {1959, 2, 17, 5, 2, -15}, // 0x7C '|' + {1964, 8, 21, 9, 2, -16}, // 0x7D '}' + {1985, 11, 4, 12, 1, -7}}; // 0x7E '~' + +const GFXfont FreeSerifBold12pt7b PROGMEM = { + (uint8_t *)FreeSerifBold12pt7bBitmaps, + (GFXglyph *)FreeSerifBold12pt7bGlyphs, 0x20, 0x7E, 29}; + +// Approx. 2663 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold18pt7b.h new file mode 100644 index 0000000..d28014e --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold18pt7b.h @@ -0,0 +1,461 @@ +const uint8_t FreeSerifBold18pt7bBitmaps[] PROGMEM = { + 0x7B, 0xEF, 0xFF, 0xFF, 0xF7, 0x9E, 0x71, 0xC7, 0x0C, 0x20, 0x82, 0x00, + 0x00, 0x07, 0x3E, 0xFF, 0xFF, 0xDC, 0x60, 0x37, 0x83, 0xFC, 0x1F, 0xE0, + 0xFF, 0x07, 0xB8, 0x3D, 0xC0, 0xCC, 0x06, 0x20, 0x31, 0x01, 0x80, 0x03, + 0x8E, 0x00, 0xC3, 0x80, 0x30, 0xE0, 0x1C, 0x38, 0x07, 0x0E, 0x01, 0xC3, + 0x87, 0xFF, 0xFD, 0xFF, 0xFF, 0x7F, 0xFF, 0xC1, 0x87, 0x00, 0xE1, 0xC0, + 0x38, 0x70, 0x0E, 0x1C, 0x03, 0x86, 0x0F, 0xFF, 0xF3, 0xFF, 0xFC, 0xFF, + 0xFF, 0x07, 0x0E, 0x01, 0xC3, 0x80, 0x70, 0xE0, 0x1C, 0x30, 0x07, 0x0C, + 0x01, 0x87, 0x00, 0x61, 0xC0, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0xFF, + 0x03, 0x27, 0x8C, 0x47, 0x38, 0x86, 0x71, 0x0C, 0xF2, 0x09, 0xF4, 0x03, + 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x03, 0xFC, 0x03, 0xFE, 0x01, 0xFE, 0x03, + 0xFC, 0x04, 0xFC, 0x08, 0xFA, 0x10, 0xF4, 0x21, 0xEC, 0x43, 0xD8, 0x8F, + 0x3D, 0x3C, 0x3F, 0xF0, 0x1F, 0x00, 0x08, 0x00, 0x10, 0x00, 0x03, 0xC0, + 0x18, 0x01, 0xFE, 0x0F, 0x00, 0x7C, 0xFF, 0xC0, 0x1F, 0x0F, 0x90, 0x07, + 0xC1, 0x06, 0x00, 0xF0, 0x21, 0x80, 0x3E, 0x04, 0x30, 0x07, 0x81, 0x8C, + 0x00, 0xF0, 0x21, 0x80, 0x1E, 0x0C, 0x60, 0x03, 0xC1, 0x18, 0x1E, 0x3C, + 0xE3, 0x0F, 0xE7, 0xF8, 0xC3, 0xE6, 0x3C, 0x18, 0xF8, 0x40, 0x06, 0x3E, + 0x08, 0x01, 0x87, 0x81, 0x00, 0x31, 0xF0, 0x20, 0x0C, 0x3E, 0x04, 0x01, + 0x87, 0x81, 0x00, 0x60, 0xF0, 0x60, 0x18, 0x1E, 0x08, 0x03, 0x03, 0xC7, + 0x00, 0xC0, 0x3F, 0xC0, 0x18, 0x03, 0xE0, 0x00, 0x7E, 0x00, 0x00, 0x7F, + 0xE0, 0x00, 0x38, 0xF8, 0x00, 0x1E, 0x1F, 0x00, 0x07, 0x83, 0xC0, 0x01, + 0xF0, 0xF0, 0x00, 0x7C, 0x38, 0x00, 0x1F, 0x9C, 0x00, 0x03, 0xFC, 0x00, + 0x00, 0xFE, 0x0F, 0xF0, 0x3F, 0x80, 0xF0, 0x1F, 0xF0, 0x18, 0x1C, 0xFE, + 0x0C, 0x0E, 0x1F, 0xC3, 0x07, 0x87, 0xF1, 0x81, 0xE0, 0xFE, 0x40, 0xF8, + 0x1F, 0xF0, 0x3F, 0x07, 0xF8, 0x0F, 0xC0, 0xFE, 0x03, 0xF8, 0x1F, 0xC0, + 0xFE, 0x07, 0xF8, 0x9F, 0xE3, 0xFF, 0xE7, 0xFF, 0x9F, 0xF0, 0xFF, 0xC3, + 0xF8, 0x0F, 0x80, 0x3C, 0x00, 0x6F, 0xFF, 0xFF, 0x66, 0x66, 0x00, 0x81, + 0x81, 0x81, 0x81, 0x80, 0xC0, 0xE0, 0x70, 0x70, 0x38, 0x3C, 0x1E, 0x0F, + 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x0E, 0x07, 0x03, 0x80, 0xE0, + 0x70, 0x18, 0x06, 0x01, 0x00, 0x40, 0x10, 0x04, 0x80, 0x30, 0x0C, 0x03, + 0x00, 0xC0, 0x60, 0x38, 0x1C, 0x07, 0x03, 0x81, 0xC0, 0xF0, 0x78, 0x3C, + 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xE0, 0x70, 0x38, 0x38, 0x1C, 0x0C, + 0x0C, 0x06, 0x04, 0x04, 0x04, 0x00, 0x03, 0x00, 0x1E, 0x00, 0x78, 0x1D, + 0xE6, 0xFB, 0x3D, 0xED, 0xF3, 0xFF, 0x01, 0xC0, 0x7F, 0xF3, 0xED, 0xFF, + 0x33, 0xD9, 0xE6, 0x07, 0x80, 0x1E, 0x00, 0x30, 0x00, 0x00, 0xE0, 0x00, + 0x1C, 0x00, 0x03, 0x80, 0x00, 0x70, 0x00, 0x0E, 0x00, 0x01, 0xC0, 0x00, + 0x38, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, + 0x70, 0x00, 0x0E, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x07, 0x00, 0x00, + 0xE0, 0x00, 0x1C, 0x00, 0x03, 0x80, 0x00, 0x73, 0xEF, 0xFF, 0xFD, 0xF0, + 0xC2, 0x18, 0xC6, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0x7B, 0xFF, 0xFF, 0xFD, + 0xE0, 0x00, 0xE0, 0x3C, 0x07, 0x00, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x1C, + 0x07, 0x00, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x1C, + 0x07, 0x00, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x00, + 0x03, 0xC0, 0x0E, 0x70, 0x1E, 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0x3E, + 0x7C, 0x3E, 0x7C, 0x3E, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, + 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3E, 0x7C, 0x3E, + 0x7C, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x1E, 0x78, 0x0E, 0x70, 0x03, 0xC0, + 0x00, 0xC0, 0x3C, 0x0F, 0xC3, 0xFC, 0x4F, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, + 0x0F, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, + 0x0F, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, 0x1F, 0xEF, 0xFF, + 0x03, 0xE0, 0x0F, 0xF8, 0x1F, 0xFC, 0x3F, 0xFC, 0x30, 0xFE, 0x60, 0x7E, + 0x40, 0x3E, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x78, + 0x00, 0x70, 0x00, 0xE0, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x01, + 0x0C, 0x03, 0x1F, 0xFF, 0x1F, 0xFF, 0x3F, 0xFE, 0x7F, 0xFE, 0xFF, 0xFE, + 0x03, 0xF0, 0x0F, 0xF8, 0x3F, 0xFC, 0x21, 0xFE, 0x40, 0xFE, 0x00, 0x7E, + 0x00, 0x7E, 0x00, 0x7C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xFC, 0x03, 0xFE, + 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, + 0x00, 0x0E, 0x70, 0x0E, 0xFC, 0x1C, 0xFE, 0x38, 0x7F, 0xE0, 0x3F, 0x80, + 0x00, 0x38, 0x00, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x1F, 0x80, 0x5F, 0x00, + 0xBE, 0x02, 0x7C, 0x08, 0xF8, 0x31, 0xF0, 0x43, 0xE1, 0x07, 0xC4, 0x0F, + 0x88, 0x1F, 0x20, 0x3E, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, + 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x0F, 0xFE, 0x1F, + 0xF8, 0x7F, 0xF0, 0xFF, 0xE1, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x18, 0x00, + 0x3F, 0x80, 0xFF, 0xC1, 0xFF, 0xC3, 0xFF, 0xC3, 0xFF, 0x80, 0x3F, 0x80, + 0x0F, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x18, 0x00, 0x37, 0x80, 0x4F, 0x81, + 0x9F, 0xC6, 0x3F, 0xF8, 0x1F, 0x80, 0x00, 0x07, 0x00, 0x7C, 0x01, 0xF0, + 0x03, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x7E, 0x00, + 0x7F, 0xF0, 0x7F, 0xFC, 0xFC, 0x7E, 0xFC, 0x7E, 0xFC, 0x3F, 0xFC, 0x3F, + 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0x7C, 0x3F, 0x7C, 0x3E, 0x3C, 0x3E, + 0x3E, 0x3C, 0x1E, 0x78, 0x07, 0xE0, 0x7F, 0xFF, 0x7F, 0xFE, 0x7F, 0xFE, + 0xFF, 0xFE, 0xFF, 0xFC, 0xC0, 0x1C, 0x80, 0x18, 0x80, 0x38, 0x00, 0x38, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, + 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x03, 0x80, 0x03, 0x80, 0x07, 0x80, + 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x0F, 0xE0, 0x38, 0x78, 0x70, 0x3C, + 0xF0, 0x1E, 0xF0, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFE, 0x3C, 0x7F, 0xB0, + 0x7F, 0xE0, 0x3F, 0xF0, 0x0F, 0xF8, 0x1F, 0xFC, 0x39, 0xFE, 0x70, 0xFF, + 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1E, 0x78, 0x3E, + 0x7C, 0x7C, 0x3F, 0xF8, 0x0F, 0xE0, 0x07, 0xE0, 0x1E, 0x78, 0x3C, 0x7C, + 0x7C, 0x3C, 0x7C, 0x3E, 0xFC, 0x3E, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, + 0xFC, 0x3F, 0xFC, 0x3F, 0x7E, 0x3F, 0x7E, 0x3F, 0x3F, 0xFE, 0x0F, 0xFE, + 0x00, 0x7E, 0x00, 0x7C, 0x00, 0xF8, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xC0, + 0x0F, 0x80, 0x3E, 0x00, 0xE0, 0x00, 0x7B, 0xFF, 0xFF, 0xFD, 0xE0, 0x00, + 0x00, 0x07, 0xBF, 0xFF, 0xFF, 0xDE, 0x39, 0xFB, 0xF7, 0xEF, 0xC7, 0x00, + 0x00, 0x00, 0x01, 0xE7, 0xEF, 0xFF, 0xFF, 0xBF, 0x06, 0x08, 0x30, 0xC2, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x0F, 0x80, 0x07, 0xF0, + 0x03, 0xFC, 0x01, 0xFE, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x3F, 0x80, 0x1F, + 0xC0, 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, 0x07, 0xF0, 0x00, + 0x3F, 0x80, 0x01, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0x7C, 0x00, 0x01, 0x80, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x18, 0x00, 0x03, + 0xE0, 0x00, 0x7F, 0x00, 0x07, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, + 0x07, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xFC, 0x00, 0x3F, 0x80, 0x1F, 0xC0, + 0x0F, 0xE0, 0x07, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x00, 0xFE, 0x00, 0x1F, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0xFF, 0xC7, 0x1F, + 0xB8, 0x3E, 0xF0, 0xFF, 0xC3, 0xFF, 0x0F, 0xD8, 0x3F, 0x00, 0xF8, 0x07, + 0xC0, 0x1E, 0x00, 0x60, 0x03, 0x00, 0x08, 0x00, 0x20, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x70, 0x03, 0xE0, 0x1F, 0x80, 0x7E, 0x01, 0xF8, 0x01, + 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFF, 0xE0, 0x07, 0xC0, 0xF0, 0x0F, 0x00, + 0x38, 0x1E, 0x00, 0x0C, 0x3C, 0x07, 0x06, 0x38, 0x1F, 0x72, 0x78, 0x3C, + 0xF3, 0x78, 0x78, 0xE1, 0xF0, 0x70, 0xE1, 0xF0, 0xF0, 0xE1, 0xF0, 0xE0, + 0xC1, 0xF1, 0xE1, 0xC1, 0xF1, 0xC1, 0xC1, 0xF1, 0xC3, 0x82, 0xF1, 0xC3, + 0x86, 0x71, 0xC7, 0x8C, 0x79, 0xFB, 0xF8, 0x78, 0xF1, 0xF0, 0x3C, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0xC0, 0x78, 0x03, 0xFF, + 0xE0, 0x00, 0x7F, 0x80, 0x00, 0x10, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0xFE, 0x00, 0x01, 0xBF, 0x00, 0x01, 0xBF, 0x00, 0x01, 0x1F, + 0x00, 0x03, 0x1F, 0x80, 0x02, 0x1F, 0x80, 0x06, 0x0F, 0xC0, 0x06, 0x0F, + 0xC0, 0x04, 0x07, 0xE0, 0x0F, 0xFF, 0xE0, 0x0F, 0xFF, 0xE0, 0x18, 0x03, + 0xF0, 0x18, 0x03, 0xF0, 0x30, 0x01, 0xF8, 0x30, 0x01, 0xF8, 0x70, 0x01, + 0xFC, 0xFE, 0x0F, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFE, 0x0F, 0xE1, 0xF8, + 0x3F, 0x07, 0xC1, 0xF8, 0x3F, 0x0F, 0xC1, 0xF8, 0x7E, 0x0F, 0xC3, 0xF0, + 0x7E, 0x1F, 0x87, 0xE0, 0xFC, 0x7C, 0x07, 0xFF, 0x00, 0x3F, 0xFF, 0x01, + 0xF8, 0xFE, 0x0F, 0xC1, 0xF8, 0x7E, 0x0F, 0xC3, 0xF0, 0x3F, 0x1F, 0x81, + 0xF8, 0xFC, 0x0F, 0xC7, 0xE0, 0x7E, 0x3F, 0x03, 0xF1, 0xF8, 0x3F, 0x0F, + 0xC3, 0xF0, 0xFF, 0xFF, 0x1F, 0xFF, 0xC0, 0x00, 0x7E, 0x04, 0x07, 0xFF, + 0x18, 0x1F, 0x07, 0xF0, 0x7C, 0x03, 0xE1, 0xF0, 0x03, 0xC7, 0xC0, 0x03, + 0x9F, 0x80, 0x03, 0x3F, 0x00, 0x06, 0x7C, 0x00, 0x05, 0xF8, 0x00, 0x03, + 0xF0, 0x00, 0x07, 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0x1F, 0x80, 0x00, 0x3F, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x01, 0xF8, + 0x00, 0x01, 0xF0, 0x00, 0x23, 0xF0, 0x00, 0xC3, 0xF0, 0x07, 0x03, 0xF0, + 0x3C, 0x01, 0xFF, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, + 0x00, 0x7E, 0x1F, 0x80, 0xFC, 0x1F, 0x81, 0xF8, 0x1F, 0x83, 0xF0, 0x1F, + 0x07, 0xE0, 0x3F, 0x0F, 0xC0, 0x7E, 0x1F, 0x80, 0x7E, 0x3F, 0x00, 0xFC, + 0x7E, 0x01, 0xF8, 0xFC, 0x03, 0xF1, 0xF8, 0x07, 0xE3, 0xF0, 0x0F, 0xC7, + 0xE0, 0x1F, 0x8F, 0xC0, 0x3F, 0x1F, 0x80, 0x7C, 0x3F, 0x01, 0xF8, 0x7E, + 0x03, 0xE0, 0xFC, 0x0F, 0x81, 0xF8, 0x1F, 0x03, 0xF0, 0xFC, 0x0F, 0xFF, + 0xE0, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0x0F, 0xC0, 0x78, + 0x7E, 0x01, 0xC3, 0xF0, 0x06, 0x1F, 0x80, 0x10, 0xFC, 0x10, 0x87, 0xE0, + 0x80, 0x3F, 0x0C, 0x01, 0xF8, 0xE0, 0x0F, 0xFF, 0x00, 0x7F, 0xF8, 0x03, + 0xF1, 0xC0, 0x1F, 0x86, 0x00, 0xFC, 0x10, 0x07, 0xE0, 0x80, 0x3F, 0x00, + 0x09, 0xF8, 0x00, 0xCF, 0xC0, 0x0C, 0x7E, 0x00, 0x63, 0xF0, 0x0F, 0x1F, + 0x81, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xFF, 0xEF, 0xFF, 0xFC, + 0xFC, 0x0F, 0x9F, 0x80, 0x73, 0xF0, 0x06, 0x7E, 0x00, 0x4F, 0xC1, 0x09, + 0xF8, 0x20, 0x3F, 0x0C, 0x07, 0xE3, 0x80, 0xFF, 0xF0, 0x1F, 0xFE, 0x03, + 0xF1, 0xC0, 0x7E, 0x18, 0x0F, 0xC1, 0x01, 0xF8, 0x20, 0x3F, 0x00, 0x07, + 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x1F, + 0xE0, 0x07, 0xFF, 0x00, 0x00, 0x7E, 0x02, 0x01, 0xFF, 0xE3, 0x01, 0xF0, + 0x3F, 0x81, 0xF0, 0x07, 0xC1, 0xF0, 0x01, 0xE1, 0xF0, 0x00, 0x71, 0xF8, + 0x00, 0x18, 0xFC, 0x00, 0x0C, 0x7C, 0x00, 0x02, 0x7E, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x03, + 0xF0, 0x0F, 0xFF, 0xF8, 0x01, 0xFE, 0x7C, 0x00, 0x7E, 0x3F, 0x00, 0x3F, + 0x1F, 0x80, 0x1F, 0x87, 0xC0, 0x0F, 0xC1, 0xF0, 0x07, 0xE0, 0xFC, 0x03, + 0xF0, 0x1F, 0x83, 0xF0, 0x07, 0xFF, 0xE0, 0x00, 0x7F, 0x80, 0x00, 0xFF, + 0xC3, 0xFF, 0x7F, 0x81, 0xFE, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, + 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, + 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, + 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, + 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x3F, + 0x00, 0xFC, 0x3F, 0x00, 0xFC, 0x7F, 0x81, 0xFE, 0xFF, 0xC3, 0xFF, 0xFF, + 0xEF, 0xF0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, + 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, + 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x1F, 0xE7, 0xFF, 0x07, 0xFF, 0x01, 0xFE, + 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, + 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, + 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, + 0x70, 0xFC, 0xF8, 0xFC, 0xF8, 0xF8, 0xF0, 0xF8, 0x71, 0xF0, 0x7F, 0xE0, + 0x1F, 0x80, 0xFF, 0xC3, 0xFF, 0x3F, 0xC0, 0x3E, 0x0F, 0xC0, 0x1C, 0x07, + 0xE0, 0x18, 0x03, 0xF0, 0x18, 0x01, 0xF8, 0x18, 0x00, 0xFC, 0x18, 0x00, + 0x7E, 0x18, 0x00, 0x3F, 0x18, 0x00, 0x1F, 0x9C, 0x00, 0x0F, 0xDF, 0x00, + 0x07, 0xFF, 0xC0, 0x03, 0xFF, 0xF0, 0x01, 0xF9, 0xF8, 0x00, 0xFC, 0xFE, + 0x00, 0x7E, 0x3F, 0x80, 0x3F, 0x0F, 0xE0, 0x1F, 0x83, 0xF8, 0x0F, 0xC0, + 0xFC, 0x07, 0xE0, 0x7F, 0x03, 0xF0, 0x1F, 0xC1, 0xF8, 0x07, 0xF1, 0xFE, + 0x03, 0xFD, 0xFF, 0x8F, 0xFF, 0xFF, 0xE0, 0x03, 0xFC, 0x00, 0x0F, 0xC0, + 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, + 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x7E, 0x00, + 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x01, 0x3F, + 0x00, 0x19, 0xF8, 0x00, 0xCF, 0xC0, 0x0C, 0x7E, 0x00, 0x63, 0xF0, 0x0F, + 0x1F, 0x81, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFE, 0xFF, 0x80, 0x03, 0xFE, + 0x7F, 0x00, 0x07, 0xF8, 0x7E, 0x00, 0x0F, 0xE0, 0xFE, 0x00, 0x3F, 0xC1, + 0x7C, 0x00, 0x5F, 0x82, 0xFC, 0x01, 0xBF, 0x05, 0xF8, 0x02, 0x7E, 0x09, + 0xF8, 0x0C, 0xFC, 0x13, 0xF0, 0x11, 0xF8, 0x23, 0xE0, 0x23, 0xF0, 0x47, + 0xE0, 0xC7, 0xE0, 0x87, 0xC1, 0x0F, 0xC1, 0x0F, 0xC6, 0x1F, 0x82, 0x0F, + 0x88, 0x3F, 0x04, 0x1F, 0xB0, 0x7E, 0x08, 0x3F, 0x60, 0xFC, 0x10, 0x3E, + 0x81, 0xF8, 0x20, 0x7F, 0x03, 0xF0, 0x40, 0x7C, 0x07, 0xE0, 0x80, 0xF8, + 0x0F, 0xC1, 0x00, 0xE0, 0x1F, 0x82, 0x01, 0xC0, 0x3F, 0x0E, 0x03, 0x80, + 0xFF, 0x7F, 0x82, 0x03, 0xFF, 0xFE, 0x00, 0xFE, 0xFE, 0x00, 0x70, 0xFE, + 0x00, 0x40, 0xFE, 0x00, 0x81, 0xFC, 0x01, 0x03, 0xFC, 0x02, 0x05, 0xFC, + 0x04, 0x09, 0xFC, 0x08, 0x11, 0xFC, 0x10, 0x23, 0xF8, 0x20, 0x43, 0xF8, + 0x40, 0x83, 0xF8, 0x81, 0x03, 0xF9, 0x02, 0x03, 0xFA, 0x04, 0x03, 0xF4, + 0x08, 0x07, 0xF8, 0x10, 0x07, 0xF0, 0x20, 0x07, 0xE0, 0x40, 0x07, 0xC0, + 0x80, 0x07, 0x81, 0x00, 0x0F, 0x02, 0x00, 0x0E, 0x0E, 0x00, 0x0C, 0x7F, + 0x00, 0x08, 0x00, 0x7F, 0x00, 0x01, 0xFF, 0xF0, 0x01, 0xF0, 0x7C, 0x01, + 0xF0, 0x1F, 0x01, 0xF0, 0x07, 0xC1, 0xF0, 0x01, 0xF1, 0xF8, 0x00, 0xFC, + 0xFC, 0x00, 0x7E, 0x7C, 0x00, 0x1F, 0x7E, 0x00, 0x0F, 0xFF, 0x00, 0x07, + 0xFF, 0x80, 0x03, 0xFF, 0xC0, 0x01, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0x00, + 0x7F, 0xF8, 0x00, 0x3F, 0x7C, 0x00, 0x1F, 0x3E, 0x00, 0x1F, 0x9F, 0x80, + 0x0F, 0xC7, 0xC0, 0x07, 0xC1, 0xF0, 0x07, 0xC0, 0xFC, 0x07, 0xE0, 0x3F, + 0x07, 0xC0, 0x07, 0xFF, 0xC0, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0xFC, 0x0F, + 0xFF, 0xE0, 0xFC, 0x7E, 0x1F, 0x87, 0xE3, 0xF0, 0x7E, 0x7E, 0x0F, 0xCF, + 0xC1, 0xF9, 0xF8, 0x3F, 0x3F, 0x07, 0xE7, 0xE0, 0xFC, 0xFC, 0x3F, 0x1F, + 0x8F, 0xC3, 0xFF, 0xF0, 0x7F, 0xF8, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, + 0x00, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, + 0x00, 0x1F, 0xE0, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x01, 0xFF, 0xF0, + 0x01, 0xF0, 0x7C, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x07, 0xC1, 0xF0, 0x01, + 0xF1, 0xF8, 0x00, 0xFC, 0xFC, 0x00, 0x7E, 0x7C, 0x00, 0x1F, 0x7E, 0x00, + 0x0F, 0xFF, 0x00, 0x07, 0xFF, 0x80, 0x03, 0xFF, 0xC0, 0x01, 0xFF, 0xE0, + 0x00, 0xFF, 0xF0, 0x00, 0x7F, 0xF8, 0x00, 0x3F, 0x7C, 0x00, 0x1F, 0x3E, + 0x00, 0x0F, 0x9F, 0x80, 0x0F, 0xC7, 0xC0, 0x07, 0xC1, 0xF0, 0x07, 0xC0, + 0x78, 0x03, 0xC0, 0x1E, 0x07, 0xC0, 0x03, 0xFF, 0x80, 0x00, 0x7F, 0x00, + 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xF0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, + 0xF8, 0x00, 0x0F, 0xE0, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0x00, 0xFC, 0x3F, + 0x01, 0xF8, 0x3F, 0x03, 0xF0, 0x3F, 0x07, 0xE0, 0x7E, 0x0F, 0xC0, 0xFC, + 0x1F, 0x81, 0xF8, 0x3F, 0x03, 0xF0, 0x7E, 0x07, 0xC0, 0xFC, 0x1F, 0x81, + 0xF8, 0x7E, 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0xC0, 0x0F, 0xDF, 0xC0, 0x1F, + 0x9F, 0x80, 0x3F, 0x1F, 0x80, 0x7E, 0x3F, 0x80, 0xFC, 0x3F, 0x81, 0xF8, + 0x3F, 0x03, 0xF0, 0x7F, 0x07, 0xE0, 0x7F, 0x1F, 0xE0, 0x7F, 0x7F, 0xE0, + 0xFF, 0x07, 0xC2, 0x1F, 0xF2, 0x3C, 0x3E, 0x70, 0x0E, 0xF0, 0x06, 0xF0, + 0x06, 0xF0, 0x02, 0xF8, 0x00, 0xFE, 0x00, 0xFF, 0x80, 0x7F, 0xE0, 0x3F, + 0xF8, 0x1F, 0xFC, 0x0F, 0xFE, 0x03, 0xFE, 0x00, 0xFF, 0x00, 0x3F, 0x80, + 0x1F, 0xC0, 0x0F, 0xC0, 0x0F, 0xE0, 0x0E, 0xF0, 0x1E, 0xF8, 0x3C, 0x9F, + 0xF8, 0x87, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7E, 0x3F, 0x83, + 0xF0, 0x7C, 0x1F, 0x81, 0xC0, 0xFC, 0x06, 0x07, 0xE0, 0x20, 0x3F, 0x00, + 0x01, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x1F, + 0x80, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF8, 0x00, + 0x0F, 0xC0, 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, + 0x00, 0x0F, 0xF0, 0x01, 0xFF, 0xE0, 0xFF, 0xC1, 0xFD, 0xFE, 0x01, 0xC3, + 0xF0, 0x02, 0x0F, 0xC0, 0x08, 0x3F, 0x00, 0x20, 0xFC, 0x00, 0x83, 0xF0, + 0x02, 0x0F, 0xC0, 0x08, 0x3F, 0x00, 0x20, 0xFC, 0x00, 0x83, 0xF0, 0x02, + 0x0F, 0xC0, 0x08, 0x3F, 0x00, 0x20, 0xFC, 0x00, 0x83, 0xF0, 0x02, 0x0F, + 0xC0, 0x08, 0x3F, 0x00, 0x20, 0xFC, 0x00, 0x83, 0xF0, 0x02, 0x0F, 0xC0, + 0x18, 0x1F, 0x80, 0x40, 0x7E, 0x03, 0x00, 0xFC, 0x18, 0x01, 0xFF, 0xC0, + 0x00, 0xFC, 0x00, 0xFF, 0xF0, 0x7F, 0x3F, 0xC0, 0x1E, 0x1F, 0x80, 0x0C, + 0x1F, 0x80, 0x08, 0x0F, 0xC0, 0x18, 0x0F, 0xC0, 0x18, 0x07, 0xE0, 0x10, + 0x07, 0xE0, 0x30, 0x07, 0xE0, 0x20, 0x03, 0xF0, 0x60, 0x03, 0xF0, 0x60, + 0x01, 0xF8, 0x40, 0x01, 0xF8, 0xC0, 0x00, 0xF8, 0x80, 0x00, 0xFC, 0x80, + 0x00, 0xFD, 0x80, 0x00, 0x7F, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x0C, 0x00, 0xFF, 0xE7, 0xFF, 0x0F, 0xCF, 0xE0, 0x7F, 0x00, 0xE1, + 0xF8, 0x0F, 0xC0, 0x30, 0x7E, 0x03, 0xF0, 0x0C, 0x1F, 0x80, 0x7C, 0x02, + 0x03, 0xE0, 0x1F, 0x81, 0x80, 0xFC, 0x07, 0xE0, 0x60, 0x3F, 0x03, 0xF8, + 0x10, 0x07, 0xC0, 0xBF, 0x0C, 0x01, 0xF8, 0x2F, 0xC3, 0x00, 0x7E, 0x19, + 0xF0, 0x80, 0x0F, 0x84, 0x7C, 0x60, 0x03, 0xF3, 0x0F, 0x98, 0x00, 0xFC, + 0xC3, 0xE4, 0x00, 0x1F, 0x20, 0xFB, 0x00, 0x07, 0xF8, 0x1F, 0xC0, 0x00, + 0xFC, 0x07, 0xE0, 0x00, 0x3F, 0x01, 0xF8, 0x00, 0x0F, 0xC0, 0x3E, 0x00, + 0x01, 0xE0, 0x0F, 0x00, 0x00, 0x78, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0x20, 0x00, + 0x80, 0x00, 0xFF, 0xF3, 0xFE, 0x7F, 0x80, 0x78, 0x3F, 0x80, 0x70, 0x1F, + 0xC0, 0x60, 0x0F, 0xC0, 0xC0, 0x0F, 0xE1, 0x80, 0x07, 0xF1, 0x00, 0x03, + 0xF3, 0x00, 0x03, 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x9F, 0x80, 0x01, 0x8F, 0xC0, 0x03, 0x0F, 0xE0, 0x06, 0x07, 0xE0, 0x06, + 0x07, 0xF0, 0x0C, 0x03, 0xF8, 0x1C, 0x03, 0xF8, 0x3C, 0x03, 0xFC, 0xFF, + 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0x7F, 0x80, 0x1E, 0x3F, 0x80, 0x1C, 0x1F, + 0x80, 0x18, 0x1F, 0xC0, 0x10, 0x0F, 0xC0, 0x30, 0x07, 0xE0, 0x20, 0x07, + 0xE0, 0x60, 0x03, 0xF0, 0xC0, 0x03, 0xF0, 0x80, 0x01, 0xF9, 0x80, 0x01, + 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7E, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x01, + 0xFF, 0x80, 0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x9F, 0x01, 0xF8, 0xE0, 0x1F, + 0x86, 0x01, 0xFC, 0x20, 0x0F, 0xC1, 0x00, 0xFC, 0x00, 0x07, 0xE0, 0x00, + 0x7E, 0x00, 0x07, 0xE0, 0x00, 0x3F, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x80, + 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x01, 0xFC, 0x01, 0x0F, 0xC0, 0x18, 0xFC, + 0x00, 0xC7, 0xE0, 0x06, 0x7E, 0x00, 0x77, 0xF0, 0x07, 0x3F, 0x00, 0xFB, + 0xFF, 0xFF, 0xDF, 0xFF, 0xFE, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xE0, 0x1E, + 0x01, 0xC0, 0x38, 0x07, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x1C, 0x03, 0x80, + 0x70, 0x07, 0x00, 0xE0, 0x1C, 0x01, 0xC0, 0x38, 0x07, 0x00, 0x70, 0x0E, + 0x01, 0xC0, 0x1C, 0x03, 0x80, 0x70, 0x0F, 0x00, 0xE0, 0xFF, 0xFF, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0x03, 0x80, 0x0F, 0x00, 0x1F, 0x00, 0x7E, 0x00, 0xEE, 0x03, + 0x9C, 0x07, 0x1C, 0x1C, 0x38, 0x38, 0x38, 0xE0, 0x71, 0xC0, 0x77, 0x00, + 0xEE, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xE0, 0xF0, + 0x78, 0x3C, 0x0E, 0x07, 0x0F, 0xE0, 0x3F, 0xF0, 0x78, 0xF8, 0x78, 0x7C, + 0x78, 0x7C, 0x38, 0x7C, 0x00, 0x7C, 0x03, 0xFC, 0x1E, 0x7C, 0x7C, 0x7C, + 0xFC, 0x7C, 0xFC, 0x7C, 0xFC, 0xFC, 0xFF, 0xFD, 0x7F, 0x7F, 0x3C, 0x3C, + 0xFC, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1F, + 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0xF8, 0x1F, 0x7F, 0x87, 0xE3, + 0xF1, 0xF0, 0x7E, 0x7C, 0x0F, 0x9F, 0x03, 0xF7, 0xC0, 0xFD, 0xF0, 0x3F, + 0x7C, 0x0F, 0xDF, 0x03, 0xF7, 0xC0, 0xFD, 0xF0, 0x3E, 0x7C, 0x1F, 0x1F, + 0x8F, 0xC6, 0x7F, 0xC1, 0x07, 0xC0, 0x07, 0xC0, 0x7F, 0xC3, 0xC7, 0x9F, + 0x1E, 0x78, 0x7B, 0xE1, 0xCF, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, + 0x80, 0x3F, 0x00, 0x7C, 0x00, 0xFC, 0x61, 0xFF, 0x03, 0xF0, 0x00, 0x7F, + 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xC0, + 0x01, 0xF0, 0x00, 0x7C, 0x07, 0x9F, 0x07, 0xF7, 0xC3, 0xE3, 0xF1, 0xF8, + 0x7C, 0x7C, 0x1F, 0x3F, 0x07, 0xCF, 0xC1, 0xF3, 0xF0, 0x7C, 0xFC, 0x1F, + 0x3F, 0x07, 0xCF, 0xC1, 0xF1, 0xF0, 0x7C, 0x7E, 0x1F, 0x0F, 0x8F, 0xC1, + 0xFD, 0xFC, 0x3E, 0x70, 0x0F, 0xC0, 0x7F, 0xC3, 0xC7, 0x1E, 0x1E, 0xF8, + 0x7B, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0xC0, 0x1F, + 0x03, 0x7E, 0x18, 0xFF, 0xC1, 0xFE, 0x03, 0xF0, 0x0F, 0x83, 0xF8, 0xF3, + 0xBE, 0xF7, 0xDC, 0xF8, 0x1F, 0x03, 0xE0, 0xFF, 0x1F, 0xE1, 0xF0, 0x3E, + 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, + 0xC0, 0xF8, 0x1F, 0x07, 0xF8, 0x0F, 0xC0, 0x1F, 0xFF, 0xDF, 0x1F, 0xFF, + 0x07, 0x8F, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF9, 0xF0, 0x7C, 0x78, 0x3C, + 0x1E, 0x3E, 0x03, 0xFC, 0x03, 0x00, 0x07, 0x00, 0x07, 0x80, 0x03, 0xFF, + 0xF1, 0xFF, 0xFE, 0x7F, 0xFF, 0x8F, 0xFF, 0xF8, 0x01, 0xFC, 0x00, 0x7F, + 0x00, 0x73, 0xFF, 0xF0, 0x7F, 0xC0, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x00, + 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, + 0x7C, 0x3E, 0xFF, 0x1F, 0xCF, 0xCF, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF9, + 0xF0, 0x7C, 0xF8, 0x3E, 0x7C, 0x1F, 0x3E, 0x0F, 0x9F, 0x07, 0xCF, 0x83, + 0xE7, 0xC1, 0xF3, 0xE0, 0xF9, 0xF0, 0x7D, 0xFC, 0x7F, 0x39, 0xFB, 0xF7, + 0xE7, 0x80, 0x00, 0x00, 0xFC, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, + 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x7F, 0x03, 0xC0, 0xFC, 0x1F, 0x83, 0xF0, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, + 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, + 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7D, 0xCF, 0xF9, 0xEE, 0x7C, 0xFF, 0x0F, + 0x80, 0xFC, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, + 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x7F, 0x9F, 0x07, 0x87, + 0xC1, 0x81, 0xF0, 0xC0, 0x7C, 0x60, 0x1F, 0x30, 0x07, 0xDE, 0x01, 0xFF, + 0xC0, 0x7F, 0xF0, 0x1F, 0x3E, 0x07, 0xCF, 0xC1, 0xF1, 0xF8, 0x7C, 0x3E, + 0x1F, 0x07, 0xC7, 0xC1, 0xFB, 0xF9, 0xFF, 0xFC, 0xF9, 0xF3, 0xE7, 0xCF, + 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, + 0xE7, 0xCF, 0x9F, 0x7F, 0xFC, 0x7C, 0x1F, 0x0F, 0xBF, 0xCF, 0xF1, 0xF8, + 0xFF, 0x3F, 0x3E, 0x0F, 0x83, 0xE7, 0xC1, 0xF0, 0x7C, 0xF8, 0x3E, 0x0F, + 0x9F, 0x07, 0xC1, 0xF3, 0xE0, 0xF8, 0x3E, 0x7C, 0x1F, 0x07, 0xCF, 0x83, + 0xE0, 0xF9, 0xF0, 0x7C, 0x1F, 0x3E, 0x0F, 0x83, 0xE7, 0xC1, 0xF0, 0x7C, + 0xF8, 0x3E, 0x0F, 0x9F, 0x07, 0xC1, 0xF7, 0xF1, 0xFC, 0x7F, 0xFC, 0x7C, + 0x3E, 0xFF, 0x1F, 0xCF, 0xCF, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF9, 0xF0, + 0x7C, 0xF8, 0x3E, 0x7C, 0x1F, 0x3E, 0x0F, 0x9F, 0x07, 0xCF, 0x83, 0xE7, + 0xC1, 0xF3, 0xE0, 0xF9, 0xF0, 0x7D, 0xFC, 0x7F, 0x07, 0xF0, 0x0F, 0xFE, + 0x0F, 0x8F, 0x8F, 0x87, 0xE7, 0xC1, 0xF7, 0xE0, 0xFF, 0xF0, 0x7F, 0xF8, + 0x3F, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0x07, 0xEF, 0x83, 0xE7, 0xC1, 0xF1, + 0xF1, 0xF0, 0x7F, 0xF0, 0x0F, 0xE0, 0xFE, 0x7C, 0x07, 0xDF, 0xE0, 0xFE, + 0x3E, 0x1F, 0x07, 0xE3, 0xE0, 0x7C, 0x7C, 0x0F, 0xCF, 0x81, 0xF9, 0xF0, + 0x3F, 0x3E, 0x07, 0xE7, 0xC0, 0xFC, 0xF8, 0x1F, 0x9F, 0x03, 0xE3, 0xE0, + 0xFC, 0x7E, 0x3F, 0x0F, 0xBF, 0xC1, 0xF3, 0xE0, 0x3E, 0x00, 0x07, 0xC0, + 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7E, 0x00, 0x1F, 0xE0, + 0x00, 0x07, 0xC1, 0x0F, 0xF9, 0x8F, 0xCD, 0xCF, 0xC3, 0xE7, 0xC1, 0xF7, + 0xE0, 0xFB, 0xF0, 0x7D, 0xF8, 0x3E, 0xFC, 0x1F, 0x7E, 0x0F, 0xBF, 0x07, + 0xDF, 0x83, 0xE7, 0xE1, 0xF1, 0xF1, 0xF8, 0x7F, 0x7C, 0x1F, 0x3E, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x01, 0xF8, + 0x01, 0xFE, 0xFC, 0x73, 0xEF, 0xDF, 0xFE, 0xFC, 0xF7, 0xC3, 0xBE, 0x01, + 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, + 0x01, 0xF0, 0x1F, 0xE0, 0x1E, 0x23, 0xFE, 0x70, 0xEE, 0x06, 0xE0, 0x2F, + 0x80, 0xFF, 0x07, 0xFC, 0x3F, 0xE0, 0xFF, 0x81, 0xF8, 0x07, 0xC0, 0x7E, + 0x0E, 0xBF, 0xC8, 0xF8, 0x04, 0x03, 0x01, 0xC0, 0xF0, 0x7C, 0x3F, 0xEF, + 0xF9, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, + 0x7C, 0x5F, 0x37, 0xF8, 0xFE, 0x1E, 0x00, 0xFC, 0x7F, 0x1F, 0x07, 0xC7, + 0xC1, 0xF1, 0xF0, 0x7C, 0x7C, 0x1F, 0x1F, 0x07, 0xC7, 0xC1, 0xF1, 0xF0, + 0x7C, 0x7C, 0x1F, 0x1F, 0x07, 0xC7, 0xC1, 0xF1, 0xF0, 0x7C, 0x7C, 0x1F, + 0x1F, 0x8F, 0xC3, 0xFD, 0xFC, 0x7C, 0x60, 0xFF, 0x9F, 0xBF, 0x83, 0x0F, + 0x81, 0x87, 0xE0, 0x81, 0xF0, 0x40, 0xF8, 0x40, 0x3E, 0x20, 0x1F, 0x30, + 0x07, 0xD0, 0x03, 0xF8, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3C, 0x00, 0x0E, + 0x00, 0x07, 0x00, 0x01, 0x00, 0xFF, 0x3F, 0xCF, 0x7E, 0x1F, 0x06, 0x3E, + 0x0F, 0x06, 0x3E, 0x0F, 0x84, 0x1F, 0x0F, 0x8C, 0x1F, 0x1F, 0x88, 0x0F, + 0x17, 0xC8, 0x0F, 0x97, 0xD8, 0x0F, 0xB3, 0xD0, 0x07, 0xE3, 0xF0, 0x07, + 0xE3, 0xE0, 0x03, 0xC1, 0xE0, 0x03, 0xC1, 0xE0, 0x03, 0x81, 0xC0, 0x01, + 0x80, 0xC0, 0x01, 0x80, 0x80, 0xFF, 0x3F, 0x7E, 0x0C, 0x3E, 0x08, 0x3F, + 0x18, 0x1F, 0x30, 0x0F, 0xE0, 0x0F, 0xC0, 0x07, 0xE0, 0x03, 0xE0, 0x03, + 0xF0, 0x05, 0xF8, 0x0C, 0xF8, 0x18, 0xFC, 0x30, 0x7E, 0x70, 0x7E, 0xFC, + 0xFF, 0xFF, 0x3F, 0x7E, 0x0C, 0x7C, 0x0C, 0x3E, 0x08, 0x3E, 0x08, 0x1E, + 0x18, 0x1F, 0x10, 0x0F, 0x30, 0x0F, 0xA0, 0x0F, 0xA0, 0x07, 0xE0, 0x07, + 0xC0, 0x03, 0xC0, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x01, + 0x00, 0x61, 0x00, 0xF2, 0x00, 0xF6, 0x00, 0xFC, 0x00, 0x78, 0x00, 0x7F, + 0xFD, 0xFF, 0xF7, 0x0F, 0xD0, 0x3E, 0x01, 0xF0, 0x0F, 0xC0, 0x3E, 0x01, + 0xF0, 0x0F, 0xC0, 0x3E, 0x01, 0xF8, 0x0F, 0xC1, 0x3E, 0x05, 0xF8, 0x7F, + 0xFF, 0xFF, 0xFF, 0x01, 0xE0, 0xF8, 0x3E, 0x07, 0x80, 0xF0, 0x1E, 0x03, + 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x87, + 0x80, 0x1E, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, + 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x0F, 0x80, 0x78, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0x0F, 0x80, 0xF0, + 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, + 0x01, 0xE0, 0x3C, 0x03, 0xC0, 0x0F, 0x0F, 0x03, 0xC0, 0x78, 0x0F, 0x01, + 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0xE0, + 0xF8, 0x3C, 0x00, 0x3E, 0x00, 0x7F, 0xC6, 0xFF, 0xFF, 0x61, 0xFE, 0x00, + 0x7C}; + +const GFXglyph FreeSerifBold18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 9, 0, 1}, // 0x20 ' ' + {0, 6, 24, 12, 3, -23}, // 0x21 '!' + {18, 13, 10, 19, 3, -23}, // 0x22 '"' + {35, 18, 24, 17, 0, -23}, // 0x23 '#' + {89, 15, 28, 17, 1, -25}, // 0x24 '$' + {142, 27, 24, 35, 4, -23}, // 0x25 '%' + {223, 26, 25, 29, 2, -23}, // 0x26 '&' + {305, 4, 10, 10, 3, -23}, // 0x27 ''' + {310, 9, 30, 12, 2, -23}, // 0x28 '(' + {344, 9, 30, 12, 1, -23}, // 0x29 ')' + {378, 14, 15, 18, 2, -23}, // 0x2A '*' + {405, 19, 19, 24, 2, -17}, // 0x2B '+' + {451, 6, 12, 9, 1, -5}, // 0x2C ',' + {460, 8, 4, 12, 2, -9}, // 0x2D '-' + {464, 6, 6, 9, 1, -5}, // 0x2E '.' + {469, 11, 25, 10, -1, -23}, // 0x2F '/' + {504, 16, 24, 18, 1, -23}, // 0x30 '0' + {552, 12, 24, 18, 3, -23}, // 0x31 '1' + {588, 16, 24, 17, 1, -23}, // 0x32 '2' + {636, 16, 24, 18, 0, -23}, // 0x33 '3' + {684, 15, 24, 18, 1, -23}, // 0x34 '4' + {729, 15, 24, 18, 1, -23}, // 0x35 '5' + {774, 16, 24, 18, 1, -23}, // 0x36 '6' + {822, 16, 24, 17, 1, -23}, // 0x37 '7' + {870, 16, 24, 17, 1, -23}, // 0x38 '8' + {918, 16, 24, 18, 1, -23}, // 0x39 '9' + {966, 6, 16, 12, 3, -15}, // 0x3A ':' + {978, 7, 22, 12, 2, -15}, // 0x3B ';' + {998, 19, 20, 24, 2, -18}, // 0x3C '<' + {1046, 19, 12, 24, 2, -14}, // 0x3D '=' + {1075, 19, 20, 24, 3, -18}, // 0x3E '>' + {1123, 14, 24, 18, 2, -23}, // 0x3F '?' + {1165, 24, 25, 33, 4, -23}, // 0x40 '@' + {1240, 24, 24, 25, 1, -23}, // 0x41 'A' + {1312, 21, 24, 23, 1, -23}, // 0x42 'B' + {1375, 23, 25, 25, 1, -23}, // 0x43 'C' + {1447, 23, 24, 26, 1, -23}, // 0x44 'D' + {1516, 21, 24, 23, 2, -23}, // 0x45 'E' + {1579, 19, 24, 22, 2, -23}, // 0x46 'F' + {1636, 25, 25, 27, 1, -23}, // 0x47 'G' + {1715, 24, 24, 27, 2, -23}, // 0x48 'H' + {1787, 11, 24, 14, 2, -23}, // 0x49 'I' + {1820, 16, 27, 18, 0, -23}, // 0x4A 'J' + {1874, 25, 24, 27, 2, -23}, // 0x4B 'K' + {1949, 21, 24, 23, 2, -23}, // 0x4C 'L' + {2012, 31, 24, 33, 1, -23}, // 0x4D 'M' + {2105, 23, 24, 25, 1, -23}, // 0x4E 'N' + {2174, 25, 25, 27, 1, -23}, // 0x4F 'O' + {2253, 19, 24, 22, 2, -23}, // 0x50 'P' + {2310, 25, 30, 27, 1, -23}, // 0x51 'Q' + {2404, 23, 24, 25, 2, -23}, // 0x52 'R' + {2473, 16, 25, 20, 2, -23}, // 0x53 'S' + {2523, 21, 24, 23, 1, -23}, // 0x54 'T' + {2586, 22, 25, 25, 2, -23}, // 0x55 'U' + {2655, 24, 24, 25, 0, -23}, // 0x56 'V' + {2727, 34, 25, 34, 0, -23}, // 0x57 'W' + {2834, 24, 24, 25, 1, -23}, // 0x58 'X' + {2906, 24, 24, 25, 1, -23}, // 0x59 'Y' + {2978, 21, 24, 23, 1, -23}, // 0x5A 'Z' + {3041, 8, 29, 12, 2, -23}, // 0x5B '[' + {3070, 11, 25, 10, -1, -23}, // 0x5C '\' + {3105, 8, 29, 12, 2, -23}, // 0x5D ']' + {3134, 15, 13, 20, 3, -23}, // 0x5E '^' + {3159, 18, 3, 17, 0, 3}, // 0x5F '_' + {3166, 8, 6, 12, 0, -23}, // 0x60 '`' + {3172, 16, 16, 18, 1, -15}, // 0x61 'a' + {3204, 18, 24, 19, 1, -23}, // 0x62 'b' + {3258, 14, 16, 15, 1, -15}, // 0x63 'c' + {3286, 18, 24, 19, 1, -23}, // 0x64 'd' + {3340, 14, 16, 16, 1, -15}, // 0x65 'e' + {3368, 11, 24, 14, 2, -23}, // 0x66 'f' + {3401, 17, 23, 17, 1, -15}, // 0x67 'g' + {3450, 17, 24, 19, 1, -23}, // 0x68 'h' + {3501, 7, 24, 10, 2, -23}, // 0x69 'i' + {3522, 11, 31, 14, 0, -23}, // 0x6A 'j' + {3565, 18, 24, 19, 1, -23}, // 0x6B 'k' + {3619, 7, 24, 10, 1, -23}, // 0x6C 'l' + {3640, 27, 16, 29, 1, -15}, // 0x6D 'm' + {3694, 17, 16, 19, 1, -15}, // 0x6E 'n' + {3728, 17, 16, 18, 1, -15}, // 0x6F 'o' + {3762, 19, 23, 19, 0, -15}, // 0x70 'p' + {3817, 17, 23, 19, 1, -15}, // 0x71 'q' + {3866, 13, 16, 15, 1, -15}, // 0x72 'r' + {3892, 12, 16, 14, 1, -15}, // 0x73 's' + {3916, 10, 21, 12, 1, -20}, // 0x74 't' + {3943, 18, 16, 20, 1, -15}, // 0x75 'u' + {3979, 17, 16, 17, 0, -15}, // 0x76 'v' + {4013, 24, 16, 25, 0, -15}, // 0x77 'w' + {4061, 16, 16, 18, 1, -15}, // 0x78 'x' + {4093, 16, 23, 17, 0, -15}, // 0x79 'y' + {4139, 14, 16, 16, 0, -15}, // 0x7A 'z' + {4167, 11, 31, 14, 1, -24}, // 0x7B '{' + {4210, 3, 25, 8, 2, -23}, // 0x7C '|' + {4220, 11, 31, 14, 3, -24}, // 0x7D '}' + {4263, 16, 5, 18, 1, -11}}; // 0x7E '~' + +const GFXfont FreeSerifBold18pt7b PROGMEM = { + (uint8_t *)FreeSerifBold18pt7bBitmaps, + (GFXglyph *)FreeSerifBold18pt7bGlyphs, 0x20, 0x7E, 42}; + +// Approx. 4945 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold24pt7b.h new file mode 100644 index 0000000..a185051 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold24pt7b.h @@ -0,0 +1,758 @@ +const uint8_t FreeSerifBold24pt7bBitmaps[] PROGMEM = { + 0x3C, 0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0x7C, 0x7C, + 0x3C, 0x3C, 0x38, 0x38, 0x38, 0x38, 0x18, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x70, 0x07, + 0x7C, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xC0, + 0x3E, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xC4, 0x00, 0xE2, + 0x00, 0x20, 0x00, 0xF0, 0x70, 0x01, 0xC0, 0xE0, 0x03, 0x81, 0xC0, 0x0F, + 0x07, 0x80, 0x1E, 0x0F, 0x00, 0x3C, 0x1E, 0x00, 0x78, 0x3C, 0x00, 0xF0, + 0x78, 0x01, 0xC0, 0xE0, 0x03, 0x81, 0xC0, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, + 0xF3, 0xFF, 0xFF, 0xE0, 0x78, 0x3C, 0x00, 0xF0, 0x78, 0x01, 0xC0, 0xE0, + 0x03, 0x81, 0xC0, 0x0F, 0x07, 0x80, 0x1E, 0x0F, 0x00, 0x3C, 0x1E, 0x0F, + 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0x03, 0x81, 0xC0, 0x0F, + 0x07, 0x80, 0x1E, 0x0F, 0x00, 0x3C, 0x1E, 0x00, 0x78, 0x3C, 0x00, 0xF0, + 0x78, 0x01, 0xE0, 0xE0, 0x03, 0x81, 0xC0, 0x07, 0x07, 0x80, 0x1E, 0x0F, + 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, + 0x7F, 0xF0, 0x0F, 0x37, 0xE0, 0xE1, 0x8F, 0x8E, 0x0C, 0x3C, 0x70, 0x60, + 0xE7, 0x83, 0x03, 0x3C, 0x18, 0x19, 0xF0, 0xC0, 0x4F, 0xC6, 0x02, 0x7F, + 0xF0, 0x03, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xF0, + 0x03, 0xFF, 0xE0, 0x0F, 0xFF, 0x80, 0x1F, 0xFE, 0x00, 0x3F, 0xF8, 0x01, + 0xFF, 0xC0, 0x0C, 0xFF, 0x00, 0x63, 0xFA, 0x03, 0x0F, 0xD0, 0x18, 0x3E, + 0x80, 0xC1, 0xF6, 0x06, 0x0F, 0xB8, 0x30, 0x79, 0xC1, 0x87, 0xCF, 0x0C, + 0x3C, 0x7E, 0x67, 0xC0, 0xFF, 0xF8, 0x00, 0xFE, 0x00, 0x00, 0xC0, 0x00, + 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x3E, 0x00, 0x0C, 0x00, 0x0F, 0xF0, 0x03, 0x80, 0x07, 0xE7, 0x03, + 0xE0, 0x01, 0xF8, 0x7F, 0xFC, 0x00, 0x3E, 0x07, 0xF7, 0x00, 0x0F, 0xC0, + 0x80, 0xE0, 0x03, 0xF0, 0x10, 0x38, 0x00, 0x7E, 0x02, 0x07, 0x00, 0x0F, + 0x80, 0x41, 0xC0, 0x03, 0xF0, 0x10, 0x30, 0x00, 0x7E, 0x02, 0x0E, 0x00, + 0x0F, 0x80, 0xC1, 0x80, 0x01, 0xF0, 0x10, 0x70, 0x00, 0x3E, 0x06, 0x1C, + 0x00, 0x07, 0xC1, 0x83, 0x80, 0x00, 0x7C, 0x60, 0xE0, 0x1F, 0x07, 0xF8, + 0x18, 0x0F, 0xF8, 0x7C, 0x07, 0x07, 0xF1, 0x00, 0x00, 0xC1, 0xF8, 0x10, + 0x00, 0x38, 0x3F, 0x02, 0x00, 0x06, 0x0F, 0xC0, 0x40, 0x01, 0xC3, 0xF0, + 0x08, 0x00, 0x30, 0x7E, 0x01, 0x00, 0x0E, 0x1F, 0x80, 0x40, 0x03, 0x83, + 0xF0, 0x08, 0x00, 0x60, 0x7E, 0x01, 0x00, 0x1C, 0x0F, 0x80, 0x40, 0x03, + 0x01, 0xF0, 0x18, 0x00, 0xE0, 0x3E, 0x02, 0x00, 0x18, 0x03, 0xC0, 0xC0, + 0x07, 0x00, 0x7C, 0x70, 0x00, 0xC0, 0x07, 0xFC, 0x00, 0x38, 0x00, 0x7E, + 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x07, 0x8F, + 0xE0, 0x00, 0x03, 0xC1, 0xF8, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x7C, + 0x07, 0xC0, 0x00, 0x1F, 0x01, 0xF0, 0x00, 0x07, 0xE0, 0x7C, 0x00, 0x01, + 0xF8, 0x1E, 0x00, 0x00, 0x7F, 0x07, 0x80, 0x00, 0x1F, 0xE3, 0x80, 0x00, + 0x03, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x1F, 0xE0, 0x3F, + 0xF0, 0x07, 0xFC, 0x01, 0xF0, 0x07, 0xFF, 0x00, 0x78, 0x07, 0xBF, 0xE0, + 0x1C, 0x03, 0x87, 0xFC, 0x07, 0x01, 0xE0, 0xFF, 0x81, 0x80, 0xF0, 0x3F, + 0xE0, 0xC0, 0x7C, 0x07, 0xFC, 0x30, 0x1F, 0x00, 0xFF, 0x98, 0x0F, 0xC0, + 0x3F, 0xFC, 0x03, 0xF0, 0x07, 0xFF, 0x00, 0xFE, 0x00, 0xFF, 0x80, 0x3F, + 0x80, 0x3F, 0xF0, 0x0F, 0xF0, 0x07, 0xFE, 0x03, 0xFC, 0x00, 0xFF, 0x81, + 0x7F, 0x80, 0x3F, 0xF8, 0xDF, 0xF0, 0x1F, 0xFF, 0xE3, 0xFF, 0x0E, 0xFF, + 0xF8, 0xFF, 0xFE, 0x1F, 0xFC, 0x0F, 0xFE, 0x03, 0xFE, 0x00, 0xFE, 0x00, + 0x3E, 0x00, 0x77, 0xFF, 0xFF, 0xFF, 0xEE, 0x73, 0x9C, 0xE2, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x60, 0x1C, 0x03, 0x80, 0x70, 0x06, 0x00, 0xE0, 0x1C, + 0x01, 0xC0, 0x3C, 0x03, 0xC0, 0x78, 0x07, 0x80, 0x78, 0x07, 0x80, 0xF8, + 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, + 0x0F, 0x80, 0x78, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x3C, 0x01, 0xC0, 0x1C, + 0x00, 0xE0, 0x0E, 0x00, 0x70, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x03, 0x00, + 0x10, 0x00, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x06, 0x00, + 0x70, 0x03, 0x80, 0x38, 0x03, 0xC0, 0x3C, 0x03, 0xE0, 0x1E, 0x01, 0xE0, + 0x1E, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, + 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x3C, 0x03, 0xC0, + 0x38, 0x03, 0x80, 0x70, 0x07, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x0C, + 0x00, 0x80, 0x00, 0x01, 0xC0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, + 0x03, 0xE0, 0x3C, 0x78, 0xEF, 0x9C, 0x7B, 0xF7, 0x3F, 0xFE, 0xDF, 0x8F, + 0xFF, 0xC0, 0x7F, 0x00, 0x3F, 0xC0, 0x7E, 0xBF, 0x3F, 0x77, 0xEF, 0x9C, + 0xFF, 0xC7, 0x1E, 0x63, 0xE3, 0x80, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, + 0x01, 0xC0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xE0, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, + 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, + 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x07, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x3C, 0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x07, 0x06, 0x06, 0x0C, + 0x18, 0x30, 0x60, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3C, + 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x00, 0x1E, 0x00, 0x7C, 0x00, + 0xF0, 0x01, 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x7C, 0x00, 0xF0, + 0x01, 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x7C, 0x00, 0xF0, 0x01, + 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x7C, 0x00, 0xF0, 0x01, 0xE0, + 0x03, 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0xF0, 0x01, 0xE0, 0x03, + 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0xF0, 0x00, 0x00, 0xFC, 0x00, + 0x0F, 0x3C, 0x00, 0x78, 0x78, 0x03, 0xE1, 0xF0, 0x1F, 0x03, 0xE0, 0x7C, + 0x0F, 0x83, 0xF0, 0x3F, 0x0F, 0xC0, 0xFC, 0x7F, 0x03, 0xF9, 0xFC, 0x0F, + 0xE7, 0xF0, 0x3F, 0xBF, 0xC0, 0xFE, 0xFF, 0x03, 0xFF, 0xFC, 0x0F, 0xFF, + 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFC, 0x0F, 0xFF, 0xF0, + 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFC, 0x0F, 0xFF, 0xF0, 0x3F, + 0x9F, 0xC0, 0xFE, 0x7F, 0x03, 0xF9, 0xFC, 0x0F, 0xE3, 0xF0, 0x3F, 0x0F, + 0xC0, 0xFC, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x80, 0xF8, 0x7C, 0x01, 0xE1, + 0xE0, 0x03, 0xCF, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x18, 0x00, 0x1E, 0x00, + 0x1F, 0x80, 0x1F, 0xE0, 0x1F, 0xF8, 0x1D, 0xFE, 0x00, 0x3F, 0x80, 0x0F, + 0xE0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF8, + 0x00, 0xFE, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x00, 0xFE, 0x00, + 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x3F, 0x80, 0x0F, + 0xE0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF8, + 0x00, 0xFE, 0x00, 0x7F, 0x80, 0x3F, 0xF8, 0xFF, 0xFF, 0xC0, 0x00, 0xFC, + 0x00, 0x1F, 0xF8, 0x03, 0xFF, 0xE0, 0x3F, 0xFF, 0x81, 0xFF, 0xFC, 0x1C, + 0x1F, 0xF1, 0xC0, 0x7F, 0x8C, 0x01, 0xFC, 0x40, 0x0F, 0xE0, 0x00, 0x3F, + 0x00, 0x01, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, + 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x00, + 0x00, 0x70, 0x00, 0x07, 0x80, 0x00, 0x38, 0x00, 0x03, 0x80, 0x00, 0x38, + 0x01, 0x03, 0x80, 0x18, 0x38, 0x00, 0x81, 0x80, 0x1C, 0x1F, 0xFF, 0xE1, + 0xFF, 0xFF, 0x1F, 0xFF, 0xF9, 0xFF, 0xFF, 0x9F, 0xFF, 0xFC, 0xFF, 0xFF, + 0xE0, 0x00, 0xFE, 0x00, 0x3F, 0xFC, 0x03, 0xFF, 0xF0, 0x30, 0xFF, 0xC2, + 0x01, 0xFE, 0x30, 0x0F, 0xF1, 0x00, 0x3F, 0x80, 0x01, 0xFC, 0x00, 0x0F, + 0xE0, 0x00, 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x01, 0xF0, 0x00, + 0x1F, 0xC0, 0x03, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0x7F, 0xF0, 0x00, 0xFF, + 0x80, 0x03, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0x80, 0x00, 0xFC, 0x00, + 0x07, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0x80, 0x00, 0x3C, + 0x00, 0x01, 0xC7, 0x80, 0x0E, 0x7F, 0x00, 0xE3, 0xFC, 0x06, 0x1F, 0xF8, + 0xE0, 0x7F, 0xFC, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x1E, + 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x00, + 0xFF, 0x00, 0x0D, 0xF8, 0x00, 0xEF, 0xC0, 0x06, 0x7E, 0x00, 0x63, 0xF0, + 0x07, 0x1F, 0x80, 0x30, 0xFC, 0x03, 0x07, 0xE0, 0x38, 0x3F, 0x03, 0x81, + 0xF8, 0x18, 0x0F, 0xC1, 0xC0, 0x7E, 0x1C, 0x03, 0xF0, 0xC0, 0x1F, 0x8E, + 0x00, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0xFC, 0x00, + 0x07, 0xE0, 0x00, 0x3F, 0x00, 0x01, 0xF8, 0x00, 0x0F, 0xC0, 0x07, 0xFF, + 0xF0, 0x7F, 0xFF, 0x0F, 0xFF, 0xE0, 0xFF, 0xFE, 0x0F, 0xFF, 0xE1, 0xFF, + 0xFC, 0x18, 0x00, 0x01, 0x80, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x3F, + 0x80, 0x03, 0xFF, 0x80, 0x7F, 0xFE, 0x07, 0xFF, 0xF0, 0x7F, 0xFF, 0x87, + 0xFF, 0xFC, 0x7F, 0xFF, 0xC0, 0x07, 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0x7E, + 0x00, 0x03, 0xE0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, + 0xC0, 0x00, 0x0C, 0x78, 0x00, 0x8F, 0xE0, 0x18, 0xFF, 0x87, 0x0F, 0xFF, + 0xE0, 0x7F, 0xF8, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1F, 0x00, + 0x07, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, + 0x00, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0xFE, 0x00, 0x0F, 0xF0, 0x00, 0x7F, + 0x00, 0x07, 0xF8, 0x00, 0x3F, 0xFF, 0x01, 0xFF, 0xFE, 0x1F, 0xF1, 0xFC, + 0xFF, 0x07, 0xE7, 0xF8, 0x3F, 0xBF, 0xC1, 0xFD, 0xFE, 0x07, 0xFF, 0xF0, + 0x3F, 0xFF, 0x81, 0xFF, 0xFC, 0x0F, 0xFF, 0xE0, 0x7F, 0xFF, 0x03, 0xFB, + 0xF8, 0x1F, 0xDF, 0xC0, 0xFE, 0xFE, 0x07, 0xE3, 0xF0, 0x3F, 0x1F, 0xC1, + 0xF0, 0x7E, 0x0F, 0x01, 0xF0, 0xF8, 0x03, 0xC7, 0x00, 0x07, 0xE0, 0x00, + 0x3F, 0xFF, 0xF9, 0xFF, 0xFF, 0xDF, 0xFF, 0xFE, 0xFF, 0xFF, 0xE7, 0xFF, + 0xFF, 0x3F, 0xFF, 0xF9, 0x80, 0x07, 0x98, 0x00, 0x3C, 0xC0, 0x03, 0xE4, + 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, + 0xC0, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0xF0, 0x00, + 0x07, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x01, 0xF0, + 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x07, 0xC0, 0x00, 0x3C, 0x00, 0x01, + 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, + 0x01, 0xFE, 0x00, 0x38, 0x7C, 0x07, 0x80, 0xF0, 0x78, 0x07, 0xC3, 0xC0, + 0x1F, 0x3E, 0x00, 0xF9, 0xF0, 0x07, 0xCF, 0xC0, 0x3E, 0x7E, 0x01, 0xF3, + 0xF8, 0x0F, 0x1F, 0xE0, 0xF8, 0x7F, 0xC7, 0x83, 0xFF, 0xF0, 0x0F, 0xFE, + 0x00, 0x7F, 0xFC, 0x01, 0xFF, 0xF0, 0x03, 0xFF, 0xC0, 0x1F, 0xFF, 0x03, + 0xBF, 0xFC, 0x7C, 0x7F, 0xE7, 0xC1, 0xFF, 0x3E, 0x07, 0xFF, 0xE0, 0x1F, + 0xFF, 0x00, 0x7F, 0xF8, 0x03, 0xFF, 0xC0, 0x0F, 0xFE, 0x00, 0x7F, 0xF0, + 0x03, 0xE7, 0x80, 0x1F, 0x3E, 0x01, 0xF0, 0xF8, 0x0F, 0x83, 0xE1, 0xF8, + 0x0F, 0xFF, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x1C, 0x3C, 0x00, + 0xF0, 0x78, 0x07, 0x81, 0xF8, 0x3E, 0x07, 0xE1, 0xF8, 0x0F, 0xC7, 0xE0, + 0x3F, 0x3F, 0x80, 0xFE, 0xFE, 0x03, 0xFB, 0xF8, 0x0F, 0xFF, 0xE0, 0x3F, + 0xFF, 0x80, 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x0F, 0xFF, 0xE0, 0x3F, 0xDF, + 0xC0, 0xFF, 0x7F, 0x03, 0xFC, 0xFC, 0x0F, 0xF3, 0xFC, 0x7F, 0x83, 0xFF, + 0xFE, 0x07, 0xF7, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0x00, 0x03, 0xF8, + 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x00, 0x03, 0xF8, 0x00, 0x0F, 0xC0, 0x00, + 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x01, 0xF0, 0x00, 0x3F, 0x00, + 0x03, 0x80, 0x00, 0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7E, 0x3C, 0x3C, 0x3F, 0x3F, 0xDF, 0xEF, 0xF7, 0xF9, 0xF8, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x7F, + 0xBF, 0xFF, 0xFF, 0xFB, 0xFC, 0xFE, 0x07, 0x03, 0x01, 0x81, 0x81, 0x81, + 0x83, 0x81, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xC0, 0x01, 0xFF, 0x80, 0x01, + 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x01, + 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x00, 0x3F, 0xE0, 0x00, + 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x7F, + 0xE0, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x03, 0xFF, 0x00, + 0x00, 0x3F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0xC0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x03, 0xFC, 0x00, + 0x00, 0xFF, 0xC0, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x0F, + 0xFE, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xE0, + 0x00, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x1F, 0xF0, 0x00, 0x1F, + 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x1F, + 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x3F, 0xF0, 0x00, 0x0F, + 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x07, 0xF0, 0x07, 0xFF, 0x03, 0x87, 0xE1, 0xC0, 0xFC, 0xF0, 0x3F, + 0xBE, 0x07, 0xEF, 0xC1, 0xFF, 0xF0, 0x7F, 0xFC, 0x1F, 0xDF, 0x07, 0xF7, + 0x81, 0xFC, 0x00, 0xFE, 0x00, 0x3F, 0x80, 0x1F, 0xC0, 0x07, 0xE0, 0x03, + 0xE0, 0x00, 0xF0, 0x00, 0x70, 0x00, 0x18, 0x00, 0x04, 0x00, 0x01, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x1F, 0x80, 0x0F, 0xF0, 0x03, 0xFC, 0x00, 0xFF, 0x00, 0x3F, + 0xC0, 0x07, 0xE0, 0x00, 0xF0, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1F, + 0xFF, 0xC0, 0x00, 0x1F, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x1E, 0x00, 0x3C, + 0x00, 0x03, 0x80, 0x3C, 0x00, 0x00, 0xE0, 0x3C, 0x00, 0x00, 0x30, 0x3E, + 0x00, 0x00, 0x0C, 0x3E, 0x00, 0x3C, 0x37, 0x1F, 0x00, 0x7E, 0xF1, 0x9F, + 0x00, 0x7C, 0xF8, 0xCF, 0x80, 0x78, 0x7C, 0x37, 0xC0, 0x7C, 0x3C, 0x1F, + 0xC0, 0x3C, 0x1E, 0x0F, 0xE0, 0x3C, 0x0F, 0x07, 0xF0, 0x3E, 0x0F, 0x03, + 0xF8, 0x1E, 0x07, 0x81, 0xFC, 0x0F, 0x03, 0xC0, 0xFE, 0x0F, 0x03, 0xE0, + 0x7F, 0x07, 0x81, 0xE0, 0x6F, 0x83, 0xC1, 0xF0, 0x37, 0xC1, 0xE1, 0x78, + 0x31, 0xF0, 0xF9, 0xBC, 0x18, 0xF8, 0x3F, 0x9E, 0x38, 0x3C, 0x0F, 0x0F, + 0xF8, 0x1F, 0x00, 0x01, 0xF0, 0x07, 0x80, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0xC0, 0x01, 0xF8, 0x03, 0xE0, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x03, + 0xFF, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, + 0x03, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, + 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, + 0x37, 0xF8, 0x00, 0x00, 0x33, 0xF8, 0x00, 0x00, 0x33, 0xFC, 0x00, 0x00, + 0x61, 0xFC, 0x00, 0x00, 0x61, 0xFE, 0x00, 0x00, 0xC1, 0xFE, 0x00, 0x00, + 0xC0, 0xFF, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x01, 0x80, 0x7F, 0x00, 0x01, + 0x80, 0x7F, 0x80, 0x03, 0x80, 0x7F, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, + 0xFF, 0xFF, 0xC0, 0x07, 0x00, 0x3F, 0xC0, 0x06, 0x00, 0x1F, 0xE0, 0x0E, + 0x00, 0x1F, 0xE0, 0x0C, 0x00, 0x0F, 0xF0, 0x0C, 0x00, 0x0F, 0xF0, 0x1C, + 0x00, 0x0F, 0xF8, 0x1C, 0x00, 0x0F, 0xF8, 0x7E, 0x00, 0x0F, 0xFC, 0xFF, + 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, + 0x7F, 0x00, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0xFF, 0x01, 0xFE, + 0x0F, 0xF0, 0x1F, 0xE0, 0xFF, 0x01, 0xFE, 0x0F, 0xF0, 0x1F, 0xE0, 0xFF, + 0x01, 0xFE, 0x0F, 0xF0, 0x1F, 0xC0, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, + 0x00, 0xFF, 0x0F, 0xC0, 0x0F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xC0, 0x0F, + 0xF0, 0xFF, 0x00, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x1F, 0xE0, 0xFF, 0x01, + 0xFE, 0x0F, 0xF0, 0x0F, 0xF0, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x0F, 0xF0, + 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x0F, 0xF0, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, + 0x0F, 0xE0, 0xFF, 0x01, 0xFE, 0x0F, 0xF0, 0x1F, 0xC0, 0xFF, 0x87, 0xF0, + 0x3F, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x08, 0x01, + 0xFF, 0xF0, 0x60, 0x0F, 0xC1, 0xF9, 0x80, 0xFC, 0x01, 0xFE, 0x07, 0xE0, + 0x01, 0xF8, 0x3F, 0x00, 0x03, 0xE1, 0xFC, 0x00, 0x07, 0x87, 0xE0, 0x00, + 0x1E, 0x3F, 0x80, 0x00, 0x38, 0xFE, 0x00, 0x00, 0x67, 0xF8, 0x00, 0x01, + 0x9F, 0xC0, 0x00, 0x02, 0x7F, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x03, 0xFC, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x87, 0xF0, 0x00, 0x07, 0x0F, 0xE0, 0x00, 0x38, 0x1F, 0x80, 0x01, 0xC0, + 0x3F, 0x00, 0x1E, 0x00, 0x7F, 0x01, 0xE0, 0x00, 0x7F, 0xFF, 0x00, 0x00, + 0x3F, 0xE0, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x0F, + 0xF8, 0x7F, 0x80, 0x0F, 0xF0, 0x1F, 0xC0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, + 0xF0, 0x07, 0xF0, 0x0F, 0xF0, 0x03, 0xF8, 0x0F, 0xF0, 0x03, 0xFC, 0x0F, + 0xF0, 0x01, 0xFC, 0x0F, 0xF0, 0x01, 0xFE, 0x0F, 0xF0, 0x01, 0xFE, 0x0F, + 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, + 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, + 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, + 0xF0, 0x00, 0xFE, 0x0F, 0xF0, 0x00, 0xFE, 0x0F, 0xF0, 0x01, 0xFE, 0x0F, + 0xF0, 0x01, 0xFC, 0x0F, 0xF0, 0x01, 0xFC, 0x0F, 0xF0, 0x03, 0xF8, 0x0F, + 0xF0, 0x03, 0xF0, 0x0F, 0xF0, 0x07, 0xE0, 0x0F, 0xF0, 0x0F, 0xC0, 0x0F, + 0xF8, 0x3F, 0x80, 0x1F, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, + 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFC, 0x1F, 0xE0, 0x1F, 0xC1, 0xFE, 0x00, + 0x3C, 0x1F, 0xE0, 0x01, 0xC1, 0xFE, 0x00, 0x0C, 0x1F, 0xE0, 0x00, 0xC1, + 0xFE, 0x00, 0x04, 0x1F, 0xE0, 0x20, 0x41, 0xFE, 0x02, 0x00, 0x1F, 0xE0, + 0x60, 0x01, 0xFE, 0x06, 0x00, 0x1F, 0xE0, 0xE0, 0x01, 0xFE, 0x1E, 0x00, + 0x1F, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x1F, 0xE3, 0xE0, 0x01, 0xFE, + 0x0E, 0x00, 0x1F, 0xE0, 0x60, 0x01, 0xFE, 0x06, 0x00, 0x1F, 0xE0, 0x20, + 0x01, 0xFE, 0x02, 0x00, 0x1F, 0xE0, 0x00, 0x11, 0xFE, 0x00, 0x03, 0x1F, + 0xE0, 0x00, 0x71, 0xFE, 0x00, 0x07, 0x1F, 0xE0, 0x00, 0xE1, 0xFE, 0x00, + 0x1E, 0x1F, 0xE0, 0x03, 0xE3, 0xFF, 0x01, 0xFE, 0xFF, 0xFF, 0xFF, 0xEF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xC7, 0xFC, 0x07, + 0xE3, 0xFC, 0x00, 0xF1, 0xFE, 0x00, 0x38, 0xFF, 0x00, 0x0C, 0x7F, 0x80, + 0x06, 0x3F, 0xC0, 0x01, 0x1F, 0xE0, 0x20, 0x8F, 0xF0, 0x10, 0x07, 0xF8, + 0x18, 0x03, 0xFC, 0x0C, 0x01, 0xFE, 0x0E, 0x00, 0xFF, 0x1F, 0x00, 0x7F, + 0xFF, 0x80, 0x3F, 0xFF, 0xC0, 0x1F, 0xE3, 0xE0, 0x0F, 0xF0, 0x70, 0x07, + 0xF8, 0x18, 0x03, 0xFC, 0x0C, 0x01, 0xFE, 0x02, 0x00, 0xFF, 0x01, 0x00, + 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, + 0x07, 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0xC0, 0x01, 0xFF, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x08, 0x00, + 0x3F, 0xFE, 0x0C, 0x00, 0x3F, 0x07, 0xC6, 0x00, 0x7E, 0x00, 0xFF, 0x00, + 0x7E, 0x00, 0x1F, 0x80, 0x7E, 0x00, 0x07, 0xC0, 0x7F, 0x00, 0x01, 0xE0, + 0x3F, 0x00, 0x00, 0x70, 0x3F, 0x80, 0x00, 0x38, 0x1F, 0xC0, 0x00, 0x0C, + 0x1F, 0xE0, 0x00, 0x06, 0x0F, 0xE0, 0x00, 0x01, 0x07, 0xF0, 0x00, 0x00, + 0x07, 0xF8, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF0, 0x03, 0xFF, 0xFF, 0xF8, 0x00, + 0x3F, 0xF1, 0xFC, 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x07, 0xF8, 0x7F, 0x80, + 0x03, 0xFC, 0x1F, 0xC0, 0x01, 0xFE, 0x0F, 0xE0, 0x00, 0xFF, 0x03, 0xF8, + 0x00, 0x7F, 0x80, 0xFC, 0x00, 0x3F, 0xC0, 0x3F, 0x00, 0x1F, 0xE0, 0x0F, + 0xC0, 0x0F, 0xF0, 0x03, 0xF8, 0x1F, 0xF0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, + 0x07, 0xFE, 0x00, 0x00, 0xFF, 0xFC, 0x1F, 0xFF, 0x9F, 0xF8, 0x03, 0xFF, + 0x07, 0xF8, 0x00, 0xFF, 0x03, 0xFC, 0x00, 0x7F, 0x81, 0xFE, 0x00, 0x3F, + 0xC0, 0xFF, 0x00, 0x1F, 0xE0, 0x7F, 0x80, 0x0F, 0xF0, 0x3F, 0xC0, 0x07, + 0xF8, 0x1F, 0xE0, 0x03, 0xFC, 0x0F, 0xF0, 0x01, 0xFE, 0x07, 0xF8, 0x00, + 0xFF, 0x03, 0xFC, 0x00, 0x7F, 0x81, 0xFE, 0x00, 0x3F, 0xC0, 0xFF, 0x00, + 0x1F, 0xE0, 0x7F, 0x80, 0x0F, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, + 0xFF, 0xFC, 0x0F, 0xF0, 0x01, 0xFE, 0x07, 0xF8, 0x00, 0xFF, 0x03, 0xFC, + 0x00, 0x7F, 0x81, 0xFE, 0x00, 0x3F, 0xC0, 0xFF, 0x00, 0x1F, 0xE0, 0x7F, + 0x80, 0x0F, 0xF0, 0x3F, 0xC0, 0x07, 0xF8, 0x1F, 0xE0, 0x03, 0xFC, 0x0F, + 0xF0, 0x01, 0xFE, 0x07, 0xF8, 0x00, 0xFF, 0x03, 0xFC, 0x00, 0x7F, 0x81, + 0xFE, 0x00, 0x3F, 0xC0, 0xFF, 0x00, 0x1F, 0xE0, 0xFF, 0xC0, 0x1F, 0xF9, + 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFE, 0x7F, 0xE0, 0x7F, 0x80, 0xFF, 0x01, + 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, + 0x80, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, + 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x0F, + 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, 0xFE, 0x03, 0xFC, + 0x0F, 0xFC, 0x7F, 0xFF, 0x01, 0xFF, 0xFC, 0x00, 0xFF, 0xC0, 0x01, 0xFE, + 0x00, 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, + 0x07, 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, + 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, + 0x00, 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x00, + 0x1F, 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x1F, + 0xE0, 0x00, 0x7F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF8, 0x78, 0x1F, 0xE3, + 0xF0, 0x7F, 0x8F, 0xC1, 0xFC, 0x3F, 0x07, 0xF0, 0xFC, 0x1F, 0xC1, 0xE0, + 0xFE, 0x07, 0xC3, 0xF0, 0x0F, 0xFF, 0x80, 0x07, 0xF0, 0x00, 0xFF, 0xFC, + 0x1F, 0xFF, 0x0F, 0xFC, 0x00, 0xFF, 0x01, 0xFE, 0x00, 0x1E, 0x00, 0x7F, + 0x80, 0x07, 0x00, 0x1F, 0xE0, 0x03, 0x80, 0x07, 0xF8, 0x01, 0xC0, 0x01, + 0xFE, 0x00, 0xE0, 0x00, 0x7F, 0x80, 0x70, 0x00, 0x1F, 0xE0, 0x38, 0x00, + 0x07, 0xF8, 0x1C, 0x00, 0x01, 0xFE, 0x0E, 0x00, 0x00, 0x7F, 0x87, 0x00, + 0x00, 0x1F, 0xE3, 0xC0, 0x00, 0x07, 0xF9, 0xF8, 0x00, 0x01, 0xFE, 0xFE, + 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x07, 0xFD, + 0xFF, 0x00, 0x01, 0xFE, 0x7F, 0xE0, 0x00, 0x7F, 0x8F, 0xF8, 0x00, 0x1F, + 0xE1, 0xFF, 0x00, 0x07, 0xF8, 0x3F, 0xE0, 0x01, 0xFE, 0x07, 0xFC, 0x00, + 0x7F, 0x81, 0xFF, 0x80, 0x1F, 0xE0, 0x3F, 0xE0, 0x07, 0xF8, 0x07, 0xFC, + 0x01, 0xFE, 0x00, 0xFF, 0x80, 0x7F, 0x80, 0x1F, 0xF0, 0x1F, 0xE0, 0x07, + 0xFE, 0x07, 0xF8, 0x00, 0xFF, 0x83, 0xFF, 0x00, 0x3F, 0xF3, 0xFF, 0xF0, + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x1F, 0xE0, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, + 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, + 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xE0, + 0x00, 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, + 0x1F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x01, 0x1F, 0xE0, 0x00, 0x31, 0xFE, + 0x00, 0x03, 0x1F, 0xE0, 0x00, 0x71, 0xFE, 0x00, 0x07, 0x1F, 0xE0, 0x00, + 0xE1, 0xFE, 0x00, 0x1E, 0x1F, 0xE0, 0x07, 0xE3, 0xFF, 0x01, 0xFE, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0x7F, 0xF0, 0x00, 0x01, 0xFF, 0xE1, + 0xFF, 0x00, 0x00, 0x3F, 0xF0, 0x1F, 0xE0, 0x00, 0x0F, 0xFC, 0x03, 0xFC, + 0x00, 0x01, 0xFF, 0x80, 0x7F, 0xC0, 0x00, 0x2F, 0xF0, 0x0B, 0xF8, 0x00, + 0x0D, 0xFE, 0x01, 0x7F, 0x80, 0x01, 0xBF, 0xC0, 0x27, 0xF0, 0x00, 0x67, + 0xF8, 0x04, 0xFF, 0x00, 0x0C, 0xFF, 0x00, 0x8F, 0xE0, 0x03, 0x1F, 0xE0, + 0x11, 0xFE, 0x00, 0x63, 0xFC, 0x02, 0x3F, 0xC0, 0x08, 0x7F, 0x80, 0x43, + 0xF8, 0x03, 0x0F, 0xF0, 0x08, 0x7F, 0x80, 0x61, 0xFE, 0x01, 0x07, 0xF0, + 0x18, 0x3F, 0xC0, 0x20, 0xFF, 0x03, 0x07, 0xF8, 0x04, 0x0F, 0xE0, 0xC0, + 0xFF, 0x00, 0x81, 0xFE, 0x18, 0x1F, 0xE0, 0x10, 0x3F, 0xC6, 0x03, 0xFC, + 0x02, 0x03, 0xF8, 0xC0, 0x7F, 0x80, 0x40, 0x7F, 0x98, 0x0F, 0xF0, 0x08, + 0x07, 0xF6, 0x01, 0xFE, 0x01, 0x00, 0xFF, 0xC0, 0x3F, 0xC0, 0x20, 0x0F, + 0xF0, 0x07, 0xF8, 0x04, 0x01, 0xFE, 0x00, 0xFF, 0x00, 0x80, 0x1F, 0x80, + 0x1F, 0xE0, 0x10, 0x03, 0xF0, 0x03, 0xFC, 0x02, 0x00, 0x7E, 0x00, 0x7F, + 0x80, 0x40, 0x07, 0x80, 0x0F, 0xF0, 0x0C, 0x00, 0xF0, 0x01, 0xFE, 0x07, + 0xC0, 0x0C, 0x00, 0x7F, 0xE7, 0xFF, 0x01, 0x80, 0x3F, 0xFF, 0xFF, 0xC0, + 0x03, 0xFE, 0xFF, 0xC0, 0x01, 0xF0, 0xFF, 0xC0, 0x01, 0xC0, 0xFF, 0xC0, + 0x01, 0x80, 0xFF, 0x80, 0x03, 0x01, 0xFF, 0x80, 0x06, 0x03, 0xFF, 0x80, + 0x0C, 0x07, 0xFF, 0x80, 0x18, 0x0D, 0xFF, 0x80, 0x30, 0x19, 0xFF, 0x00, + 0x60, 0x31, 0xFF, 0x00, 0xC0, 0x61, 0xFF, 0x01, 0x80, 0xC1, 0xFF, 0x03, + 0x01, 0x83, 0xFF, 0x06, 0x03, 0x03, 0xFE, 0x0C, 0x06, 0x03, 0xFE, 0x18, + 0x0C, 0x03, 0xFE, 0x30, 0x18, 0x03, 0xFE, 0x60, 0x30, 0x03, 0xFE, 0xC0, + 0x60, 0x07, 0xFD, 0x80, 0xC0, 0x07, 0xFF, 0x01, 0x80, 0x07, 0xFE, 0x03, + 0x00, 0x07, 0xFC, 0x06, 0x00, 0x07, 0xF8, 0x0C, 0x00, 0x07, 0xF0, 0x18, + 0x00, 0x0F, 0xE0, 0x30, 0x00, 0x0F, 0xC0, 0x60, 0x00, 0x0F, 0x80, 0xC0, + 0x00, 0x0F, 0x01, 0xC0, 0x00, 0x0E, 0x0F, 0xC0, 0x00, 0x1C, 0x7F, 0xE0, + 0x00, 0x18, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x3F, + 0x07, 0xF0, 0x00, 0x7E, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x7E, + 0x00, 0x1F, 0xC0, 0x7F, 0x00, 0x07, 0xF0, 0x3F, 0x00, 0x03, 0xF8, 0x3F, + 0x80, 0x00, 0xFE, 0x3F, 0xC0, 0x00, 0x7F, 0x1F, 0xE0, 0x00, 0x3F, 0xCF, + 0xE0, 0x00, 0x0F, 0xEF, 0xF0, 0x00, 0x07, 0xF7, 0xF8, 0x00, 0x03, 0xFF, + 0xFC, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x7F, + 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0x0F, + 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x03, 0xFD, 0xFC, 0x00, 0x01, + 0xFC, 0xFE, 0x00, 0x01, 0xFE, 0x7F, 0x80, 0x00, 0xFF, 0x1F, 0xC0, 0x00, + 0x7F, 0x0F, 0xE0, 0x00, 0x3F, 0x83, 0xF8, 0x00, 0x3F, 0x80, 0xFC, 0x00, + 0x1F, 0x80, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x1F, 0x80, 0x03, 0xF8, + 0x3F, 0x80, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0xFF, + 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0x01, 0xFE, 0x1F, 0xE0, 0x7F, 0x81, 0xFC, + 0x1F, 0xE0, 0x7F, 0x87, 0xF8, 0x0F, 0xE1, 0xFE, 0x03, 0xFC, 0x7F, 0x80, + 0xFF, 0x1F, 0xE0, 0x3F, 0xC7, 0xF8, 0x0F, 0xF1, 0xFE, 0x03, 0xFC, 0x7F, + 0x80, 0xFF, 0x1F, 0xE0, 0x3F, 0x87, 0xF8, 0x1F, 0xE1, 0xFE, 0x07, 0xF0, + 0x7F, 0x87, 0xF8, 0x1F, 0xFF, 0xF8, 0x07, 0xFF, 0xF8, 0x01, 0xFE, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF8, 0x00, 0x01, 0xFE, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF8, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF8, 0x00, + 0x03, 0xFF, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, + 0x3F, 0xFF, 0x80, 0x00, 0x3F, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0xFC, 0x00, + 0x7E, 0x00, 0x3F, 0x00, 0x7E, 0x00, 0x1F, 0xC0, 0x7F, 0x00, 0x07, 0xF0, + 0x3F, 0x00, 0x03, 0xF8, 0x3F, 0x80, 0x00, 0xFE, 0x1F, 0xC0, 0x00, 0x7F, + 0x1F, 0xE0, 0x00, 0x3F, 0xCF, 0xE0, 0x00, 0x0F, 0xE7, 0xF0, 0x00, 0x07, + 0xF7, 0xF8, 0x00, 0x03, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xC0, 0x00, + 0x1F, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x00, + 0x03, 0xFD, 0xFC, 0x00, 0x01, 0xFC, 0xFE, 0x00, 0x01, 0xFE, 0x7F, 0x80, + 0x00, 0xFF, 0x1F, 0xC0, 0x00, 0x7F, 0x0F, 0xE0, 0x00, 0x3F, 0x83, 0xF8, + 0x00, 0x3F, 0x80, 0xFC, 0x00, 0x1F, 0x80, 0x3F, 0x00, 0x1F, 0x80, 0x0F, + 0xC0, 0x1F, 0x80, 0x03, 0xF0, 0x1F, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, + 0x0F, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, + 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0x80, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0xFF, 0xFF, 0xE0, + 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xC3, 0xFC, 0x00, 0xFF, 0x01, 0xFC, + 0x01, 0xFE, 0x03, 0xFC, 0x03, 0xFC, 0x03, 0xF8, 0x07, 0xF8, 0x07, 0xF8, + 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xE0, 0x1F, 0xE0, 0x3F, 0xC0, 0x3F, 0xC0, + 0x7F, 0x80, 0x7F, 0x80, 0xFF, 0x00, 0xFF, 0x01, 0xFE, 0x01, 0xFC, 0x03, + 0xFC, 0x07, 0xF8, 0x07, 0xF8, 0x1F, 0xE0, 0x0F, 0xF0, 0xFF, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x7F, 0x9F, 0xE0, 0x00, 0xFF, + 0x3F, 0xC0, 0x01, 0xFE, 0x3F, 0xC0, 0x03, 0xFC, 0x7F, 0xC0, 0x07, 0xF8, + 0x7F, 0xC0, 0x0F, 0xF0, 0x7F, 0x80, 0x1F, 0xE0, 0xFF, 0x80, 0x3F, 0xC0, + 0xFF, 0x80, 0x7F, 0x80, 0xFF, 0x00, 0xFF, 0x01, 0xFF, 0x01, 0xFE, 0x01, + 0xFF, 0x03, 0xFC, 0x01, 0xFF, 0x0F, 0xFC, 0x03, 0xFE, 0x7F, 0xFE, 0x03, + 0xFF, 0x03, 0xF8, 0x10, 0x7F, 0xF9, 0x87, 0xC1, 0xFC, 0x78, 0x03, 0xE7, + 0x80, 0x0F, 0x3C, 0x00, 0x3B, 0xE0, 0x01, 0xDF, 0x00, 0x06, 0xF8, 0x00, + 0x37, 0xE0, 0x00, 0xBF, 0x80, 0x01, 0xFF, 0x00, 0x0F, 0xFE, 0x00, 0x3F, + 0xFC, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xF0, 0x1F, 0xFF, 0xC0, 0x7F, 0xFF, + 0x00, 0xFF, 0xFC, 0x01, 0xFF, 0xE0, 0x03, 0xFF, 0x80, 0x07, 0xFC, 0x00, + 0x1F, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xFE, 0x00, 0x07, 0xF0, 0x00, 0x3F, + 0xC0, 0x01, 0xEE, 0x00, 0x0F, 0x78, 0x00, 0xF3, 0xE0, 0x0F, 0x9F, 0xC0, + 0xF8, 0x8F, 0xFF, 0x04, 0x0F, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xC3, 0xFF, 0x03, 0xFC, 0x0F, 0xE0, 0x3F, 0xC0, + 0x7C, 0x03, 0xFC, 0x03, 0xC0, 0x3F, 0xC0, 0x38, 0x03, 0xFC, 0x01, 0x80, + 0x3F, 0xC0, 0x10, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFC, + 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, + 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFC, 0x00, + 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, + 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xFF, 0xC0, 0xFF, + 0xFE, 0x07, 0xFC, 0xFF, 0xC0, 0x07, 0xC1, 0xFE, 0x00, 0x0E, 0x07, 0xF8, + 0x00, 0x18, 0x1F, 0xE0, 0x00, 0x60, 0x7F, 0x80, 0x01, 0x81, 0xFE, 0x00, + 0x06, 0x07, 0xF8, 0x00, 0x18, 0x1F, 0xE0, 0x00, 0x60, 0x7F, 0x80, 0x01, + 0x81, 0xFE, 0x00, 0x06, 0x07, 0xF8, 0x00, 0x18, 0x1F, 0xE0, 0x00, 0x60, + 0x7F, 0x80, 0x01, 0x81, 0xFE, 0x00, 0x06, 0x07, 0xF8, 0x00, 0x18, 0x1F, + 0xE0, 0x00, 0x60, 0x7F, 0x80, 0x01, 0x81, 0xFE, 0x00, 0x06, 0x07, 0xF8, + 0x00, 0x18, 0x1F, 0xE0, 0x00, 0x60, 0x7F, 0x80, 0x01, 0x81, 0xFE, 0x00, + 0x06, 0x07, 0xF8, 0x00, 0x18, 0x1F, 0xE0, 0x00, 0x60, 0x7F, 0x80, 0x03, + 0x00, 0xFF, 0x00, 0x0C, 0x03, 0xFC, 0x00, 0x30, 0x07, 0xF0, 0x01, 0x80, + 0x0F, 0xE0, 0x0E, 0x00, 0x1F, 0xE0, 0xF0, 0x00, 0x1F, 0xFF, 0x00, 0x00, + 0x1F, 0xF0, 0x00, 0xFF, 0xFF, 0x01, 0xFF, 0x9F, 0xFC, 0x00, 0x1F, 0x07, + 0xFC, 0x00, 0x07, 0x01, 0xFE, 0x00, 0x03, 0x00, 0x7F, 0x80, 0x03, 0x80, + 0x3F, 0xC0, 0x01, 0x80, 0x1F, 0xE0, 0x00, 0xC0, 0x07, 0xF8, 0x00, 0xC0, + 0x03, 0xFC, 0x00, 0x60, 0x00, 0xFF, 0x00, 0x30, 0x00, 0x7F, 0x80, 0x30, + 0x00, 0x1F, 0xE0, 0x18, 0x00, 0x0F, 0xF0, 0x18, 0x00, 0x07, 0xF8, 0x0C, + 0x00, 0x01, 0xFE, 0x06, 0x00, 0x00, 0xFF, 0x06, 0x00, 0x00, 0x3F, 0xC3, + 0x00, 0x00, 0x1F, 0xE3, 0x80, 0x00, 0x0F, 0xF1, 0x80, 0x00, 0x03, 0xFC, + 0xC0, 0x00, 0x01, 0xFE, 0xC0, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x3F, + 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x00, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFB, 0xFF, 0x00, 0xFF, + 0xC0, 0x1F, 0x0F, 0xF0, 0x03, 0xFC, 0x00, 0x70, 0x3F, 0x80, 0x0F, 0xE0, + 0x03, 0x81, 0xFE, 0x00, 0x7F, 0x80, 0x1C, 0x0F, 0xF0, 0x03, 0xFC, 0x00, + 0xC0, 0x3F, 0x80, 0x0F, 0xE0, 0x06, 0x01, 0xFE, 0x00, 0x7F, 0x00, 0x70, + 0x0F, 0xF0, 0x07, 0xFC, 0x03, 0x00, 0x3F, 0x80, 0x3F, 0xE0, 0x18, 0x01, + 0xFE, 0x01, 0xFF, 0x01, 0xC0, 0x0F, 0xF0, 0x1B, 0xFC, 0x0C, 0x00, 0x3F, + 0x80, 0xCF, 0xE0, 0x60, 0x01, 0xFE, 0x06, 0x7F, 0x07, 0x00, 0x0F, 0xF0, + 0x63, 0xFC, 0x30, 0x00, 0x3F, 0x83, 0x0F, 0xE1, 0x80, 0x01, 0xFE, 0x30, + 0x7F, 0x1C, 0x00, 0x07, 0xF1, 0x81, 0xFC, 0xC0, 0x00, 0x3F, 0x8C, 0x0F, + 0xE6, 0x00, 0x01, 0xFE, 0xC0, 0x7F, 0x70, 0x00, 0x07, 0xF6, 0x01, 0xFB, + 0x00, 0x00, 0x3F, 0xE0, 0x0F, 0xF8, 0x00, 0x01, 0xFF, 0x00, 0x7F, 0xC0, + 0x00, 0x07, 0xF8, 0x01, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x00, + 0x01, 0xFC, 0x00, 0x7F, 0x00, 0x00, 0x07, 0xE0, 0x01, 0xF0, 0x00, 0x00, + 0x3E, 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x00, 0x07, + 0x00, 0x01, 0xC0, 0x00, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x00, 0x01, 0xC0, + 0x00, 0x70, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0x0F, + 0xFF, 0x3F, 0xF8, 0x01, 0xF8, 0x1F, 0xF8, 0x01, 0xE0, 0x0F, 0xF8, 0x01, + 0xC0, 0x0F, 0xF8, 0x01, 0x80, 0x07, 0xFC, 0x03, 0x80, 0x03, 0xFE, 0x07, + 0x00, 0x03, 0xFE, 0x06, 0x00, 0x01, 0xFF, 0x0C, 0x00, 0x00, 0xFF, 0x9C, + 0x00, 0x00, 0xFF, 0x98, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x3F, 0xF0, + 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xFC, + 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x19, 0xFE, 0x00, 0x00, 0x31, 0xFF, + 0x00, 0x00, 0x70, 0xFF, 0x80, 0x00, 0x60, 0x7F, 0x80, 0x00, 0xC0, 0x7F, + 0xC0, 0x01, 0xC0, 0x3F, 0xE0, 0x03, 0x80, 0x1F, 0xE0, 0x07, 0x00, 0x1F, + 0xF0, 0x07, 0x00, 0x0F, 0xF8, 0x0F, 0x00, 0x0F, 0xF8, 0x3F, 0x80, 0x1F, + 0xFC, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0x7F, 0xF0, 0x00, + 0x7E, 0x1F, 0xF0, 0x00, 0x38, 0x1F, 0xF0, 0x00, 0x38, 0x0F, 0xF0, 0x00, + 0x70, 0x0F, 0xF8, 0x00, 0x60, 0x07, 0xF8, 0x00, 0x60, 0x07, 0xFC, 0x00, + 0xC0, 0x03, 0xFC, 0x01, 0xC0, 0x01, 0xFE, 0x01, 0x80, 0x01, 0xFE, 0x03, + 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0xFF, 0x86, 0x00, 0x00, 0x7F, 0x8E, + 0x00, 0x00, 0x7F, 0xCC, 0x00, 0x00, 0x3F, 0xD8, 0x00, 0x00, 0x3F, 0xF8, + 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x7F, 0xFE, + 0x00, 0x3F, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFC, 0x3F, 0x80, 0x7F, 0xC3, + 0xE0, 0x07, 0xF8, 0x38, 0x00, 0xFF, 0x83, 0x80, 0x0F, 0xF0, 0x30, 0x01, + 0xFE, 0x07, 0x00, 0x3F, 0xE0, 0x60, 0x03, 0xFC, 0x06, 0x00, 0x7F, 0xC0, + 0x00, 0x0F, 0xF8, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x01, + 0xFE, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x7F, 0x80, + 0x00, 0x0F, 0xF8, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x3F, 0xC0, 0x01, 0x07, 0xFC, 0x00, 0x30, 0xFF, 0x80, + 0x03, 0x0F, 0xF0, 0x00, 0x31, 0xFF, 0x00, 0x07, 0x1F, 0xE0, 0x00, 0xF3, + 0xFE, 0x00, 0x1E, 0x7F, 0xC0, 0x03, 0xE7, 0xF8, 0x01, 0xFE, 0xFF, 0xFF, + 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xF0, 0x7C, 0x0F, 0x81, + 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, + 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, + 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, + 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xFF, 0xFF, 0xF8, 0xF0, + 0x01, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0xC0, 0x07, 0x80, 0x0F, 0x00, + 0x1F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x7C, 0x00, 0x78, 0x00, 0xF0, 0x01, + 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1F, + 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xE0, + 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, + 0x1E, 0xFF, 0xFF, 0xFC, 0x1F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, + 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, + 0xE0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, + 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, + 0x0F, 0x81, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0x78, 0x00, 0x07, 0xC0, + 0x00, 0x3F, 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xE0, 0x01, 0xEF, 0x00, 0x0F, + 0x3C, 0x00, 0xF1, 0xE0, 0x07, 0x87, 0x80, 0x78, 0x3C, 0x03, 0xC0, 0xF0, + 0x3C, 0x07, 0x81, 0xE0, 0x1E, 0x1E, 0x00, 0xF0, 0xF0, 0x07, 0xCF, 0x00, + 0x1E, 0x78, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x70, 0x1F, 0x03, 0xF0, 0x7E, 0x03, 0xE0, 0x3E, 0x01, 0xE0, 0x1E, + 0x00, 0xE0, 0x03, 0xFC, 0x00, 0x3F, 0xFC, 0x03, 0xE1, 0xF8, 0x0F, 0x03, + 0xF0, 0x7C, 0x07, 0xC1, 0xF8, 0x1F, 0x87, 0xE0, 0x7E, 0x1F, 0x81, 0xF8, + 0x3C, 0x07, 0xE0, 0x00, 0x1F, 0x80, 0x01, 0xFE, 0x00, 0x3F, 0xF8, 0x03, + 0xE7, 0xE0, 0x3E, 0x1F, 0x83, 0xF0, 0x7E, 0x1F, 0x81, 0xF8, 0x7E, 0x07, + 0xE3, 0xF8, 0x1F, 0x8F, 0xE0, 0x7E, 0x3F, 0x83, 0xF8, 0xFF, 0x1F, 0xE1, + 0xFF, 0xDF, 0xF7, 0xFE, 0x3F, 0x07, 0xE0, 0xF8, 0xFF, 0x80, 0x00, 0x1F, + 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x03, 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x0F, 0xC7, 0xF0, 0x07, 0xEF, 0xFE, 0x03, 0xFC, 0x3F, 0x81, 0xFC, 0x0F, + 0xE0, 0xFC, 0x03, 0xF0, 0x7E, 0x01, 0xFC, 0x3F, 0x00, 0xFE, 0x1F, 0x80, + 0x3F, 0x8F, 0xC0, 0x1F, 0xC7, 0xE0, 0x0F, 0xE3, 0xF0, 0x07, 0xF1, 0xF8, + 0x03, 0xF8, 0xFC, 0x01, 0xFC, 0x7E, 0x00, 0xFE, 0x3F, 0x00, 0x7F, 0x1F, + 0x80, 0x3F, 0x0F, 0xC0, 0x1F, 0x87, 0xE0, 0x1F, 0xC3, 0xF0, 0x0F, 0xC1, + 0xF8, 0x07, 0xE0, 0xFE, 0x07, 0xE0, 0x73, 0x87, 0xE0, 0x30, 0xFF, 0xC0, + 0x10, 0x1F, 0x80, 0x00, 0x00, 0xFC, 0x00, 0x7F, 0xE0, 0x3E, 0x3E, 0x0F, + 0x83, 0xE3, 0xE0, 0x7C, 0x7C, 0x0F, 0x9F, 0x01, 0xF3, 0xE0, 0x1C, 0x7C, + 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, + 0x00, 0x3F, 0x00, 0x07, 0xF0, 0x00, 0xFE, 0x00, 0x0F, 0xE0, 0x01, 0xFC, + 0x00, 0x1F, 0xC0, 0x21, 0xFE, 0x0C, 0x3F, 0xFF, 0x01, 0xFF, 0x80, 0x0F, + 0xC0, 0x00, 0x1F, 0xF8, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, + 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, + 0xF8, 0x00, 0x01, 0xF8, 0x03, 0xF1, 0xF8, 0x07, 0xFD, 0xF8, 0x1F, 0xC7, + 0xF8, 0x1F, 0x83, 0xF8, 0x3F, 0x01, 0xF8, 0x7F, 0x01, 0xF8, 0x7E, 0x01, + 0xF8, 0x7E, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, + 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, + 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0x7E, 0x01, 0xF8, 0x7F, 0x01, + 0xF8, 0x3F, 0x03, 0xF8, 0x3F, 0x03, 0xF8, 0x1F, 0x87, 0xFC, 0x0F, 0xFD, + 0xFF, 0x03, 0xF1, 0xC0, 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE1, 0xE0, 0xF8, + 0x7C, 0x3C, 0x0F, 0x1F, 0x03, 0xE7, 0xC0, 0xFB, 0xF0, 0x3E, 0xFC, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, + 0xC0, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x1F, 0x80, 0x07, 0xF0, 0x0C, 0xFC, + 0x06, 0x3F, 0xC3, 0x07, 0xFF, 0x80, 0xFF, 0xC0, 0x0F, 0xC0, 0x00, 0xFC, + 0x01, 0xFF, 0x81, 0xF1, 0xC1, 0xF0, 0xF0, 0xF8, 0xF8, 0xFC, 0x7C, 0x7E, + 0x1C, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x1F, 0xFF, 0x0F, + 0xFF, 0x80, 0xFC, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, + 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0x3F, + 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x00, + 0xFC, 0x00, 0x7E, 0x00, 0x7F, 0x80, 0xFF, 0xF8, 0x00, 0x07, 0xF0, 0x03, + 0xFF, 0xFC, 0xF8, 0x7F, 0xBE, 0x07, 0x87, 0xC0, 0xF9, 0xF8, 0x1F, 0xBF, + 0x03, 0xF7, 0xE0, 0x7E, 0xFC, 0x0F, 0xDF, 0x81, 0xF9, 0xF0, 0x3F, 0x3E, + 0x07, 0xC3, 0xE1, 0xF8, 0x3C, 0x7E, 0x01, 0xFF, 0x00, 0x60, 0x00, 0x38, + 0x00, 0x0F, 0x00, 0x01, 0xF0, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xF9, 0xFF, + 0xFF, 0x9F, 0xFF, 0xF9, 0xFF, 0xFF, 0x0F, 0xFF, 0xEF, 0x00, 0x3F, 0xC0, + 0x03, 0xF8, 0x00, 0x7F, 0x00, 0x1C, 0xF8, 0x07, 0x0F, 0xFF, 0xC0, 0x7F, + 0xC0, 0xFF, 0x80, 0x00, 0x3F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, + 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, + 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x87, 0xE0, 0x1F, 0x9F, 0xF0, 0x1F, 0xBF, + 0xF8, 0x1F, 0xF1, 0xF8, 0x1F, 0xC0, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, + 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, + 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, + 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, + 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x3F, 0xC1, 0xFE, 0xFF, 0xE3, + 0xFF, 0x0F, 0x07, 0xE1, 0xFE, 0x3F, 0xC7, 0xF8, 0x7F, 0x03, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC3, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, + 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, + 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xE1, 0xFE, 0xFF, 0xE0, 0x00, + 0x70, 0x07, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x07, 0xF0, 0x0F, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x01, 0xFC, 0x03, 0xF0, + 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, + 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, + 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, + 0xDC, 0x3F, 0xF8, 0xFB, 0xE3, 0xEF, 0x0F, 0xBC, 0x7C, 0x7F, 0xE0, 0x7E, + 0x00, 0xFF, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x03, 0xF0, + 0x00, 0x01, 0xF8, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xE1, 0xFF, 0x83, + 0xF0, 0x3F, 0x01, 0xF8, 0x0E, 0x00, 0xFC, 0x06, 0x00, 0x7E, 0x06, 0x00, + 0x3F, 0x06, 0x00, 0x1F, 0x86, 0x00, 0x0F, 0xC7, 0x00, 0x07, 0xE7, 0x80, + 0x03, 0xF7, 0xE0, 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0xFC, 0x00, 0x7E, 0x7F, + 0x00, 0x3F, 0x1F, 0xC0, 0x1F, 0x8F, 0xE0, 0x0F, 0xC3, 0xF8, 0x07, 0xE0, + 0xFE, 0x03, 0xF0, 0x7F, 0x81, 0xF8, 0x1F, 0xC0, 0xFC, 0x0F, 0xF0, 0xFF, + 0x07, 0xFD, 0xFF, 0xC7, 0xFF, 0xFF, 0x87, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, + 0x3F, 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, + 0x07, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, + 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3F, 0x0F, 0xF7, + 0xFF, 0x00, 0x07, 0xE0, 0x3F, 0x07, 0xFC, 0xFF, 0x87, 0xFC, 0x0F, 0xEF, + 0xFE, 0x7F, 0xF0, 0x3F, 0xC3, 0xFF, 0x1F, 0x81, 0xFC, 0x0F, 0xE0, 0x7E, + 0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x7E, 0x03, 0xF0, 0x1F, 0x83, 0xF0, 0x1F, + 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0xFC, 0x07, 0xE0, 0x3F, 0x07, + 0xE0, 0x3F, 0x01, 0xF8, 0x3F, 0x01, 0xF8, 0x0F, 0xC1, 0xF8, 0x0F, 0xC0, + 0x7E, 0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x7E, 0x03, 0xF0, 0x1F, 0x83, 0xF0, + 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0xFC, 0x07, 0xE0, 0x3F, + 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x3F, 0x01, 0xF8, 0x0F, 0xC1, 0xF8, 0x0F, + 0xC0, 0x7E, 0x1F, 0xE0, 0xFF, 0x07, 0xFB, 0xFF, 0x8F, 0xFC, 0x7F, 0xE0, + 0x00, 0x07, 0xE0, 0xFF, 0x9F, 0xF0, 0x3F, 0xBF, 0xF8, 0x1F, 0xF1, 0xF8, + 0x1F, 0xC0, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, + 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, + 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, + 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, 0x1F, 0x80, 0xFC, + 0x1F, 0x80, 0xFC, 0x3F, 0xC1, 0xFE, 0xFF, 0xE3, 0xFF, 0x01, 0xFC, 0x00, + 0x3F, 0xF8, 0x03, 0xE3, 0xE0, 0x3E, 0x0F, 0x83, 0xF0, 0x7E, 0x1F, 0x01, + 0xF1, 0xF8, 0x0F, 0xCF, 0xC0, 0x7E, 0xFE, 0x03, 0xFF, 0xF0, 0x1F, 0xFF, + 0x80, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x3F, 0xFF, 0x01, 0xFF, 0xF8, 0x0F, + 0xFF, 0xC0, 0x7F, 0x7E, 0x03, 0xF3, 0xF0, 0x1F, 0x8F, 0x80, 0xF8, 0x7E, + 0x0F, 0xC1, 0xF0, 0x7C, 0x07, 0xC7, 0xC0, 0x1F, 0xFC, 0x00, 0x3F, 0x80, + 0x00, 0x0F, 0xC0, 0xFF, 0xBF, 0xF0, 0x3F, 0xF1, 0xF8, 0x1F, 0xC0, 0xFC, + 0x1F, 0xC0, 0xFC, 0x1F, 0x80, 0xFE, 0x1F, 0x80, 0x7E, 0x1F, 0x80, 0x7F, + 0x1F, 0x80, 0x7F, 0x1F, 0x80, 0x7F, 0x1F, 0x80, 0x7F, 0x1F, 0x80, 0x7F, + 0x1F, 0x80, 0x7F, 0x1F, 0x80, 0x7F, 0x1F, 0x80, 0x7F, 0x1F, 0x80, 0x7F, + 0x1F, 0x80, 0x7E, 0x1F, 0x80, 0x7E, 0x1F, 0x80, 0xFE, 0x1F, 0x80, 0xFC, + 0x1F, 0xC1, 0xF8, 0x1F, 0xE3, 0xF8, 0x1F, 0xBF, 0xE0, 0x1F, 0x8F, 0xC0, + 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, + 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0xF8, 0x00, + 0x00, 0xF8, 0x08, 0x07, 0xFE, 0x18, 0x0F, 0xC7, 0x38, 0x1F, 0x83, 0xF8, + 0x3F, 0x01, 0xF8, 0x3F, 0x01, 0xF8, 0x7F, 0x01, 0xF8, 0x7E, 0x01, 0xF8, + 0x7E, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, + 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, 0xFE, 0x01, 0xF8, + 0xFE, 0x01, 0xF8, 0x7E, 0x01, 0xF8, 0x7F, 0x01, 0xF8, 0x7F, 0x01, 0xF8, + 0x3F, 0x83, 0xF8, 0x1F, 0xC7, 0xF8, 0x0F, 0xFD, 0xF8, 0x03, 0xF1, 0xF8, + 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, + 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x03, 0xFC, 0x00, 0x0F, 0xFF, + 0x00, 0x07, 0x9F, 0xF3, 0xF8, 0xFE, 0xFF, 0x8F, 0xFF, 0xF1, 0xFE, 0x7E, + 0x3F, 0x87, 0x87, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, + 0x7E, 0x00, 0x0F, 0xC0, 0x01, 0xF8, 0x00, 0x3F, 0x00, 0x07, 0xE0, 0x00, + 0xFC, 0x00, 0x1F, 0x80, 0x03, 0xF0, 0x00, 0x7E, 0x00, 0x0F, 0xC0, 0x01, + 0xF8, 0x00, 0x7F, 0x80, 0x3F, 0xFC, 0x00, 0x0F, 0x84, 0x3F, 0xF8, 0xE1, + 0xF3, 0x80, 0xEF, 0x00, 0xDE, 0x01, 0xBE, 0x01, 0x7E, 0x00, 0xFF, 0x01, + 0xFF, 0x81, 0xFF, 0xC3, 0xFF, 0xC3, 0xFF, 0xC1, 0xFF, 0x80, 0xFF, 0x80, + 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x00, 0x7E, 0x00, 0xFE, 0x01, 0xDF, 0x0F, + 0x37, 0xFC, 0x43, 0xF0, 0x01, 0x00, 0x0C, 0x00, 0x70, 0x01, 0xC0, 0x0F, + 0x00, 0x7C, 0x03, 0xF0, 0x1F, 0xC0, 0xFF, 0xF3, 0xFF, 0xC3, 0xF0, 0x0F, + 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, + 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, + 0xFC, 0x23, 0xF0, 0x8F, 0xE6, 0x1F, 0xF0, 0x7F, 0x80, 0xF8, 0x00, 0xFF, + 0x87, 0xFC, 0x1F, 0xC0, 0xFE, 0x07, 0xE0, 0x3F, 0x03, 0xF0, 0x1F, 0x81, + 0xF8, 0x0F, 0xC0, 0xFC, 0x07, 0xE0, 0x7E, 0x03, 0xF0, 0x3F, 0x01, 0xF8, + 0x1F, 0x80, 0xFC, 0x0F, 0xC0, 0x7E, 0x07, 0xE0, 0x3F, 0x03, 0xF0, 0x1F, + 0x81, 0xF8, 0x0F, 0xC0, 0xFC, 0x07, 0xE0, 0x7E, 0x03, 0xF0, 0x3F, 0x01, + 0xF8, 0x1F, 0x80, 0xFC, 0x0F, 0xC0, 0x7E, 0x07, 0xE0, 0x7F, 0x03, 0xF8, + 0x7F, 0xC0, 0xFF, 0xEF, 0xF8, 0x3F, 0xE7, 0xC0, 0x0F, 0xC2, 0x00, 0xFF, + 0xF1, 0xFC, 0xFF, 0x01, 0xE3, 0xFC, 0x03, 0x07, 0xF0, 0x0C, 0x1F, 0xC0, + 0x60, 0x3F, 0x81, 0x80, 0xFE, 0x04, 0x01, 0xF8, 0x30, 0x07, 0xF0, 0xC0, + 0x1F, 0xC6, 0x00, 0x3F, 0x98, 0x00, 0xFE, 0x40, 0x01, 0xFB, 0x00, 0x07, + 0xFC, 0x00, 0x1F, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFE, 0x00, 0x01, 0xF0, + 0x00, 0x07, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, + 0x01, 0x00, 0x00, 0xFF, 0xE7, 0xFF, 0x3F, 0xBF, 0xE0, 0xFE, 0x07, 0x0F, + 0xE0, 0x7F, 0x03, 0x83, 0xF0, 0x1F, 0x81, 0x81, 0xFC, 0x0F, 0xC0, 0xC0, + 0xFE, 0x07, 0xF0, 0x40, 0x3F, 0x03, 0xF8, 0x60, 0x1F, 0xC3, 0xFC, 0x30, + 0x07, 0xE1, 0xFE, 0x10, 0x03, 0xF0, 0x9F, 0x98, 0x01, 0xFC, 0xCF, 0xCC, + 0x00, 0x7E, 0x67, 0xEC, 0x00, 0x3F, 0xE1, 0xFE, 0x00, 0x1F, 0xF0, 0xFE, + 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x03, 0xF8, 0x3F, 0x80, 0x00, 0xFC, 0x0F, + 0x80, 0x00, 0x7C, 0x07, 0xC0, 0x00, 0x3E, 0x03, 0xE0, 0x00, 0x0F, 0x00, + 0xE0, 0x00, 0x07, 0x00, 0x70, 0x00, 0x03, 0x80, 0x38, 0x00, 0x00, 0x80, + 0x08, 0x00, 0xFF, 0xF3, 0xFD, 0xFF, 0x03, 0xC3, 0xFC, 0x0E, 0x07, 0xF0, + 0x30, 0x1F, 0xE1, 0x80, 0x3F, 0x8C, 0x00, 0x7F, 0x70, 0x01, 0xFF, 0x80, + 0x03, 0xFC, 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0xE0, 0x00, 0x3F, 0x80, 0x00, + 0xFF, 0x00, 0x07, 0xFE, 0x00, 0x1B, 0xF8, 0x00, 0xCF, 0xF0, 0x06, 0x1F, + 0xC0, 0x38, 0x3F, 0x80, 0xC0, 0xFF, 0x07, 0x01, 0xFC, 0x3C, 0x07, 0xFB, + 0xFC, 0x7F, 0xF0, 0xFF, 0xE3, 0xFB, 0xFC, 0x07, 0x8F, 0xE0, 0x18, 0x7F, + 0x01, 0x81, 0xF8, 0x0C, 0x0F, 0xE0, 0x60, 0x7F, 0x06, 0x01, 0xF8, 0x30, + 0x0F, 0xE1, 0x80, 0x7F, 0x18, 0x01, 0xF8, 0xC0, 0x0F, 0xE6, 0x00, 0x3F, + 0x60, 0x01, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0x80, 0x01, 0xFC, 0x00, + 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x07, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x60, 0x03, 0x82, 0x00, 0x3E, + 0x30, 0x01, 0xF1, 0x00, 0x0F, 0x98, 0x00, 0x3F, 0x80, 0x00, 0xF0, 0x00, + 0x00, 0x7F, 0xFF, 0xEF, 0xFF, 0xFD, 0xE0, 0x7F, 0x30, 0x1F, 0xC6, 0x07, + 0xF8, 0x80, 0xFE, 0x00, 0x3F, 0xC0, 0x07, 0xF0, 0x01, 0xFC, 0x00, 0x3F, + 0x80, 0x0F, 0xE0, 0x03, 0xFC, 0x00, 0x7F, 0x00, 0x1F, 0xE0, 0x03, 0xF8, + 0x00, 0xFE, 0x03, 0x3F, 0xC0, 0x67, 0xF0, 0x19, 0xFE, 0x07, 0x3F, 0x83, + 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x80, 0x00, 0x7C, 0x07, 0xE0, 0x3E, 0x00, + 0xF8, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, + 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, + 0x1F, 0x00, 0xF8, 0x03, 0xC0, 0x3C, 0x01, 0xF0, 0x00, 0xF0, 0x03, 0xE0, + 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, + 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, + 0x00, 0x3E, 0x00, 0xF8, 0x01, 0xF8, 0x01, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF8, 0x01, 0xF8, 0x01, 0xF0, 0x07, 0xC0, 0x0F, 0x80, 0x3E, 0x00, + 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, + 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0x7C, 0x00, 0xF0, + 0x00, 0xF0, 0x03, 0xE0, 0x3C, 0x01, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, + 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, + 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x01, 0xF0, 0x07, 0xC0, 0x7E, + 0x03, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0xFF, 0xC0, 0x47, 0xFF, 0xC3, 0x9F, + 0xFF, 0xFF, 0x70, 0x7F, 0xF8, 0x80, 0x7F, 0xC0, 0x00, 0x3E, 0x00}; + +const GFXglyph FreeSerifBold24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 12, 0, 1}, // 0x20 ' ' + {0, 8, 34, 16, 4, -32}, // 0x21 '!' + {34, 17, 13, 26, 4, -32}, // 0x22 '"' + {62, 23, 33, 23, 0, -32}, // 0x23 '#' + {157, 21, 39, 24, 1, -34}, // 0x24 '$' + {260, 35, 34, 47, 6, -32}, // 0x25 '%' + {409, 34, 34, 39, 3, -32}, // 0x26 '&' + {554, 5, 13, 13, 4, -32}, // 0x27 ''' + {563, 12, 41, 16, 2, -32}, // 0x28 '(' + {625, 12, 41, 16, 1, -32}, // 0x29 ')' + {687, 18, 21, 24, 3, -32}, // 0x2A '*' + {735, 26, 25, 32, 3, -24}, // 0x2B '+' + {817, 8, 15, 12, 2, -6}, // 0x2C ',' + {832, 11, 5, 16, 2, -12}, // 0x2D '-' + {839, 8, 8, 12, 2, -6}, // 0x2E '.' + {847, 15, 33, 13, -1, -32}, // 0x2F '/' + {909, 22, 34, 23, 1, -32}, // 0x30 '0' + {1003, 18, 33, 23, 3, -32}, // 0x31 '1' + {1078, 21, 33, 24, 1, -32}, // 0x32 '2' + {1165, 21, 34, 24, 1, -32}, // 0x33 '3' + {1255, 21, 33, 24, 1, -32}, // 0x34 '4' + {1342, 20, 32, 23, 2, -31}, // 0x35 '5' + {1422, 21, 34, 24, 1, -32}, // 0x36 '6' + {1512, 21, 32, 23, 1, -31}, // 0x37 '7' + {1596, 21, 34, 23, 1, -32}, // 0x38 '8' + {1686, 22, 34, 23, 1, -32}, // 0x39 '9' + {1780, 8, 24, 16, 4, -22}, // 0x3A ':' + {1804, 9, 31, 16, 3, -22}, // 0x3B ';' + {1839, 26, 26, 32, 3, -24}, // 0x3C '<' + {1924, 26, 17, 32, 3, -20}, // 0x3D '=' + {1980, 26, 26, 32, 3, -24}, // 0x3E '>' + {2065, 18, 34, 24, 3, -32}, // 0x3F '?' + {2142, 33, 34, 44, 5, -32}, // 0x40 '@' + {2283, 32, 33, 34, 1, -32}, // 0x41 'A' + {2415, 28, 32, 31, 1, -31}, // 0x42 'B' + {2527, 30, 34, 33, 2, -32}, // 0x43 'C' + {2655, 32, 32, 34, 1, -31}, // 0x44 'D' + {2783, 28, 32, 32, 2, -31}, // 0x45 'E' + {2895, 25, 32, 29, 2, -31}, // 0x46 'F' + {2995, 33, 34, 36, 2, -32}, // 0x47 'G' + {3136, 33, 32, 37, 2, -31}, // 0x48 'H' + {3268, 15, 32, 18, 2, -31}, // 0x49 'I' + {3328, 22, 37, 24, 0, -31}, // 0x4A 'J' + {3430, 34, 32, 36, 2, -31}, // 0x4B 'K' + {3566, 28, 32, 31, 2, -31}, // 0x4C 'L' + {3678, 43, 32, 45, 0, -31}, // 0x4D 'M' + {3850, 31, 32, 34, 1, -31}, // 0x4E 'N' + {3974, 33, 34, 37, 2, -32}, // 0x4F 'O' + {4115, 26, 32, 30, 2, -31}, // 0x50 'P' + {4219, 33, 41, 37, 2, -32}, // 0x51 'Q' + {4389, 31, 32, 34, 2, -31}, // 0x52 'R' + {4513, 21, 34, 27, 3, -32}, // 0x53 'S' + {4603, 28, 32, 30, 1, -31}, // 0x54 'T' + {4715, 30, 33, 34, 2, -31}, // 0x55 'U' + {4839, 33, 32, 33, 0, -31}, // 0x56 'V' + {4971, 45, 33, 46, 1, -31}, // 0x57 'W' + {5157, 32, 32, 34, 1, -31}, // 0x58 'X' + {5285, 32, 32, 33, 1, -31}, // 0x59 'Y' + {5413, 28, 32, 30, 1, -31}, // 0x5A 'Z' + {5525, 11, 39, 16, 3, -31}, // 0x5B '[' + {5579, 15, 33, 13, -1, -32}, // 0x5C '\' + {5641, 11, 39, 16, 2, -31}, // 0x5D ']' + {5695, 21, 17, 27, 3, -31}, // 0x5E '^' + {5740, 24, 3, 23, 0, 5}, // 0x5F '_' + {5749, 11, 9, 16, 0, -33}, // 0x60 '`' + {5762, 22, 24, 23, 1, -22}, // 0x61 'a' + {5828, 25, 33, 26, 0, -31}, // 0x62 'b' + {5932, 19, 24, 20, 1, -22}, // 0x63 'c' + {5989, 24, 33, 26, 1, -31}, // 0x64 'd' + {6088, 18, 24, 21, 1, -22}, // 0x65 'e' + {6142, 17, 33, 18, 1, -32}, // 0x66 'f' + {6213, 19, 32, 24, 2, -22}, // 0x67 'g' + {6289, 24, 32, 26, 0, -31}, // 0x68 'h' + {6385, 11, 33, 14, 1, -32}, // 0x69 'i' + {6431, 14, 42, 18, 0, -32}, // 0x6A 'j' + {6505, 25, 32, 26, 0, -31}, // 0x6B 'k' + {6605, 11, 32, 13, 0, -31}, // 0x6C 'l' + {6649, 37, 23, 39, 0, -22}, // 0x6D 'm' + {6756, 24, 23, 26, 0, -22}, // 0x6E 'n' + {6825, 21, 24, 24, 1, -22}, // 0x6F 'o' + {6888, 24, 32, 26, 0, -22}, // 0x70 'p' + {6984, 24, 32, 26, 1, -22}, // 0x71 'q' + {7080, 19, 23, 20, 0, -22}, // 0x72 'r' + {7135, 15, 24, 19, 2, -22}, // 0x73 's' + {7180, 14, 31, 16, 1, -29}, // 0x74 't' + {7235, 25, 23, 27, 0, -21}, // 0x75 'u' + {7307, 22, 23, 23, 0, -21}, // 0x76 'v' + {7371, 33, 23, 33, 0, -21}, // 0x77 'w' + {7466, 22, 22, 24, 1, -21}, // 0x78 'x' + {7527, 21, 31, 23, 0, -21}, // 0x79 'y' + {7609, 19, 22, 21, 1, -21}, // 0x7A 'z' + {7662, 14, 42, 19, 1, -33}, // 0x7B '{' + {7736, 4, 33, 10, 3, -32}, // 0x7C '|' + {7753, 14, 42, 19, 4, -33}, // 0x7D '}' + {7827, 22, 7, 24, 1, -14}}; // 0x7E '~' + +const GFXfont FreeSerifBold24pt7b PROGMEM = { + (uint8_t *)FreeSerifBold24pt7bBitmaps, + (GFXglyph *)FreeSerifBold24pt7bGlyphs, 0x20, 0x7E, 56}; + +// Approx. 8519 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold9pt7b.h new file mode 100644 index 0000000..37ac714 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBold9pt7b.h @@ -0,0 +1,201 @@ +const uint8_t FreeSerifBold9pt7bBitmaps[] PROGMEM = { + 0xFF, 0xF4, 0x92, 0x1F, 0xF0, 0xCF, 0x3C, 0xE3, 0x88, 0x13, 0x09, 0x84, + 0xC2, 0x47, 0xF9, 0x90, 0xC8, 0x4C, 0xFF, 0x13, 0x09, 0x0C, 0x86, 0x40, + 0x10, 0x38, 0xD6, 0x92, 0xD2, 0xF0, 0x7C, 0x3E, 0x17, 0x93, 0x93, 0xD6, + 0x7C, 0x10, 0x3C, 0x21, 0xCF, 0x0E, 0x24, 0x30, 0xA0, 0xC5, 0x03, 0x34, + 0xE7, 0x26, 0x40, 0xB9, 0x04, 0xC4, 0x23, 0x30, 0x8C, 0x84, 0x1C, 0x0F, + 0x00, 0xCC, 0x06, 0x60, 0x3E, 0x00, 0xE7, 0x8F, 0x18, 0x9C, 0x8C, 0xE4, + 0xE3, 0xC7, 0x9E, 0x3C, 0x72, 0xFD, 0xE0, 0xFF, 0x80, 0x32, 0x44, 0xCC, + 0xCC, 0xCC, 0xC4, 0x62, 0x10, 0x84, 0x22, 0x33, 0x33, 0x33, 0x32, 0x64, + 0x80, 0x31, 0x6B, 0xB1, 0x8E, 0xD6, 0x8C, 0x00, 0x08, 0x04, 0x02, 0x01, + 0x0F, 0xF8, 0x40, 0x20, 0x10, 0x08, 0x00, 0xDF, 0x95, 0x00, 0xFF, 0xFF, + 0x80, 0x0C, 0x21, 0x86, 0x10, 0xC3, 0x08, 0x61, 0x84, 0x30, 0xC0, 0x1C, + 0x33, 0x98, 0xDC, 0x7E, 0x3F, 0x1F, 0x8F, 0xC7, 0xE3, 0xB1, 0x98, 0xC3, + 0x80, 0x08, 0xE3, 0x8E, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0xBF, 0x3C, 0x3F, + 0x23, 0xC0, 0xE0, 0x70, 0x30, 0x38, 0x18, 0x18, 0x18, 0x5F, 0xDF, 0xE0, + 0x7C, 0x8E, 0x0E, 0x0E, 0x0C, 0x1E, 0x07, 0x03, 0x03, 0x02, 0xE6, 0xF8, + 0x06, 0x0E, 0x0E, 0x3E, 0x2E, 0x4E, 0x8E, 0x8E, 0xFF, 0xFF, 0x0E, 0x0E, + 0x3F, 0x7E, 0x40, 0x40, 0xF8, 0xFC, 0x1E, 0x06, 0x02, 0x02, 0xE4, 0xF8, + 0x07, 0x1C, 0x30, 0x70, 0xFC, 0xE6, 0xE7, 0xE7, 0xE7, 0x67, 0x66, 0x3C, + 0x7F, 0x3F, 0xA0, 0xD0, 0x40, 0x60, 0x30, 0x10, 0x18, 0x0C, 0x04, 0x06, + 0x03, 0x00, 0x3C, 0xC6, 0xC6, 0xC6, 0xFC, 0x7C, 0x3E, 0xCF, 0xC7, 0xC7, + 0xC6, 0x7C, 0x3E, 0x33, 0xB8, 0xDC, 0x7E, 0x3F, 0x1D, 0xCE, 0x7F, 0x07, + 0x07, 0x0F, 0x1C, 0x00, 0xFF, 0x80, 0x3F, 0xE0, 0xFF, 0x80, 0x37, 0xE5, + 0x40, 0x00, 0x00, 0x70, 0x78, 0x78, 0x78, 0x38, 0x03, 0x80, 0x3C, 0x03, + 0xC0, 0x30, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0x3C, 0x03, + 0xC0, 0x1C, 0x01, 0xC1, 0xE1, 0xE1, 0xE0, 0xE0, 0x00, 0x00, 0x3D, 0x9F, + 0x3E, 0x70, 0xE1, 0x04, 0x08, 0x00, 0x70, 0xE1, 0xC0, 0x0F, 0x81, 0x83, + 0x18, 0xC4, 0x89, 0x9C, 0x4C, 0xE4, 0x67, 0x22, 0x39, 0x22, 0x4F, 0xE3, + 0x00, 0x0C, 0x10, 0x1F, 0x00, 0x02, 0x00, 0x30, 0x01, 0xC0, 0x0E, 0x00, + 0xB8, 0x05, 0xC0, 0x4F, 0x02, 0x38, 0x3F, 0xE1, 0x07, 0x18, 0x3D, 0xE3, + 0xF0, 0xFF, 0x87, 0x1C, 0xE3, 0x9C, 0x73, 0x9C, 0x7F, 0x0E, 0x71, 0xC7, + 0x38, 0xE7, 0x1C, 0xE7, 0x7F, 0xC0, 0x1F, 0x26, 0x1D, 0xC1, 0xB0, 0x1E, + 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x0E, 0x04, 0xE1, 0x0F, 0xC0, 0xFF, + 0x0E, 0x71, 0xC7, 0x38, 0x77, 0x0E, 0xE1, 0xDC, 0x3B, 0x87, 0x70, 0xCE, + 0x39, 0xC6, 0x7F, 0x80, 0xFF, 0xCE, 0x19, 0xC1, 0x38, 0x87, 0x30, 0xFE, + 0x1C, 0xC3, 0x88, 0x70, 0x2E, 0x0D, 0xC3, 0x7F, 0xE0, 0xFF, 0xDC, 0x37, + 0x05, 0xC4, 0x73, 0x1F, 0xC7, 0x31, 0xC4, 0x70, 0x1C, 0x07, 0x03, 0xE0, + 0x1F, 0x23, 0x0E, 0x70, 0x6E, 0x02, 0xE0, 0x0E, 0x00, 0xE1, 0xFE, 0x0E, + 0x60, 0xE7, 0x0E, 0x38, 0xE0, 0xF8, 0xF9, 0xF7, 0x0E, 0x70, 0xE7, 0x0E, + 0x70, 0xE7, 0xFE, 0x70, 0xE7, 0x0E, 0x70, 0xE7, 0x0E, 0x70, 0xEF, 0x9F, + 0xFB, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9D, 0xF0, 0x1F, 0x0E, 0x0E, 0x0E, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0xCE, 0xCC, 0x78, 0xF9, 0xF3, + 0x82, 0x1C, 0x20, 0xE2, 0x07, 0x20, 0x3F, 0x01, 0xDC, 0x0E, 0x70, 0x73, + 0xC3, 0x8F, 0x1C, 0x3D, 0xF3, 0xF0, 0xF8, 0x0E, 0x01, 0xC0, 0x38, 0x07, + 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x2E, 0x09, 0xC3, 0x7F, 0xE0, 0xF8, + 0x0F, 0x3C, 0x1E, 0x3C, 0x1E, 0x2E, 0x2E, 0x2E, 0x2E, 0x26, 0x4E, 0x27, + 0x4E, 0x27, 0x4E, 0x23, 0x8E, 0x23, 0x8E, 0x21, 0x0E, 0x71, 0x1F, 0xF0, + 0xEE, 0x09, 0xE1, 0x3E, 0x25, 0xE4, 0x9E, 0x91, 0xD2, 0x1E, 0x43, 0xC8, + 0x39, 0x03, 0x70, 0x20, 0x1F, 0x83, 0x0C, 0x70, 0xEE, 0x07, 0xE0, 0x7E, + 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x0E, 0x30, 0xC1, 0xF8, 0xFF, 0x1C, + 0xE7, 0x1D, 0xC7, 0x71, 0xDC, 0xE7, 0xF1, 0xC0, 0x70, 0x1C, 0x07, 0x03, + 0xE0, 0x0F, 0x83, 0x9C, 0x70, 0xE6, 0x06, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, + 0x07, 0xE0, 0x76, 0x06, 0x30, 0xC1, 0x98, 0x0F, 0x00, 0x78, 0x03, 0xE0, + 0xFF, 0x07, 0x38, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x38, 0x7E, 0x07, 0x70, + 0x77, 0x87, 0x3C, 0x71, 0xEF, 0x8F, 0x39, 0x47, 0xC1, 0xC0, 0xF0, 0x7C, + 0x3E, 0x0F, 0x83, 0xC3, 0xC6, 0xBC, 0xFF, 0xFC, 0xE3, 0x8E, 0x10, 0xE0, + 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xF0, + 0xF8, 0xEE, 0x09, 0xC1, 0x38, 0x27, 0x04, 0xE0, 0x9C, 0x13, 0x82, 0x70, + 0x4E, 0x08, 0xE2, 0x0F, 0x80, 0xFC, 0x7B, 0xC1, 0x0E, 0x08, 0x70, 0x81, + 0xC4, 0x0E, 0x20, 0x7A, 0x01, 0xD0, 0x0E, 0x80, 0x38, 0x01, 0xC0, 0x04, + 0x00, 0x20, 0x00, 0xFD, 0xFB, 0xDC, 0x38, 0x43, 0x87, 0x10, 0xE1, 0xC4, + 0x38, 0xF2, 0x07, 0x2E, 0x81, 0xD3, 0xA0, 0x34, 0x70, 0x0E, 0x1C, 0x03, + 0x87, 0x00, 0x60, 0x80, 0x10, 0x20, 0xFE, 0xF3, 0xC3, 0x0F, 0x10, 0x39, + 0x00, 0xF0, 0x03, 0x80, 0x1E, 0x01, 0x70, 0x09, 0xC0, 0x8F, 0x08, 0x3D, + 0xF3, 0xF0, 0xFC, 0x7B, 0xC1, 0x8E, 0x08, 0x38, 0x81, 0xE8, 0x07, 0x40, + 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x1F, 0x00, 0xFF, 0xD8, + 0x72, 0x1E, 0x43, 0x80, 0xE0, 0x1C, 0x07, 0x01, 0xC0, 0x38, 0x2E, 0x0F, + 0x83, 0x7F, 0xE0, 0xFC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xF0, 0xC1, + 0x06, 0x18, 0x20, 0xC3, 0x04, 0x18, 0x60, 0x83, 0x0C, 0xF3, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0xF0, 0x18, 0x1C, 0x34, 0x26, 0x62, 0x43, 0xC1, + 0xFF, 0x80, 0xC6, 0x30, 0x7C, 0x63, 0xB1, 0xC0, 0xE1, 0xF3, 0x3B, 0x9D, + 0xCE, 0xFF, 0x80, 0xF0, 0x1C, 0x07, 0x01, 0xDC, 0x7B, 0x9C, 0x77, 0x1D, + 0xC7, 0x71, 0xDC, 0x77, 0x39, 0x3C, 0x3C, 0xED, 0x9F, 0x0E, 0x1C, 0x38, + 0x39, 0x3C, 0x07, 0x80, 0xE0, 0x38, 0xEE, 0x77, 0xB8, 0xEE, 0x3B, 0x8E, + 0xE3, 0xB8, 0xE7, 0x78, 0xEF, 0x3C, 0x66, 0xE6, 0xFE, 0xE0, 0xE0, 0xE0, + 0x72, 0x3C, 0x3E, 0xED, 0xC7, 0xC7, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC7, + 0xC0, 0x31, 0xDF, 0xBF, 0x7E, 0xE7, 0x90, 0x60, 0xFC, 0xFE, 0x0C, 0x17, + 0xC0, 0xF0, 0x1C, 0x07, 0x01, 0xDC, 0x7B, 0x9C, 0xE7, 0x39, 0xCE, 0x73, + 0x9C, 0xE7, 0x3B, 0xFF, 0x73, 0x9D, 0xE7, 0x39, 0xCE, 0x73, 0x9D, 0xF0, + 0x1C, 0x71, 0xCF, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x7D, 0xBE, + 0xF0, 0x1C, 0x07, 0x01, 0xCE, 0x71, 0x1C, 0x87, 0x41, 0xF8, 0x77, 0x1C, + 0xE7, 0x1B, 0xEF, 0xF3, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9D, 0xF0, 0xF7, + 0x38, 0xF7, 0xB9, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, + 0x39, 0xCE, 0xFF, 0xFE, 0xF7, 0x1E, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, + 0x39, 0xCE, 0xFF, 0xC0, 0x3E, 0x31, 0xB8, 0xFC, 0x7E, 0x3F, 0x1F, 0x8E, + 0xC6, 0x3E, 0x00, 0xF7, 0x1E, 0xE7, 0x1D, 0xC7, 0x71, 0xDC, 0x77, 0x1D, + 0xCE, 0x7F, 0x1C, 0x07, 0x01, 0xC0, 0xF8, 0x00, 0x3C, 0x9C, 0xEE, 0x3B, + 0x8E, 0xE3, 0xB8, 0xEE, 0x39, 0xCE, 0x3F, 0x80, 0xE0, 0x38, 0x0E, 0x07, + 0xC0, 0xF7, 0x7B, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0xF8, 0x7E, 0x73, + 0xC7, 0x8E, 0x39, 0xB0, 0x10, 0xCF, 0x9C, 0x71, 0xC7, 0x1C, 0x71, 0xD3, + 0x80, 0xF7, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x3F, + 0xC0, 0xFB, 0xB8, 0x8C, 0x87, 0x43, 0xC0, 0xE0, 0x70, 0x10, 0x08, 0x00, + 0xF7, 0xB6, 0x31, 0x73, 0xA3, 0x3A, 0x3D, 0xA3, 0xDC, 0x18, 0xC1, 0x88, + 0x10, 0x80, 0xFB, 0xB8, 0x8E, 0x83, 0x81, 0xC0, 0xF0, 0x98, 0xCE, 0xEF, + 0x80, 0xF7, 0x62, 0x72, 0x34, 0x34, 0x3C, 0x18, 0x18, 0x10, 0x10, 0x10, + 0xE0, 0xE0, 0xFF, 0x1C, 0x70, 0xE3, 0x87, 0x1C, 0x71, 0xFE, 0x19, 0x8C, + 0x63, 0x18, 0xCC, 0x61, 0x8C, 0x63, 0x18, 0xC3, 0xFF, 0xF8, 0xC3, 0x18, + 0xC6, 0x31, 0x86, 0x33, 0x18, 0xC6, 0x31, 0x98, 0xF0, 0x8E}; + +const GFXglyph FreeSerifBold9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 3, 12, 6, 1, -11}, // 0x21 '!' + {5, 6, 5, 10, 2, -11}, // 0x22 '"' + {9, 9, 13, 9, 0, -12}, // 0x23 '#' + {24, 8, 14, 9, 1, -12}, // 0x24 '$' + {38, 14, 12, 18, 2, -11}, // 0x25 '%' + {59, 13, 12, 15, 1, -11}, // 0x26 '&' + {79, 2, 5, 5, 1, -11}, // 0x27 ''' + {81, 4, 15, 6, 1, -11}, // 0x28 '(' + {89, 4, 15, 6, 1, -11}, // 0x29 ')' + {97, 7, 7, 9, 2, -11}, // 0x2A '*' + {104, 9, 9, 12, 1, -8}, // 0x2B '+' + {115, 3, 6, 4, 1, -2}, // 0x2C ',' + {118, 4, 2, 6, 1, -4}, // 0x2D '-' + {119, 3, 3, 4, 1, -2}, // 0x2E '.' + {121, 6, 13, 5, 0, -11}, // 0x2F '/' + {131, 9, 12, 9, 0, -11}, // 0x30 '0' + {145, 6, 12, 9, 1, -11}, // 0x31 '1' + {154, 9, 12, 9, 0, -11}, // 0x32 '2' + {168, 8, 12, 9, 0, -11}, // 0x33 '3' + {180, 8, 12, 9, 1, -11}, // 0x34 '4' + {192, 8, 12, 9, 1, -11}, // 0x35 '5' + {204, 8, 12, 9, 1, -11}, // 0x36 '6' + {216, 9, 12, 9, 0, -11}, // 0x37 '7' + {230, 8, 12, 9, 1, -11}, // 0x38 '8' + {242, 9, 12, 9, 0, -11}, // 0x39 '9' + {256, 3, 9, 6, 1, -8}, // 0x3A ':' + {260, 3, 12, 6, 2, -8}, // 0x3B ';' + {265, 10, 10, 12, 1, -9}, // 0x3C '<' + {278, 10, 5, 12, 1, -6}, // 0x3D '=' + {285, 10, 10, 12, 1, -8}, // 0x3E '>' + {298, 7, 12, 9, 1, -11}, // 0x3F '?' + {309, 13, 12, 17, 2, -11}, // 0x40 '@' + {329, 13, 12, 13, 0, -11}, // 0x41 'A' + {349, 11, 12, 12, 0, -11}, // 0x42 'B' + {366, 11, 12, 13, 1, -11}, // 0x43 'C' + {383, 11, 12, 13, 1, -11}, // 0x44 'D' + {400, 11, 12, 12, 1, -11}, // 0x45 'E' + {417, 10, 12, 11, 1, -11}, // 0x46 'F' + {432, 12, 12, 14, 1, -11}, // 0x47 'G' + {450, 12, 12, 14, 1, -11}, // 0x48 'H' + {468, 5, 12, 7, 1, -11}, // 0x49 'I' + {476, 8, 14, 9, 0, -11}, // 0x4A 'J' + {490, 13, 12, 14, 1, -11}, // 0x4B 'K' + {510, 11, 12, 12, 1, -11}, // 0x4C 'L' + {527, 16, 12, 17, 0, -11}, // 0x4D 'M' + {551, 11, 12, 13, 1, -11}, // 0x4E 'N' + {568, 12, 12, 14, 1, -11}, // 0x4F 'O' + {586, 10, 12, 11, 1, -11}, // 0x50 'P' + {601, 12, 15, 14, 1, -11}, // 0x51 'Q' + {624, 12, 12, 13, 1, -11}, // 0x52 'R' + {642, 8, 12, 10, 1, -11}, // 0x53 'S' + {654, 12, 12, 12, 0, -11}, // 0x54 'T' + {672, 11, 12, 13, 1, -11}, // 0x55 'U' + {689, 13, 13, 13, 0, -11}, // 0x56 'V' + {711, 18, 12, 18, 0, -11}, // 0x57 'W' + {738, 13, 12, 13, 0, -11}, // 0x58 'X' + {758, 13, 12, 13, 0, -11}, // 0x59 'Y' + {778, 11, 12, 12, 1, -11}, // 0x5A 'Z' + {795, 4, 15, 6, 1, -11}, // 0x5B '[' + {803, 6, 13, 5, 0, -11}, // 0x5C '\' + {813, 4, 15, 6, 1, -11}, // 0x5D ']' + {821, 8, 7, 10, 1, -11}, // 0x5E '^' + {828, 9, 1, 9, 0, 3}, // 0x5F '_' + {830, 4, 3, 6, 0, -12}, // 0x60 '`' + {832, 9, 9, 9, 0, -8}, // 0x61 'a' + {843, 10, 12, 10, 0, -11}, // 0x62 'b' + {858, 7, 9, 8, 0, -8}, // 0x63 'c' + {866, 10, 12, 10, 0, -11}, // 0x64 'd' + {881, 8, 9, 8, 0, -8}, // 0x65 'e' + {890, 7, 12, 7, 0, -11}, // 0x66 'f' + {901, 7, 13, 9, 1, -8}, // 0x67 'g' + {913, 10, 12, 10, 0, -11}, // 0x68 'h' + {928, 5, 12, 5, 0, -11}, // 0x69 'i' + {936, 6, 16, 7, 0, -11}, // 0x6A 'j' + {948, 10, 12, 10, 0, -11}, // 0x6B 'k' + {963, 5, 12, 5, 0, -11}, // 0x6C 'l' + {971, 15, 9, 15, 0, -8}, // 0x6D 'm' + {988, 10, 9, 10, 0, -8}, // 0x6E 'n' + {1000, 9, 9, 9, 0, -8}, // 0x6F 'o' + {1011, 10, 13, 10, 0, -8}, // 0x70 'p' + {1028, 10, 13, 10, 0, -8}, // 0x71 'q' + {1045, 8, 9, 8, 0, -8}, // 0x72 'r' + {1054, 5, 9, 7, 1, -8}, // 0x73 's' + {1060, 6, 11, 6, 0, -10}, // 0x74 't' + {1069, 10, 9, 10, 0, -8}, // 0x75 'u' + {1081, 9, 9, 9, 0, -8}, // 0x76 'v' + {1092, 12, 9, 13, 0, -8}, // 0x77 'w' + {1106, 9, 9, 9, 0, -8}, // 0x78 'x' + {1117, 8, 13, 9, 0, -8}, // 0x79 'y' + {1130, 7, 9, 8, 1, -8}, // 0x7A 'z' + {1138, 5, 16, 7, 0, -12}, // 0x7B '{' + {1148, 1, 13, 4, 1, -11}, // 0x7C '|' + {1150, 5, 16, 7, 2, -12}, // 0x7D '}' + {1160, 8, 2, 9, 1, -4}}; // 0x7E '~' + +const GFXfont FreeSerifBold9pt7b PROGMEM = { + (uint8_t *)FreeSerifBold9pt7bBitmaps, (GFXglyph *)FreeSerifBold9pt7bGlyphs, + 0x20, 0x7E, 22}; + +// Approx. 1834 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic12pt7b.h new file mode 100644 index 0000000..79d8d7a --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic12pt7b.h @@ -0,0 +1,291 @@ +const uint8_t FreeSerifBoldItalic12pt7bBitmaps[] PROGMEM = { + 0x07, 0x07, 0x07, 0x0F, 0x0E, 0x0E, 0x0C, 0x0C, 0x08, 0x18, 0x10, 0x00, + 0x00, 0x60, 0xF0, 0xF0, 0x60, 0x61, 0xF1, 0xF8, 0xF8, 0x6C, 0x34, 0x12, + 0x08, 0x01, 0x8C, 0x06, 0x60, 0x31, 0x80, 0xCC, 0x06, 0x30, 0xFF, 0xF0, + 0xC6, 0x03, 0x18, 0x0C, 0xC0, 0x63, 0x0F, 0xFF, 0x0C, 0x60, 0x33, 0x01, + 0x8C, 0x06, 0x30, 0x19, 0x80, 0x00, 0x80, 0x08, 0x07, 0xC1, 0x96, 0x31, + 0x33, 0x13, 0x3A, 0x23, 0xE0, 0x1E, 0x01, 0xF0, 0x07, 0x80, 0x7C, 0x05, + 0xC4, 0xCC, 0x48, 0xCC, 0x8C, 0xF8, 0x83, 0x30, 0x1E, 0x01, 0x00, 0x00, + 0x02, 0x07, 0x83, 0x03, 0x9F, 0x81, 0xC4, 0x20, 0x71, 0x10, 0x3C, 0x44, + 0x0E, 0x22, 0x03, 0x88, 0x80, 0xE4, 0x40, 0x1E, 0x31, 0xE0, 0x08, 0xE4, + 0x06, 0x71, 0x01, 0x3C, 0x40, 0x8E, 0x10, 0x23, 0x88, 0x10, 0xE2, 0x04, + 0x39, 0x02, 0x07, 0x80, 0x00, 0xF0, 0x01, 0x98, 0x03, 0x98, 0x03, 0x98, + 0x03, 0xB0, 0x03, 0xE0, 0x03, 0x80, 0x0F, 0x9F, 0x19, 0xCE, 0x31, 0xCC, + 0x61, 0xC8, 0xE1, 0xC8, 0xE0, 0xF0, 0xE0, 0xE0, 0xF0, 0x70, 0x78, 0x79, + 0x3F, 0xBE, 0x7F, 0xED, 0x20, 0x02, 0x08, 0x20, 0xC3, 0x0E, 0x18, 0x30, + 0xE1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x20, 0x40, 0x80, 0x81, 0x01, 0x00, + 0x10, 0x10, 0x20, 0x20, 0x40, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x70, 0xE1, + 0x83, 0x0C, 0x18, 0x61, 0x86, 0x00, 0x00, 0x0C, 0x33, 0x6C, 0x9B, 0xAE, + 0x1C, 0x3F, 0xEC, 0x9B, 0x36, 0x0C, 0x02, 0x00, 0x06, 0x00, 0x60, 0x06, + 0x00, 0x60, 0x06, 0x0F, 0xFF, 0xFF, 0xF0, 0x60, 0x06, 0x00, 0x60, 0x06, + 0x00, 0x60, 0x31, 0xCE, 0x31, 0x08, 0x98, 0xFF, 0xFF, 0xC0, 0x6F, 0xF6, + 0x01, 0x80, 0x60, 0x30, 0x0C, 0x07, 0x01, 0x80, 0xE0, 0x30, 0x1C, 0x06, + 0x01, 0x80, 0xC0, 0x30, 0x18, 0x06, 0x03, 0x00, 0x03, 0x81, 0xC8, 0x71, + 0x1C, 0x33, 0x86, 0xE1, 0xDC, 0x3B, 0x87, 0xE0, 0xFC, 0x3B, 0x87, 0x70, + 0xEC, 0x39, 0x87, 0x31, 0xC2, 0x30, 0x3C, 0x00, 0x01, 0xC3, 0xF0, 0x38, + 0x0E, 0x03, 0x81, 0xE0, 0x70, 0x1C, 0x0F, 0x03, 0x80, 0xE0, 0x38, 0x1E, + 0x07, 0x01, 0xC0, 0xF0, 0xFF, 0x80, 0x07, 0x81, 0xF8, 0x47, 0x90, 0x70, + 0x0E, 0x01, 0xC0, 0x30, 0x0E, 0x01, 0x80, 0x60, 0x18, 0x06, 0x01, 0x80, + 0x40, 0x8F, 0xF3, 0xFC, 0xFF, 0x80, 0x07, 0xC3, 0x3C, 0x03, 0x80, 0x70, + 0x0C, 0x03, 0x81, 0xC0, 0xFC, 0x07, 0xC0, 0x78, 0x07, 0x00, 0xE0, 0x1C, + 0x03, 0x30, 0xE7, 0x10, 0x7C, 0x00, 0x00, 0x10, 0x01, 0x80, 0x3C, 0x03, + 0xE0, 0x2E, 0x02, 0x70, 0x23, 0x82, 0x38, 0x21, 0xC2, 0x0E, 0x1F, 0xF9, + 0xFF, 0xC0, 0x38, 0x01, 0xC0, 0x1C, 0x00, 0xE0, 0x07, 0xF0, 0x7E, 0x0F, + 0xE0, 0x80, 0x08, 0x01, 0xE0, 0x1F, 0x83, 0xF8, 0x03, 0xC0, 0x1C, 0x00, + 0xC0, 0x0C, 0x00, 0xC0, 0x08, 0x61, 0x8F, 0x30, 0x7C, 0x00, 0x00, 0x60, + 0x78, 0x1C, 0x0F, 0x01, 0xC0, 0x70, 0x1F, 0xC3, 0x8C, 0xE1, 0xDC, 0x3B, + 0x87, 0x61, 0xEC, 0x3D, 0x87, 0x31, 0xE2, 0x38, 0x3C, 0x00, 0x3F, 0xEF, + 0xF9, 0xFF, 0x60, 0xC8, 0x18, 0x06, 0x00, 0x80, 0x30, 0x0C, 0x01, 0x80, + 0x60, 0x1C, 0x03, 0x00, 0xC0, 0x18, 0x06, 0x00, 0x03, 0x81, 0x88, 0x61, + 0x8C, 0x31, 0x86, 0x38, 0xC7, 0xB0, 0x78, 0x0F, 0x86, 0x71, 0x87, 0x60, + 0x6C, 0x0D, 0x81, 0xB0, 0x63, 0x18, 0x3E, 0x00, 0x07, 0x81, 0xC8, 0x71, + 0x8E, 0x33, 0xC6, 0x70, 0xCE, 0x39, 0xC7, 0x38, 0xE3, 0x38, 0x3F, 0x01, + 0xC0, 0x38, 0x0E, 0x03, 0x81, 0xC0, 0xE0, 0x00, 0x0C, 0x3C, 0x78, 0x60, + 0x00, 0x00, 0x00, 0x61, 0xE3, 0xC3, 0x00, 0x0E, 0x0F, 0x0F, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x38, 0x38, 0x18, 0x10, 0x20, 0x40, 0x00, 0x10, + 0x07, 0x01, 0xF0, 0x7C, 0x3F, 0x0F, 0x80, 0xE0, 0x0F, 0x80, 0x3E, 0x00, + 0xF8, 0x03, 0xE0, 0x07, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0x00, 0x3F, 0x00, 0x3E, 0x00, 0x7C, 0x00, + 0xF8, 0x01, 0xE0, 0x1F, 0x07, 0xE0, 0xF8, 0x1F, 0x01, 0xE0, 0x0C, 0x00, + 0x00, 0x1E, 0x19, 0x8C, 0xE6, 0x70, 0x38, 0x38, 0x1C, 0x18, 0x18, 0x08, + 0x08, 0x00, 0x00, 0x03, 0x03, 0xC1, 0xE0, 0x60, 0x00, 0x03, 0xF0, 0x07, + 0x06, 0x06, 0x00, 0x86, 0x0E, 0x66, 0x0D, 0xDB, 0x0C, 0xE7, 0x06, 0x33, + 0x83, 0x31, 0xC3, 0x18, 0xE1, 0x8C, 0x70, 0xCC, 0x4C, 0x66, 0x46, 0x1F, + 0xC1, 0x80, 0x00, 0x30, 0x10, 0x07, 0xF0, 0x00, 0x10, 0x00, 0x30, 0x00, + 0x70, 0x00, 0x70, 0x00, 0xF0, 0x01, 0xF0, 0x01, 0x78, 0x03, 0x78, 0x02, + 0x38, 0x04, 0x38, 0x0C, 0x38, 0x0F, 0xF8, 0x18, 0x3C, 0x30, 0x3C, 0x20, + 0x3C, 0x60, 0x3C, 0xF8, 0x7F, 0x1F, 0xFC, 0x07, 0x9E, 0x07, 0x0F, 0x07, + 0x0F, 0x0F, 0x0F, 0x0F, 0x1E, 0x0E, 0x3C, 0x0F, 0xE0, 0x1E, 0x3C, 0x1E, + 0x1E, 0x1C, 0x1E, 0x3C, 0x1E, 0x3C, 0x1E, 0x3C, 0x3E, 0x38, 0x3C, 0x7C, + 0x78, 0xFF, 0xE0, 0x01, 0xF2, 0x0E, 0x1C, 0x38, 0x18, 0xE0, 0x33, 0xC0, + 0x4F, 0x00, 0x9E, 0x00, 0x7C, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, + 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x1E, 0x04, 0x1E, 0x30, 0x0F, 0x80, 0x1F, + 0xFC, 0x01, 0xE3, 0xC0, 0x70, 0x78, 0x1C, 0x0E, 0x0F, 0x03, 0xC3, 0xC0, + 0xF0, 0xE0, 0x3C, 0x38, 0x0F, 0x1E, 0x03, 0xC7, 0x81, 0xF1, 0xC0, 0x78, + 0xF0, 0x1E, 0x3C, 0x0F, 0x0F, 0x03, 0xC3, 0x81, 0xC1, 0xE1, 0xE0, 0xFF, + 0xE0, 0x00, 0x1F, 0xFF, 0x83, 0xC1, 0xC1, 0xC0, 0x40, 0xE0, 0x20, 0xF0, + 0x00, 0x78, 0xC0, 0x38, 0x40, 0x1F, 0xE0, 0x1E, 0x70, 0x0F, 0x18, 0x07, + 0x08, 0x03, 0x84, 0x03, 0xC0, 0x61, 0xE0, 0x20, 0xE0, 0x30, 0xF8, 0x78, + 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0x07, 0x87, 0x07, 0x02, 0x07, 0x02, 0x0F, + 0x00, 0x0F, 0x18, 0x0E, 0x10, 0x0F, 0xF0, 0x1E, 0x70, 0x1E, 0x30, 0x1C, + 0x20, 0x1C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x38, 0x00, 0x7C, 0x00, 0xFE, + 0x00, 0x01, 0xF9, 0x03, 0xC3, 0x83, 0x81, 0xC3, 0x80, 0x43, 0xC0, 0x23, + 0xC0, 0x01, 0xE0, 0x01, 0xF0, 0x00, 0xF0, 0x3F, 0xF8, 0x0F, 0x3C, 0x07, + 0x9E, 0x03, 0xCF, 0x01, 0xC3, 0x80, 0xE1, 0xE0, 0xF0, 0x78, 0x70, 0x0F, + 0xE0, 0x00, 0x1F, 0xE7, 0xF0, 0x78, 0x3C, 0x07, 0x83, 0xC0, 0x70, 0x3C, + 0x0F, 0x03, 0x80, 0xF0, 0x78, 0x0E, 0x07, 0x80, 0xE0, 0x70, 0x1F, 0xFF, + 0x01, 0xE0, 0xF0, 0x1C, 0x0F, 0x03, 0xC0, 0xE0, 0x3C, 0x1E, 0x03, 0xC1, + 0xE0, 0x38, 0x1E, 0x07, 0xC3, 0xE0, 0xFE, 0x7F, 0x00, 0x1F, 0xC1, 0xE0, + 0x70, 0x1C, 0x0F, 0x03, 0xC0, 0xE0, 0x38, 0x1E, 0x07, 0x81, 0xC0, 0x70, + 0x3C, 0x0F, 0x03, 0x81, 0xF0, 0xFE, 0x00, 0x01, 0xFC, 0x03, 0xC0, 0x0F, + 0x00, 0x38, 0x00, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x70, 0x01, 0xC0, 0x0F, + 0x00, 0x3C, 0x00, 0xE0, 0x07, 0x80, 0x1E, 0x0E, 0x70, 0x3B, 0xC0, 0xCE, + 0x01, 0xF0, 0x00, 0x1F, 0xEF, 0x83, 0xC1, 0x81, 0xC1, 0x80, 0xE1, 0x80, + 0xF1, 0x80, 0x79, 0x00, 0x39, 0x00, 0x1F, 0x80, 0x1F, 0xE0, 0x0F, 0x70, + 0x07, 0x3C, 0x07, 0x8E, 0x03, 0xC7, 0x01, 0xE3, 0xC0, 0xE0, 0xE0, 0xF8, + 0x78, 0xFE, 0xFE, 0x00, 0x1F, 0xE0, 0x0F, 0x00, 0x1C, 0x00, 0x38, 0x00, + 0xF0, 0x01, 0xE0, 0x03, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x70, + 0x00, 0xE0, 0x03, 0xC0, 0x27, 0x00, 0xCE, 0x03, 0x3C, 0x1E, 0xFF, 0xFC, + 0x0F, 0x80, 0x7E, 0x0F, 0x00, 0xF0, 0x1E, 0x03, 0xE0, 0x3C, 0x0F, 0x80, + 0xB8, 0x17, 0x01, 0x70, 0x5E, 0x02, 0xF1, 0xBC, 0x05, 0xE2, 0x70, 0x11, + 0xC8, 0xE0, 0x23, 0xB3, 0xC0, 0x47, 0x47, 0x81, 0x0F, 0x8E, 0x02, 0x1E, + 0x1C, 0x04, 0x38, 0x78, 0x08, 0x70, 0xF0, 0x30, 0xC3, 0xE0, 0xF9, 0x8F, + 0xE0, 0x1F, 0x03, 0xE0, 0xF0, 0x38, 0x1E, 0x02, 0x03, 0xE0, 0xC0, 0xBC, + 0x10, 0x13, 0xC2, 0x02, 0x78, 0x40, 0x47, 0x90, 0x10, 0xF2, 0x02, 0x0F, + 0x40, 0x41, 0xE8, 0x18, 0x1E, 0x02, 0x03, 0xC0, 0x40, 0x38, 0x08, 0x06, + 0x03, 0x00, 0x40, 0x10, 0x08, 0x00, 0x01, 0xF8, 0x07, 0x1C, 0x0E, 0x0E, + 0x1E, 0x0F, 0x3C, 0x0F, 0x3C, 0x0F, 0x78, 0x0F, 0x78, 0x0F, 0xF8, 0x1F, + 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, 0x78, 0x70, 0x70, + 0x38, 0xE0, 0x1F, 0x80, 0x1F, 0xFC, 0x07, 0x9E, 0x07, 0x0F, 0x07, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0E, 0x1E, 0x0E, 0x3C, 0x1F, 0xF0, 0x1E, 0x00, + 0x1C, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x38, 0x00, 0x38, 0x00, 0x7C, 0x00, + 0xFE, 0x00, 0x01, 0xF8, 0x07, 0x1C, 0x0E, 0x0E, 0x1E, 0x0F, 0x3C, 0x0F, + 0x3C, 0x0F, 0x78, 0x0F, 0x78, 0x1F, 0xF8, 0x1F, 0xF0, 0x1E, 0xF0, 0x1E, + 0xF0, 0x3C, 0xF0, 0x3C, 0xF0, 0x78, 0x70, 0x70, 0x39, 0xC0, 0x0E, 0x00, + 0x08, 0x02, 0x3F, 0x04, 0x7F, 0xF8, 0x83, 0xF0, 0x1F, 0xF8, 0x07, 0x9E, + 0x07, 0x8F, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x1E, 0x0E, 0x3C, + 0x1F, 0xF0, 0x1E, 0xF0, 0x1C, 0xF0, 0x3C, 0xF0, 0x3C, 0x78, 0x3C, 0x78, + 0x3C, 0x78, 0x7C, 0x3C, 0xFE, 0x3E, 0x07, 0x91, 0xC7, 0x18, 0x73, 0x82, + 0x38, 0x23, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x01, 0xE0, 0x1E, + 0x40, 0xE4, 0x0E, 0x60, 0xCE, 0x1C, 0x9F, 0x00, 0x7F, 0xFE, 0xE7, 0x9D, + 0x0E, 0x16, 0x3C, 0x20, 0x78, 0x40, 0xE0, 0x01, 0xC0, 0x07, 0x80, 0x0F, + 0x00, 0x1C, 0x00, 0x38, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0x80, 0x0F, 0x00, + 0x1E, 0x00, 0xFF, 0x00, 0x7F, 0x1F, 0x3C, 0x0E, 0x38, 0x04, 0x38, 0x0C, + 0x78, 0x08, 0x78, 0x08, 0x70, 0x08, 0x70, 0x10, 0xF0, 0x10, 0xF0, 0x10, + 0xF0, 0x10, 0xF0, 0x20, 0xF0, 0x20, 0xF0, 0x20, 0xF0, 0x40, 0x78, 0xC0, + 0x3F, 0x00, 0xFF, 0x1F, 0x3C, 0x06, 0x3C, 0x04, 0x3C, 0x08, 0x3C, 0x08, + 0x3C, 0x10, 0x1C, 0x20, 0x1C, 0x20, 0x1E, 0x40, 0x1E, 0x80, 0x1E, 0x80, + 0x1F, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0x08, 0x00, 0xFE, 0x7C, + 0x79, 0xE1, 0xC1, 0x8F, 0x0E, 0x08, 0x78, 0x70, 0x43, 0xC7, 0x84, 0x1E, + 0x3E, 0x20, 0x72, 0xF2, 0x03, 0x97, 0x90, 0x1D, 0x1D, 0x00, 0xE8, 0xE8, + 0x07, 0x87, 0x80, 0x3C, 0x3C, 0x01, 0xC1, 0xC0, 0x0E, 0x0E, 0x00, 0x20, + 0x60, 0x01, 0x02, 0x00, 0x1F, 0xCF, 0x83, 0xC1, 0x81, 0xE1, 0x80, 0x71, + 0x80, 0x39, 0x80, 0x1F, 0x80, 0x07, 0x80, 0x03, 0x80, 0x01, 0xE0, 0x01, + 0xF0, 0x00, 0xB8, 0x00, 0x9C, 0x00, 0x8F, 0x00, 0x83, 0x80, 0xC1, 0xC0, + 0xE0, 0xF0, 0xF9, 0xFE, 0x00, 0xFE, 0x7C, 0xE0, 0x63, 0x81, 0x0F, 0x08, + 0x1C, 0x40, 0x71, 0x01, 0xE8, 0x03, 0xC0, 0x0E, 0x00, 0x38, 0x01, 0xE0, + 0x07, 0x80, 0x1C, 0x00, 0x70, 0x03, 0xC0, 0x0F, 0x00, 0xFF, 0x00, 0x1F, + 0xFE, 0x38, 0x78, 0x60, 0xF1, 0x83, 0xC2, 0x0F, 0x00, 0x1E, 0x00, 0x78, + 0x01, 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF8, 0x01, 0xE0, 0x47, + 0x81, 0x1F, 0x06, 0x3C, 0x3C, 0xFF, 0xF0, 0x07, 0xC1, 0x80, 0xE0, 0x30, + 0x0C, 0x03, 0x01, 0xC0, 0x60, 0x18, 0x06, 0x03, 0x80, 0xC0, 0x30, 0x0C, + 0x07, 0x01, 0xC0, 0x60, 0x18, 0x0E, 0x03, 0xE0, 0xC3, 0x06, 0x18, 0x61, + 0x83, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x60, 0xC3, 0x0F, 0x81, 0xC0, 0xE0, + 0x60, 0x30, 0x18, 0x1C, 0x0C, 0x06, 0x03, 0x03, 0x81, 0x80, 0xC0, 0x60, + 0x70, 0x38, 0x18, 0x0C, 0x0E, 0x1F, 0x00, 0x0C, 0x07, 0x81, 0xE0, 0xDC, + 0x33, 0x18, 0xC6, 0x1B, 0x06, 0xC0, 0xC0, 0xFF, 0xF0, 0xC7, 0x0C, 0x30, + 0x07, 0x70, 0xCE, 0x1C, 0xE3, 0x8E, 0x70, 0xC7, 0x0C, 0x71, 0xCE, 0x1C, + 0xE1, 0x8E, 0x79, 0xE9, 0xA7, 0x1C, 0x02, 0x07, 0xC0, 0x38, 0x06, 0x01, + 0xC0, 0x38, 0x06, 0x71, 0xF7, 0x38, 0xE7, 0x1C, 0xC3, 0xB8, 0x77, 0x1C, + 0xE3, 0xB8, 0xE7, 0x18, 0xE6, 0x0F, 0x80, 0x07, 0x0C, 0xCE, 0x66, 0x07, + 0x03, 0x83, 0x81, 0xC0, 0xE0, 0x70, 0xBC, 0x87, 0x80, 0x00, 0x08, 0x03, + 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x70, 0x01, 0xC0, 0x77, 0x03, 0x3C, 0x18, + 0xE0, 0xE3, 0x87, 0x0E, 0x1C, 0x70, 0x71, 0xC3, 0x87, 0x0E, 0x3C, 0x38, + 0xE8, 0xE5, 0xA1, 0xE7, 0x00, 0x07, 0x0C, 0xCE, 0x66, 0x37, 0x33, 0xBB, + 0xB1, 0xE0, 0xE0, 0x70, 0xB8, 0x87, 0x80, 0x00, 0x38, 0x01, 0xB0, 0x0C, + 0xC0, 0x30, 0x01, 0xC0, 0x07, 0x00, 0x7E, 0x00, 0xE0, 0x03, 0x80, 0x0E, + 0x00, 0x30, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x03, 0x80, 0x0E, + 0x00, 0x38, 0x00, 0xC0, 0x33, 0x00, 0xD8, 0x01, 0xC0, 0x00, 0x03, 0x80, + 0x73, 0xC7, 0x1C, 0x38, 0xE1, 0xCF, 0x06, 0x70, 0x1E, 0x01, 0x00, 0x1C, + 0x00, 0xF8, 0x07, 0xF0, 0xC7, 0x8C, 0x0C, 0x60, 0x63, 0x86, 0x07, 0xE0, + 0x01, 0x00, 0xF8, 0x01, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x31, 0xC3, + 0xBE, 0x1E, 0x70, 0xE3, 0x8F, 0x38, 0x71, 0xC3, 0x8E, 0x1C, 0xE1, 0xC7, + 0x0E, 0x3A, 0x71, 0xD3, 0x0F, 0x00, 0x1C, 0x71, 0xC0, 0x00, 0x6F, 0x8E, + 0x31, 0xC7, 0x18, 0x63, 0x8E, 0xBC, 0xE0, 0x00, 0xE0, 0x1C, 0x03, 0x80, + 0x00, 0x00, 0x0F, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x70, 0x0E, 0x01, 0xC0, + 0x38, 0x0E, 0x01, 0xC0, 0x38, 0x06, 0x01, 0xC3, 0x38, 0x6E, 0x07, 0x80, + 0x01, 0x00, 0xF8, 0x01, 0xC0, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x33, 0xE3, + 0x8C, 0x1C, 0xC0, 0xE4, 0x06, 0x40, 0x7E, 0x03, 0xF0, 0x1D, 0x81, 0xCE, + 0x0E, 0x72, 0x71, 0xA3, 0x8E, 0x00, 0x06, 0x7C, 0x70, 0xE1, 0xC3, 0x0E, + 0x1C, 0x38, 0x61, 0xC3, 0x87, 0x0C, 0x38, 0x72, 0xE9, 0xE0, 0x3C, 0x73, + 0xC7, 0x7D, 0x71, 0xE7, 0x9C, 0xF1, 0xCE, 0x3C, 0xF3, 0x8E, 0x39, 0xC3, + 0x8E, 0x71, 0xC3, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xD7, 0x1C, 0x7B, 0x8E, + 0x1C, 0x3C, 0xF1, 0xD7, 0x1E, 0x73, 0xCE, 0x3C, 0xE3, 0x8E, 0x39, 0xC7, + 0x9C, 0x71, 0xC7, 0x1D, 0x71, 0xEE, 0x1C, 0x0F, 0x06, 0x63, 0x9D, 0xC7, + 0x71, 0xF8, 0x7E, 0x3F, 0x8E, 0xE3, 0xB9, 0xC6, 0x60, 0xF0, 0x0F, 0x38, + 0x1F, 0x70, 0x71, 0xC1, 0xC7, 0x0E, 0x1C, 0x38, 0xF0, 0xE3, 0x83, 0x8E, + 0x1C, 0x70, 0x71, 0xC1, 0xCE, 0x07, 0xE0, 0x38, 0x00, 0xE0, 0x03, 0x80, + 0x3F, 0x00, 0x07, 0x70, 0xCE, 0x18, 0xE3, 0x8E, 0x70, 0xE7, 0x1C, 0xF1, + 0xCE, 0x1C, 0xE3, 0x8E, 0x38, 0xE7, 0x87, 0xB0, 0x07, 0x00, 0x70, 0x0F, + 0x03, 0xF8, 0x0D, 0xDF, 0x71, 0xAC, 0xF0, 0x38, 0x0E, 0x03, 0x81, 0xC0, + 0x70, 0x1C, 0x0E, 0x00, 0x1D, 0x99, 0x8C, 0x46, 0x23, 0x80, 0xE0, 0x70, + 0x1C, 0x06, 0x23, 0x19, 0x17, 0x00, 0x0C, 0x10, 0xE3, 0xF3, 0x86, 0x1C, + 0x38, 0x71, 0xC3, 0x87, 0x0E, 0x9E, 0x38, 0x00, 0xF8, 0xE3, 0x8E, 0x38, + 0xC3, 0x9C, 0x71, 0xC7, 0x18, 0x71, 0x87, 0x38, 0xE3, 0x8E, 0xFA, 0xF3, + 0xAE, 0x3C, 0xF0, 0xDC, 0x33, 0x0C, 0xC2, 0x31, 0x8C, 0xC3, 0x60, 0xF0, + 0x38, 0x0C, 0x02, 0x00, 0xE0, 0x86, 0xE3, 0x0C, 0xC6, 0x19, 0x9C, 0x23, + 0x78, 0xC7, 0xF9, 0x0E, 0x74, 0x1C, 0xF0, 0x31, 0xC0, 0x43, 0x00, 0x84, + 0x00, 0x0E, 0x31, 0xF3, 0x83, 0xA0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x1C, + 0x00, 0xE0, 0x0B, 0x02, 0x5D, 0x3C, 0xF1, 0xC3, 0x00, 0x04, 0x67, 0x8C, + 0x79, 0x87, 0x10, 0xE2, 0x1C, 0x81, 0x90, 0x3A, 0x07, 0x80, 0xF0, 0x1C, + 0x03, 0x00, 0x40, 0x08, 0x32, 0x07, 0x80, 0x3F, 0xCF, 0xE6, 0x30, 0x08, + 0x04, 0x02, 0x01, 0x00, 0xC0, 0x30, 0x1E, 0x0F, 0x98, 0x76, 0x07, 0x00, + 0x01, 0xE0, 0x70, 0x1C, 0x03, 0x80, 0x60, 0x1C, 0x03, 0x80, 0x60, 0x0C, + 0x03, 0x80, 0xF0, 0x3C, 0x07, 0x00, 0x40, 0x0C, 0x01, 0x80, 0x70, 0x0E, + 0x01, 0xC0, 0x30, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xE0, + 0x18, 0x06, 0x01, 0x80, 0xE0, 0x38, 0x0C, 0x03, 0x00, 0xC0, 0x10, 0x1F, + 0x07, 0x03, 0x80, 0xE0, 0x30, 0x0C, 0x07, 0x01, 0x80, 0xE0, 0xE0, 0x00, + 0x38, 0x0F, 0xCD, 0x1F, 0x80, 0xE0}; + +const GFXglyph FreeSerifBoldItalic12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 6, 0, 1}, // 0x20 ' ' + {0, 8, 17, 9, 2, -15}, // 0x21 '!' + {17, 9, 7, 13, 4, -15}, // 0x22 '"' + {25, 14, 16, 12, -1, -15}, // 0x23 '#' + {53, 12, 20, 12, 0, -17}, // 0x24 '$' + {83, 18, 18, 20, 1, -16}, // 0x25 '%' + {124, 16, 17, 19, 0, -15}, // 0x26 '&' + {158, 3, 7, 7, 3, -15}, // 0x27 ''' + {161, 7, 21, 8, 1, -15}, // 0x28 '(' + {180, 7, 21, 8, -1, -15}, // 0x29 ')' + {199, 10, 10, 12, 1, -15}, // 0x2A '*' + {212, 12, 12, 14, 1, -11}, // 0x2B '+' + {230, 5, 8, 6, -2, -3}, // 0x2C ',' + {235, 6, 3, 8, 0, -6}, // 0x2D '-' + {238, 4, 4, 6, 0, -2}, // 0x2E '.' + {240, 10, 16, 8, 0, -15}, // 0x2F '/' + {260, 11, 17, 12, 0, -15}, // 0x30 '0' + {284, 10, 17, 12, 0, -15}, // 0x31 '1' + {306, 11, 17, 12, 0, -15}, // 0x32 '2' + {330, 11, 17, 12, 0, -15}, // 0x33 '3' + {354, 13, 16, 12, 0, -15}, // 0x34 '4' + {380, 12, 17, 12, 0, -15}, // 0x35 '5' + {406, 11, 17, 12, 1, -15}, // 0x36 '6' + {430, 11, 16, 12, 2, -15}, // 0x37 '7' + {452, 11, 17, 12, 0, -15}, // 0x38 '8' + {476, 11, 17, 12, 0, -15}, // 0x39 '9' + {500, 7, 12, 6, 0, -10}, // 0x3A ':' + {511, 8, 15, 6, -1, -10}, // 0x3B ';' + {526, 12, 13, 14, 1, -12}, // 0x3C '<' + {546, 12, 6, 14, 2, -8}, // 0x3D '=' + {555, 13, 13, 14, 1, -12}, // 0x3E '>' + {577, 9, 17, 12, 2, -15}, // 0x3F '?' + {597, 17, 16, 20, 1, -15}, // 0x40 '@' + {631, 16, 17, 17, 0, -15}, // 0x41 'A' + {665, 16, 17, 15, 0, -15}, // 0x42 'B' + {699, 15, 17, 15, 1, -15}, // 0x43 'C' + {731, 18, 17, 17, 0, -15}, // 0x44 'D' + {770, 17, 17, 15, 0, -15}, // 0x45 'E' + {807, 16, 17, 15, 0, -15}, // 0x46 'F' + {841, 17, 17, 17, 1, -15}, // 0x47 'G' + {878, 20, 17, 18, 0, -15}, // 0x48 'H' + {921, 10, 17, 9, 0, -15}, // 0x49 'I' + {943, 14, 18, 12, 0, -15}, // 0x4A 'J' + {975, 17, 17, 16, 0, -15}, // 0x4B 'K' + {1012, 15, 17, 15, 0, -15}, // 0x4C 'L' + {1044, 23, 17, 21, 0, -15}, // 0x4D 'M' + {1093, 19, 17, 17, 0, -15}, // 0x4E 'N' + {1134, 16, 17, 16, 1, -15}, // 0x4F 'O' + {1168, 16, 17, 14, 0, -15}, // 0x50 'P' + {1202, 16, 21, 16, 1, -15}, // 0x51 'Q' + {1244, 16, 17, 16, 0, -15}, // 0x52 'R' + {1278, 12, 17, 12, 0, -15}, // 0x53 'S' + {1304, 15, 17, 14, 2, -15}, // 0x54 'T' + {1336, 16, 17, 17, 3, -15}, // 0x55 'U' + {1370, 16, 16, 17, 3, -15}, // 0x56 'V' + {1402, 21, 16, 22, 3, -15}, // 0x57 'W' + {1444, 17, 17, 17, 0, -15}, // 0x58 'X' + {1481, 14, 17, 15, 3, -15}, // 0x59 'Y' + {1511, 15, 17, 13, 0, -15}, // 0x5A 'Z' + {1543, 10, 20, 8, -1, -15}, // 0x5B '[' + {1568, 6, 16, 10, 3, -15}, // 0x5C '\' + {1580, 9, 20, 8, -1, -15}, // 0x5D ']' + {1603, 10, 9, 14, 2, -15}, // 0x5E '^' + {1615, 12, 1, 12, 0, 4}, // 0x5F '_' + {1617, 5, 4, 8, 2, -15}, // 0x60 '`' + {1620, 12, 12, 12, 0, -10}, // 0x61 'a' + {1638, 11, 18, 12, 1, -16}, // 0x62 'b' + {1663, 9, 12, 10, 1, -10}, // 0x63 'c' + {1677, 14, 18, 12, 0, -16}, // 0x64 'd' + {1709, 9, 12, 10, 1, -10}, // 0x65 'e' + {1723, 14, 22, 12, -2, -16}, // 0x66 'f' + {1762, 13, 16, 12, -1, -10}, // 0x67 'g' + {1788, 13, 18, 13, 0, -16}, // 0x68 'h' + {1818, 6, 17, 7, 1, -15}, // 0x69 'i' + {1831, 11, 21, 8, -2, -15}, // 0x6A 'j' + {1860, 13, 18, 12, 0, -16}, // 0x6B 'k' + {1890, 7, 18, 7, 1, -16}, // 0x6C 'l' + {1906, 18, 12, 18, 0, -10}, // 0x6D 'm' + {1933, 12, 12, 13, 0, -10}, // 0x6E 'n' + {1951, 10, 12, 11, 1, -10}, // 0x6F 'o' + {1966, 14, 16, 12, -2, -10}, // 0x70 'p' + {1994, 12, 16, 12, 0, -10}, // 0x71 'q' + {2018, 10, 11, 10, 0, -10}, // 0x72 'r' + {2032, 9, 12, 9, 0, -10}, // 0x73 's' + {2046, 7, 15, 7, 1, -13}, // 0x74 't' + {2060, 12, 12, 13, 1, -10}, // 0x75 'u' + {2078, 10, 11, 11, 1, -10}, // 0x76 'v' + {2092, 15, 11, 16, 1, -10}, // 0x77 'w' + {2113, 13, 12, 11, -1, -10}, // 0x78 'x' + {2133, 11, 16, 10, -1, -10}, // 0x79 'y' + {2155, 10, 13, 10, 0, -10}, // 0x7A 'z' + {2172, 11, 21, 8, 0, -16}, // 0x7B '{' + {2201, 2, 16, 6, 3, -15}, // 0x7C '|' + {2205, 10, 21, 8, -3, -16}, // 0x7D '}' + {2232, 11, 4, 14, 1, -7}}; // 0x7E '~' + +const GFXfont FreeSerifBoldItalic12pt7b PROGMEM = { + (uint8_t *)FreeSerifBoldItalic12pt7bBitmaps, + (GFXglyph *)FreeSerifBoldItalic12pt7bGlyphs, 0x20, 0x7E, 29}; + +// Approx. 2910 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic18pt7b.h new file mode 100644 index 0000000..750b81d --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic18pt7b.h @@ -0,0 +1,499 @@ +const uint8_t FreeSerifBoldItalic18pt7bBitmaps[] PROGMEM = { + 0x01, 0xC0, 0x7C, 0x0F, 0x81, 0xF0, 0x3E, 0x07, 0x80, 0xF0, 0x3C, 0x07, + 0x80, 0xE0, 0x1C, 0x03, 0x00, 0x60, 0x0C, 0x03, 0x00, 0x60, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x81, 0xF8, 0x3F, 0x07, 0xE0, 0x78, 0x00, 0x38, + 0x1D, 0xE0, 0xF7, 0x83, 0xDC, 0x0E, 0x70, 0x39, 0xC0, 0xE6, 0x03, 0x18, + 0x0C, 0x40, 0x23, 0x01, 0x80, 0x00, 0x38, 0x60, 0x07, 0x0E, 0x00, 0x70, + 0xC0, 0x06, 0x1C, 0x00, 0xE1, 0xC0, 0x0E, 0x38, 0x01, 0xC3, 0x81, 0xFF, + 0xFF, 0x1F, 0xFF, 0xE1, 0xFF, 0xFE, 0x03, 0x86, 0x00, 0x30, 0xE0, 0x07, + 0x0E, 0x00, 0x71, 0xC0, 0x0E, 0x1C, 0x0F, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, + 0xFF, 0xF0, 0x1C, 0x30, 0x01, 0x87, 0x00, 0x38, 0x70, 0x03, 0x0E, 0x00, + 0x70, 0xE0, 0x07, 0x0C, 0x00, 0xE1, 0xC0, 0x00, 0x00, 0x08, 0x00, 0x0C, + 0x00, 0x7E, 0x00, 0xFF, 0xC0, 0xF3, 0x70, 0x71, 0x9C, 0x70, 0xC6, 0x38, + 0x43, 0x1C, 0x61, 0x0F, 0x30, 0x87, 0xD8, 0x03, 0xF8, 0x00, 0xFE, 0x00, + 0x3F, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x01, 0xFC, 0x00, 0xDF, 0x10, 0x47, + 0x88, 0x63, 0xCC, 0x31, 0xE6, 0x10, 0xF3, 0x98, 0x71, 0xCC, 0x78, 0x7E, + 0x78, 0x07, 0xF8, 0x03, 0xF0, 0x01, 0x80, 0x00, 0xC0, 0x00, 0x03, 0xC0, + 0x18, 0x01, 0xFE, 0x0F, 0x00, 0x7C, 0xFF, 0xC0, 0x1F, 0x0F, 0x98, 0x07, + 0xC1, 0x06, 0x00, 0xF8, 0x21, 0x80, 0x3E, 0x04, 0x30, 0x07, 0xC1, 0x8C, + 0x00, 0xF0, 0x21, 0x80, 0x1E, 0x0C, 0x60, 0x03, 0xC1, 0x0C, 0x00, 0x78, + 0xC3, 0x03, 0xC7, 0xF8, 0x61, 0xFC, 0x7C, 0x18, 0x7C, 0xC0, 0x06, 0x1F, + 0x08, 0x00, 0xC7, 0xC1, 0x00, 0x30, 0xF0, 0x20, 0x06, 0x3E, 0x04, 0x01, + 0x87, 0xC1, 0x00, 0x30, 0xF0, 0x20, 0x0C, 0x1E, 0x0C, 0x03, 0x03, 0xC1, + 0x00, 0x60, 0x3C, 0xC0, 0x18, 0x07, 0xF8, 0x03, 0x00, 0x7C, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x1F, 0xF0, 0x00, 0x1E, 0x38, 0x00, 0x0E, 0x0E, 0x00, + 0x0F, 0x07, 0x00, 0x07, 0x83, 0x80, 0x03, 0xC3, 0x80, 0x01, 0xE3, 0x80, + 0x00, 0xF7, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x7F, 0x0F, + 0xF0, 0xE7, 0x81, 0xE0, 0xE3, 0xE0, 0xE0, 0xE1, 0xF0, 0x60, 0xE0, 0x7C, + 0x60, 0xF0, 0x3E, 0x20, 0x78, 0x1F, 0xB0, 0x3C, 0x07, 0xF0, 0x1F, 0x03, + 0xF0, 0x0F, 0x80, 0xFC, 0x03, 0xF0, 0x7F, 0x8D, 0xFF, 0xEF, 0xFC, 0x7F, + 0xE3, 0xFC, 0x0F, 0xC0, 0x78, 0x00, 0x3B, 0xDE, 0xE7, 0x39, 0x8C, 0x46, + 0x00, 0x00, 0x60, 0x18, 0x06, 0x01, 0x80, 0x60, 0x1C, 0x07, 0x01, 0xE0, + 0x38, 0x0F, 0x01, 0xC0, 0x38, 0x0F, 0x01, 0xE0, 0x38, 0x07, 0x00, 0xE0, + 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x06, + 0x00, 0xC0, 0x08, 0x00, 0x80, 0x10, 0x00, 0x06, 0x00, 0x40, 0x04, 0x00, + 0x80, 0x18, 0x01, 0x00, 0x30, 0x06, 0x00, 0xC0, 0x1C, 0x03, 0x80, 0x70, + 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x01, 0xE0, 0x3C, 0x07, 0x00, 0xE0, 0x3C, + 0x07, 0x00, 0xE0, 0x38, 0x06, 0x01, 0xC0, 0x70, 0x18, 0x06, 0x01, 0x80, + 0x00, 0x07, 0x00, 0x38, 0x01, 0xC1, 0x8E, 0x3E, 0x23, 0xF9, 0x3F, 0xEB, + 0xE0, 0xE0, 0xFF, 0xF7, 0x93, 0xF8, 0x9F, 0x8E, 0x60, 0x70, 0x03, 0x80, + 0x08, 0x00, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, + 0x00, 0x0E, 0x00, 0x07, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, + 0x01, 0xC0, 0x00, 0x1C, 0x7C, 0xF9, 0xF1, 0xE1, 0xC3, 0x0C, 0x30, 0xC2, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x7B, 0xFF, 0xFF, 0x78, 0x00, 0x1C, + 0x00, 0xE0, 0x03, 0x80, 0x1E, 0x00, 0x70, 0x01, 0xC0, 0x0E, 0x00, 0x38, + 0x01, 0xC0, 0x07, 0x00, 0x38, 0x00, 0xE0, 0x07, 0x80, 0x1C, 0x00, 0x70, + 0x03, 0x80, 0x0E, 0x00, 0x70, 0x01, 0xC0, 0x0E, 0x00, 0x38, 0x01, 0xC0, + 0x07, 0x00, 0x1C, 0x00, 0xE0, 0x00, 0x00, 0xF0, 0x07, 0x30, 0x1C, 0x30, + 0x78, 0x60, 0xE0, 0xE3, 0xC1, 0xCF, 0x83, 0x9E, 0x0F, 0x3C, 0x1E, 0xF8, + 0x3D, 0xE0, 0x7B, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xBC, 0x0F, 0x78, 0x3E, + 0xF0, 0x7D, 0xE0, 0xF3, 0x81, 0xE7, 0x07, 0x8E, 0x0F, 0x0C, 0x3C, 0x18, + 0x70, 0x19, 0xC0, 0x1E, 0x00, 0x00, 0x06, 0x01, 0xF8, 0x1F, 0xF0, 0x03, + 0xE0, 0x07, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xE0, + 0x07, 0xC0, 0x0F, 0x80, 0x1E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xE0, 0x07, + 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, + 0x0F, 0xC0, 0xFF, 0xF0, 0x00, 0xF8, 0x01, 0xFC, 0x03, 0xFE, 0x06, 0x3F, + 0x08, 0x1F, 0x18, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0E, + 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x30, 0x00, 0x70, 0x00, 0xC0, + 0x01, 0x80, 0x03, 0x00, 0x06, 0x02, 0x0C, 0x06, 0x08, 0x0C, 0x1F, 0xFC, + 0x3F, 0xFC, 0x7F, 0xF8, 0xFF, 0xF8, 0x00, 0xF0, 0x07, 0xF8, 0x1F, 0xF0, + 0x61, 0xF0, 0x81, 0xE0, 0x03, 0xC0, 0x07, 0x80, 0x0E, 0x00, 0x3C, 0x00, + 0xE0, 0x07, 0xC0, 0x3F, 0xC0, 0x1F, 0x80, 0x0F, 0x80, 0x1F, 0x00, 0x1E, + 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x01, 0xC0, 0x07, 0x9C, 0x0E, 0x3C, + 0x38, 0x7F, 0xE0, 0x7E, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x70, 0x00, 0x3C, + 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x07, 0xE0, 0x02, 0xF8, 0x01, 0x3C, 0x00, + 0x9F, 0x00, 0x47, 0xC0, 0x31, 0xE0, 0x18, 0x78, 0x0C, 0x3E, 0x06, 0x0F, + 0x83, 0x03, 0xC1, 0x80, 0xF0, 0x7F, 0xFF, 0x1F, 0xFF, 0xCF, 0xFF, 0xF0, + 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x07, 0xC0, 0x01, + 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x60, 0x00, 0x30, 0x00, + 0x10, 0x00, 0x1F, 0x00, 0x0F, 0xE0, 0x0F, 0xF8, 0x07, 0xFE, 0x00, 0x3F, + 0x00, 0x07, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x1C, 0x00, + 0x0E, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x87, 0x83, 0x83, 0xE3, 0x81, + 0xFF, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x03, 0x80, 0x0F, 0x80, 0x1F, 0x00, + 0x3E, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x1F, 0x00, + 0x1F, 0xF0, 0x1F, 0xFE, 0x0F, 0xCF, 0x07, 0xC3, 0xC7, 0xE1, 0xE3, 0xE0, + 0xF1, 0xF0, 0x78, 0xF8, 0x3C, 0x78, 0x3E, 0x3C, 0x1F, 0x1E, 0x0F, 0x0F, + 0x0F, 0x83, 0x87, 0x81, 0xE7, 0x80, 0x7F, 0x80, 0x0F, 0x80, 0x00, 0x3F, + 0xFF, 0x3F, 0xFE, 0x3F, 0xFE, 0x7F, 0xFC, 0x60, 0x1C, 0x80, 0x38, 0x80, + 0x30, 0x00, 0x70, 0x00, 0x60, 0x00, 0xE0, 0x01, 0xC0, 0x01, 0xC0, 0x03, + 0x80, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x1C, + 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0xF0, 0x00, 0xE0, 0x00, 0x00, + 0xF8, 0x00, 0xFF, 0x00, 0xE1, 0xC0, 0xE0, 0xF0, 0xF0, 0x38, 0x78, 0x1C, + 0x3C, 0x0E, 0x1F, 0x07, 0x0F, 0x87, 0x07, 0xE7, 0x01, 0xFF, 0x00, 0x7E, + 0x00, 0x1F, 0x80, 0x3F, 0xE0, 0x73, 0xF0, 0x70, 0xFC, 0x70, 0x3E, 0x70, + 0x0F, 0x38, 0x07, 0x9C, 0x03, 0xCE, 0x01, 0xE7, 0x00, 0xE1, 0xC0, 0xE0, + 0x70, 0xE0, 0x0F, 0xC0, 0x00, 0x00, 0xF8, 0x01, 0xFF, 0x01, 0xF3, 0xC1, + 0xF0, 0xE1, 0xF0, 0x70, 0xF0, 0x3C, 0xF8, 0x1E, 0x7C, 0x0F, 0x3C, 0x0F, + 0x9E, 0x07, 0xCF, 0x03, 0xE7, 0x83, 0xF3, 0xC1, 0xF0, 0xF1, 0xF8, 0x3F, + 0xF8, 0x0F, 0xFC, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x3E, 0x00, + 0x3C, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0xF0, 0x00, 0xC0, 0x00, 0x00, 0x07, + 0x83, 0xF0, 0xFC, 0x3F, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x3F, 0x0F, 0xC3, 0xF0, 0x78, 0x00, 0x03, 0xC0, 0xFC, + 0x1F, 0x83, 0xF0, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x03, 0xC0, 0x7C, 0x0F, 0x80, 0xF0, 0x0E, 0x01, 0x80, 0x30, 0x0C, + 0x03, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x7C, 0x00, + 0x7F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00, 0xFE, 0x00, + 0x3E, 0x00, 0x0F, 0xC0, 0x01, 0xFC, 0x00, 0x1F, 0xE0, 0x01, 0xFE, 0x00, + 0x0F, 0xE0, 0x00, 0xFF, 0x00, 0x0F, 0xC0, 0x00, 0xF0, 0x00, 0x04, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, + 0x38, 0x00, 0x0F, 0x80, 0x03, 0xF8, 0x00, 0x3F, 0x80, 0x03, 0xFC, 0x00, + 0x3F, 0xC0, 0x01, 0xFC, 0x00, 0x1F, 0xC0, 0x01, 0xF0, 0x00, 0xFC, 0x00, + 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFC, 0x03, 0xFC, 0x00, 0xFC, 0x00, + 0x3C, 0x00, 0x08, 0x00, 0x00, 0x07, 0xC0, 0xFF, 0x0E, 0x3C, 0x70, 0xF3, + 0xC7, 0x8C, 0x3C, 0x01, 0xE0, 0x1F, 0x00, 0xF0, 0x07, 0x80, 0x78, 0x07, + 0x80, 0x30, 0x03, 0x00, 0x10, 0x01, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x80, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0x78, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0xFF, 0xF8, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x03, 0x81, 0xC0, + 0x00, 0xE1, 0xC0, 0x18, 0x38, 0xE0, 0x3F, 0xCC, 0xE0, 0x3C, 0xE7, 0x70, + 0x3C, 0x71, 0xF0, 0x1C, 0x30, 0xF8, 0x1E, 0x38, 0x7C, 0x0E, 0x1C, 0x3E, + 0x0F, 0x0E, 0x1F, 0x07, 0x0E, 0x0F, 0x83, 0x87, 0x0D, 0xC1, 0xC7, 0x86, + 0x70, 0xE5, 0xC6, 0x38, 0x7C, 0xFE, 0x1C, 0x1C, 0x3E, 0x07, 0x00, 0x00, + 0x01, 0xC0, 0x00, 0x00, 0x78, 0x00, 0x40, 0x1F, 0x00, 0xE0, 0x03, 0xFF, + 0xE0, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x03, + 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x17, 0xC0, 0x00, 0x67, 0x80, 0x00, 0x8F, + 0x00, 0x03, 0x1F, 0x00, 0x0C, 0x3E, 0x00, 0x10, 0x7C, 0x00, 0x60, 0xF8, + 0x00, 0x81, 0xF0, 0x03, 0xFF, 0xE0, 0x0F, 0xFF, 0xE0, 0x18, 0x07, 0xC0, + 0x60, 0x0F, 0x81, 0xC0, 0x1F, 0x03, 0x00, 0x3E, 0x0E, 0x00, 0x7C, 0x3C, + 0x00, 0xFC, 0xFE, 0x0F, 0xFE, 0x07, 0xFF, 0xE0, 0x01, 0xFF, 0xFC, 0x01, + 0xF8, 0x7E, 0x01, 0xF8, 0x3F, 0x01, 0xF0, 0x3F, 0x01, 0xF0, 0x3F, 0x01, + 0xF0, 0x3F, 0x03, 0xE0, 0x3F, 0x03, 0xE0, 0x7E, 0x03, 0xE0, 0xFC, 0x03, + 0xE3, 0xF0, 0x07, 0xFF, 0x80, 0x07, 0xC3, 0xE0, 0x07, 0xC1, 0xF8, 0x0F, + 0xC0, 0xF8, 0x0F, 0x80, 0xFC, 0x0F, 0x80, 0xFC, 0x0F, 0x80, 0xFC, 0x1F, + 0x80, 0xFC, 0x1F, 0x01, 0xFC, 0x1F, 0x01, 0xF8, 0x1F, 0x03, 0xF0, 0x3F, + 0x0F, 0xE0, 0x7F, 0xFF, 0xC0, 0xFF, 0xFE, 0x00, 0x00, 0x1F, 0x82, 0x01, + 0xFF, 0xE8, 0x07, 0xE0, 0xF0, 0x3F, 0x80, 0xE0, 0xFE, 0x00, 0xC1, 0xF8, + 0x01, 0x87, 0xE0, 0x02, 0x1F, 0x80, 0x04, 0x3F, 0x00, 0x00, 0xFC, 0x00, + 0x01, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0x80, 0x00, + 0x3F, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFC, 0x00, 0x01, 0xF8, 0x00, 0x03, + 0xF0, 0x00, 0x03, 0xE0, 0x01, 0x07, 0xE0, 0x06, 0x07, 0xE0, 0x18, 0x07, + 0xE0, 0xE0, 0x07, 0xFF, 0x00, 0x01, 0xF8, 0x00, 0x07, 0xFF, 0xE0, 0x01, + 0xFF, 0xFE, 0x00, 0x1F, 0x87, 0xE0, 0x07, 0xE0, 0x7C, 0x01, 0xF0, 0x1F, + 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x0F, 0x80, 0x3F, 0x03, 0xE0, + 0x0F, 0xC0, 0xF8, 0x03, 0xF0, 0x3E, 0x00, 0xFC, 0x1F, 0x00, 0x3F, 0x07, + 0xC0, 0x0F, 0xC1, 0xF0, 0x07, 0xF0, 0xFC, 0x01, 0xF8, 0x3E, 0x00, 0x7E, + 0x0F, 0x80, 0x3F, 0x83, 0xE0, 0x0F, 0xC1, 0xF8, 0x07, 0xF0, 0x7C, 0x01, + 0xF8, 0x1F, 0x00, 0xFC, 0x07, 0xC0, 0x7E, 0x03, 0xF0, 0x7E, 0x01, 0xFF, + 0xFF, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x03, 0xFF, 0xFC, + 0x07, 0xE0, 0x78, 0x0F, 0xC0, 0x60, 0x1F, 0x00, 0x40, 0x3E, 0x00, 0x80, + 0x7C, 0x01, 0x01, 0xF8, 0x10, 0x03, 0xE0, 0x60, 0x07, 0xC3, 0x80, 0x0F, + 0xFF, 0x00, 0x3F, 0xFE, 0x00, 0x7C, 0x38, 0x00, 0xF8, 0x30, 0x03, 0xF0, + 0x60, 0x07, 0xC0, 0x80, 0x0F, 0x81, 0x00, 0x1F, 0x00, 0x10, 0x7E, 0x00, + 0x60, 0xF8, 0x01, 0xC1, 0xF0, 0x07, 0x03, 0xE0, 0x1E, 0x0F, 0xC0, 0xFC, + 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFE, 0x03, 0xFF, 0xFC, + 0x07, 0xE0, 0x78, 0x0F, 0xC0, 0x60, 0x1F, 0x00, 0x40, 0x3E, 0x00, 0x80, + 0x7C, 0x01, 0x01, 0xF8, 0x20, 0x03, 0xE0, 0xC0, 0x07, 0xC3, 0x80, 0x0F, + 0xFE, 0x00, 0x3F, 0xFC, 0x00, 0x7C, 0x38, 0x00, 0xF8, 0x30, 0x03, 0xF0, + 0x60, 0x07, 0xC0, 0x80, 0x0F, 0x81, 0x00, 0x1F, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0xC0, 0x00, + 0x3F, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xC2, 0x00, 0xFF, 0xF6, + 0x01, 0xF8, 0x3C, 0x03, 0xE0, 0x1C, 0x0F, 0xC0, 0x0C, 0x0F, 0xC0, 0x08, + 0x1F, 0x80, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x7E, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFC, 0x03, 0xFF, + 0xFC, 0x00, 0xFC, 0xFC, 0x00, 0xF8, 0xFC, 0x00, 0xF8, 0xFC, 0x00, 0xF8, + 0xFC, 0x00, 0xF0, 0x7C, 0x01, 0xF0, 0x7E, 0x01, 0xF0, 0x3E, 0x01, 0xF0, + 0x1F, 0x83, 0xE0, 0x0F, 0xFF, 0x80, 0x01, 0xFC, 0x00, 0x07, 0xFF, 0x3F, + 0xF8, 0x0F, 0xE0, 0x7F, 0x00, 0x7E, 0x01, 0xF8, 0x03, 0xF0, 0x0F, 0x80, + 0x1F, 0x00, 0x7C, 0x00, 0xF8, 0x07, 0xE0, 0x07, 0xC0, 0x3E, 0x00, 0x7E, + 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0x80, 0x1F, 0x00, 0xF8, 0x00, 0xF8, 0x07, + 0xC0, 0x0F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF0, 0x03, 0xE0, 0x1F, 0x00, + 0x3F, 0x00, 0xF8, 0x01, 0xF0, 0x07, 0xC0, 0x0F, 0x80, 0x7E, 0x00, 0x7C, + 0x03, 0xE0, 0x07, 0xE0, 0x1F, 0x00, 0x3E, 0x00, 0xF8, 0x01, 0xF0, 0x0F, + 0xC0, 0x0F, 0x80, 0x7C, 0x00, 0xFC, 0x03, 0xE0, 0x0F, 0xE0, 0x3F, 0x80, + 0xFF, 0xC7, 0xFF, 0x00, 0x07, 0xFE, 0x03, 0xF8, 0x07, 0xE0, 0x0F, 0xC0, + 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, + 0x80, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x03, 0xF0, 0x07, 0xC0, 0x0F, 0x80, + 0x1F, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xC0, 0x3F, + 0x80, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0x01, 0xFE, 0x00, 0x0F, 0xC0, 0x00, + 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x1F, 0x80, 0x01, 0xF0, 0x00, + 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, + 0x07, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x0F, 0xC0, + 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x61, 0xF0, + 0x0F, 0x3F, 0x00, 0xE7, 0xE0, 0x07, 0xFC, 0x00, 0x3F, 0x00, 0x00, 0x07, + 0xFF, 0x3F, 0x80, 0xFE, 0x07, 0x80, 0x7E, 0x03, 0x00, 0x3F, 0x03, 0x00, + 0x1F, 0x03, 0x00, 0x0F, 0x83, 0x00, 0x07, 0xC3, 0x00, 0x07, 0xE3, 0x00, + 0x03, 0xE3, 0x00, 0x01, 0xF3, 0x00, 0x00, 0xFB, 0x80, 0x00, 0xFB, 0xC0, + 0x00, 0x7F, 0xE0, 0x00, 0x3E, 0xF8, 0x00, 0x3F, 0x7C, 0x00, 0x1F, 0x1F, + 0x00, 0x0F, 0x8F, 0x80, 0x07, 0xC7, 0xE0, 0x07, 0xE1, 0xF0, 0x03, 0xE0, + 0xFC, 0x01, 0xF0, 0x3E, 0x00, 0xF8, 0x1F, 0x00, 0xFC, 0x07, 0xC0, 0xFE, + 0x07, 0xF0, 0xFF, 0xCF, 0xFC, 0x00, 0x07, 0xFF, 0x00, 0x07, 0xF0, 0x00, + 0x1F, 0x80, 0x00, 0x7E, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x1F, + 0x00, 0x00, 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x3E, 0x00, + 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x1F, 0x00, 0x00, 0xFC, 0x00, 0x03, + 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x3E, 0x00, 0x11, 0xF0, 0x00, 0xC7, 0xC0, + 0x06, 0x1F, 0x00, 0x38, 0x7C, 0x01, 0xE3, 0xF0, 0x3F, 0x9F, 0xFF, 0xFC, + 0xFF, 0xFF, 0xF0, 0x07, 0xF8, 0x00, 0x7F, 0x80, 0xFC, 0x00, 0x3F, 0x80, + 0x3E, 0x00, 0x3F, 0x80, 0x1F, 0x00, 0x3F, 0x80, 0x1F, 0x80, 0x1F, 0xC0, + 0x0F, 0xE0, 0x1B, 0xE0, 0x07, 0xF0, 0x0D, 0xF0, 0x02, 0xF8, 0x0D, 0xF0, + 0x03, 0x7C, 0x0C, 0xF8, 0x01, 0xBE, 0x06, 0x7C, 0x00, 0xDF, 0x06, 0x7C, + 0x00, 0xCF, 0x83, 0x3E, 0x00, 0x67, 0xC3, 0x1F, 0x00, 0x31, 0xE3, 0x0F, + 0x80, 0x38, 0xF9, 0x8F, 0x80, 0x18, 0x7D, 0x87, 0xC0, 0x0C, 0x3F, 0x83, + 0xE0, 0x06, 0x1F, 0xC1, 0xF0, 0x06, 0x0F, 0xC1, 0xF0, 0x03, 0x07, 0xC0, + 0xF8, 0x01, 0x83, 0xE0, 0x7C, 0x01, 0xC0, 0xE0, 0x7E, 0x00, 0xE0, 0x70, + 0x3F, 0x00, 0xF8, 0x30, 0x3F, 0x80, 0xFF, 0x10, 0x7F, 0xF0, 0x00, 0x07, + 0xF0, 0x0F, 0xE0, 0x3E, 0x00, 0x78, 0x07, 0xE0, 0x06, 0x00, 0x7C, 0x00, + 0xC0, 0x1F, 0xC0, 0x10, 0x03, 0xF8, 0x06, 0x00, 0x6F, 0x80, 0xC0, 0x19, + 0xF0, 0x10, 0x03, 0x3F, 0x02, 0x00, 0x63, 0xE0, 0xC0, 0x0C, 0x7C, 0x18, + 0x03, 0x07, 0xC2, 0x00, 0x60, 0xF8, 0x40, 0x0C, 0x0F, 0x98, 0x03, 0x81, + 0xF3, 0x00, 0x60, 0x3F, 0x40, 0x0C, 0x03, 0xF8, 0x01, 0x80, 0x7F, 0x00, + 0x60, 0x07, 0xC0, 0x0C, 0x00, 0xF8, 0x01, 0x80, 0x0F, 0x00, 0x70, 0x01, + 0xE0, 0x0E, 0x00, 0x18, 0x03, 0xE0, 0x03, 0x00, 0x02, 0x00, 0x60, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0xC0, 0x07, 0xC3, 0xE0, 0x1F, 0x03, 0xC0, + 0x7C, 0x03, 0xC1, 0xF0, 0x07, 0x87, 0xE0, 0x0F, 0x8F, 0x80, 0x1F, 0x3F, + 0x00, 0x3E, 0x7C, 0x00, 0x7D, 0xF8, 0x01, 0xFB, 0xE0, 0x03, 0xF7, 0xC0, + 0x07, 0xDF, 0x80, 0x1F, 0xBF, 0x00, 0x3F, 0x7C, 0x00, 0x7C, 0xF8, 0x01, + 0xF9, 0xF0, 0x03, 0xE3, 0xE0, 0x0F, 0xC7, 0xC0, 0x1F, 0x07, 0x80, 0x7C, + 0x0F, 0x81, 0xF0, 0x0F, 0x87, 0xC0, 0x0F, 0xFE, 0x00, 0x07, 0xF0, 0x00, + 0x07, 0xFF, 0xE0, 0x03, 0xFF, 0xF0, 0x07, 0xE3, 0xF0, 0x0F, 0x83, 0xE0, + 0x1F, 0x07, 0xE0, 0x3E, 0x0F, 0xC0, 0x7C, 0x1F, 0x81, 0xF0, 0x3F, 0x03, + 0xE0, 0xFE, 0x07, 0xC1, 0xF8, 0x0F, 0x87, 0xF0, 0x3E, 0x1F, 0xC0, 0x7F, + 0xFE, 0x00, 0xFF, 0xF0, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x80, + 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x01, 0xF0, 0x00, + 0x03, 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x80, 0x00, 0xFF, 0xC0, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0xFF, 0xC0, 0x07, 0xC3, 0xE0, 0x1F, 0x03, 0xC0, + 0x7C, 0x03, 0xC1, 0xF0, 0x07, 0x87, 0xE0, 0x0F, 0x8F, 0x80, 0x1F, 0x3F, + 0x00, 0x3E, 0x7C, 0x00, 0x7D, 0xF8, 0x01, 0xFB, 0xF0, 0x03, 0xF7, 0xC0, + 0x07, 0xDF, 0x80, 0x0F, 0xBF, 0x00, 0x3F, 0x7C, 0x00, 0x7C, 0xF8, 0x01, + 0xF9, 0xF0, 0x03, 0xE3, 0xE0, 0x07, 0xC7, 0xC0, 0x1F, 0x07, 0x80, 0x7C, + 0x0F, 0x01, 0xF0, 0x0F, 0x07, 0x80, 0x07, 0xFE, 0x00, 0x03, 0x80, 0x00, + 0x0C, 0x00, 0x00, 0x3C, 0x00, 0x20, 0xFF, 0xC1, 0x87, 0xFF, 0xFE, 0x1E, + 0xFF, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xFF, 0xE0, 0x01, 0xFF, 0xFC, + 0x01, 0xF8, 0x7E, 0x01, 0xF8, 0x3F, 0x01, 0xF8, 0x3F, 0x01, 0xF0, 0x3F, + 0x01, 0xF0, 0x3F, 0x03, 0xF0, 0x3F, 0x03, 0xE0, 0x7E, 0x03, 0xE0, 0xFE, + 0x03, 0xE1, 0xF8, 0x07, 0xFF, 0xF0, 0x07, 0xFF, 0x80, 0x07, 0xDF, 0xC0, + 0x0F, 0xCF, 0xC0, 0x0F, 0xCF, 0xC0, 0x0F, 0x8F, 0xE0, 0x0F, 0x87, 0xE0, + 0x1F, 0x87, 0xE0, 0x1F, 0x03, 0xF0, 0x1F, 0x03, 0xF0, 0x1F, 0x03, 0xF0, + 0x3F, 0x01, 0xF8, 0x7F, 0x01, 0xF8, 0xFF, 0xE1, 0xFE, 0x00, 0xF8, 0x40, + 0xFF, 0xB0, 0x38, 0x3C, 0x1C, 0x07, 0x0F, 0x01, 0xC3, 0xC0, 0x20, 0xF0, + 0x08, 0x3E, 0x02, 0x0F, 0xC0, 0x03, 0xF8, 0x00, 0x7F, 0x00, 0x0F, 0xE0, + 0x01, 0xFC, 0x00, 0x3F, 0x80, 0x07, 0xE0, 0x00, 0xFC, 0x00, 0x1F, 0x00, + 0x03, 0xC4, 0x00, 0xF1, 0x00, 0x3C, 0x60, 0x0F, 0x38, 0x07, 0x8F, 0x83, + 0xC2, 0x3F, 0xE0, 0x83, 0xF0, 0x00, 0x3F, 0xFF, 0xF9, 0xFF, 0xFF, 0xCF, + 0x1F, 0x1E, 0x70, 0xF8, 0x77, 0x0F, 0x83, 0x30, 0x7C, 0x09, 0x03, 0xE0, + 0x40, 0x3F, 0x02, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x07, + 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, + 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xF8, + 0x00, 0x07, 0xC0, 0x00, 0x7E, 0x00, 0x07, 0xF0, 0x00, 0xFF, 0xF0, 0x00, + 0x7F, 0xF0, 0xFF, 0x1F, 0xC0, 0x3E, 0x1F, 0x80, 0x1C, 0x1F, 0x80, 0x18, + 0x1F, 0x00, 0x18, 0x1F, 0x00, 0x18, 0x1F, 0x00, 0x30, 0x3F, 0x00, 0x30, + 0x3E, 0x00, 0x30, 0x3E, 0x00, 0x30, 0x7E, 0x00, 0x60, 0x7C, 0x00, 0x60, + 0x7C, 0x00, 0x60, 0x7C, 0x00, 0xC0, 0x7C, 0x00, 0xC0, 0xF8, 0x00, 0xC0, + 0xF8, 0x00, 0xC0, 0xF8, 0x01, 0x80, 0xF8, 0x01, 0x80, 0xF8, 0x03, 0x80, + 0xF8, 0x03, 0x00, 0x7C, 0x06, 0x00, 0x7E, 0x1E, 0x00, 0x3F, 0xF8, 0x00, + 0x0F, 0xE0, 0x00, 0xFF, 0xE0, 0x7F, 0x3F, 0x80, 0x1C, 0x1F, 0x80, 0x18, + 0x1F, 0x80, 0x18, 0x1F, 0x80, 0x30, 0x1F, 0x80, 0x30, 0x0F, 0x80, 0x60, + 0x0F, 0x80, 0x40, 0x0F, 0x80, 0xC0, 0x0F, 0x81, 0x80, 0x0F, 0x81, 0x00, + 0x0F, 0xC3, 0x00, 0x0F, 0xC6, 0x00, 0x07, 0xC6, 0x00, 0x07, 0xCC, 0x00, + 0x07, 0xC8, 0x00, 0x07, 0xD8, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF0, 0x00, + 0x07, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0xFF, 0xCF, 0xF8, 0x7E, 0x7F, 0x07, + 0xE0, 0x38, 0x7C, 0x07, 0x80, 0x60, 0xF8, 0x0F, 0x00, 0x81, 0xF0, 0x1E, + 0x03, 0x03, 0xE0, 0x3E, 0x04, 0x07, 0xE0, 0xFC, 0x18, 0x07, 0xC1, 0xF8, + 0x20, 0x0F, 0x87, 0xF0, 0xC0, 0x1F, 0x0B, 0xE1, 0x00, 0x3E, 0x37, 0xC6, + 0x00, 0x7C, 0x47, 0x88, 0x00, 0xF9, 0x8F, 0x30, 0x01, 0xF2, 0x1F, 0x40, + 0x03, 0xEC, 0x3E, 0x80, 0x03, 0xF0, 0x7F, 0x00, 0x07, 0xE0, 0xFC, 0x00, + 0x0F, 0x81, 0xF8, 0x00, 0x1F, 0x03, 0xE0, 0x00, 0x3C, 0x07, 0xC0, 0x00, + 0x78, 0x07, 0x00, 0x00, 0xF0, 0x0E, 0x00, 0x00, 0xC0, 0x18, 0x00, 0x01, + 0x80, 0x30, 0x00, 0x02, 0x00, 0x40, 0x00, 0x0F, 0xFE, 0x3F, 0x81, 0xFC, + 0x07, 0x80, 0x7C, 0x03, 0x00, 0x3F, 0x03, 0x00, 0x0F, 0x83, 0x80, 0x07, + 0xC1, 0x80, 0x03, 0xE1, 0x80, 0x00, 0xF9, 0x80, 0x00, 0x7D, 0x80, 0x00, + 0x3F, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, 0xE0, 0x00, + 0x01, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xBE, 0x00, 0x00, 0xCF, 0x00, + 0x00, 0xC7, 0xC0, 0x00, 0xC3, 0xE0, 0x00, 0xC1, 0xF0, 0x00, 0xC0, 0x7C, + 0x00, 0xE0, 0x3E, 0x00, 0xE0, 0x1F, 0x00, 0xF8, 0x1F, 0xE0, 0xFF, 0x1F, + 0xF8, 0x00, 0xFF, 0xC3, 0xF9, 0xF8, 0x07, 0x87, 0xC0, 0x38, 0x3E, 0x01, + 0x81, 0xF0, 0x18, 0x07, 0xC0, 0x80, 0x3E, 0x0C, 0x01, 0xF0, 0xC0, 0x07, + 0xC4, 0x00, 0x3E, 0x60, 0x01, 0xF6, 0x00, 0x07, 0xA0, 0x00, 0x3F, 0x00, + 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0xFC, 0x00, 0x07, 0xC0, 0x00, 0x3E, + 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, + 0x7E, 0x00, 0x07, 0xF0, 0x00, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x3F, + 0xFF, 0xC3, 0xE0, 0x7E, 0x1C, 0x07, 0xE0, 0xC0, 0x3E, 0x0C, 0x03, 0xF0, + 0x40, 0x3F, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0x80, 0x01, 0xF8, 0x00, 0x1F, + 0x80, 0x00, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x00, + 0x7E, 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x83, 0xE0, 0x0C, 0x3F, 0x00, + 0xC3, 0xF0, 0x0E, 0x1F, 0x00, 0xF1, 0xF8, 0x1F, 0x9F, 0xFF, 0xF8, 0xFF, + 0xFF, 0xC0, 0x01, 0xFC, 0x0F, 0xE0, 0x3C, 0x00, 0xE0, 0x03, 0x80, 0x1E, + 0x00, 0x78, 0x01, 0xC0, 0x07, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0x80, 0x0E, + 0x00, 0x38, 0x01, 0xE0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x03, 0xC0, 0x0F, + 0x00, 0x38, 0x00, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x70, 0x01, 0xC0, 0x0F, + 0x00, 0x3C, 0x00, 0xFF, 0x03, 0xF8, 0x00, 0xE0, 0x38, 0x07, 0x01, 0xC0, + 0x70, 0x0C, 0x03, 0x80, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x70, 0x0C, 0x03, + 0x80, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x70, 0x0C, 0x03, 0x80, 0xE0, 0x38, + 0x07, 0x01, 0xC0, 0x03, 0xFC, 0x0F, 0xF0, 0x03, 0x80, 0x0E, 0x00, 0x38, + 0x01, 0xE0, 0x07, 0x80, 0x1C, 0x00, 0x70, 0x03, 0xC0, 0x0F, 0x00, 0x38, + 0x00, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x70, 0x01, 0xC0, 0x0F, 0x00, 0x3C, + 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x00, 0x1C, + 0x00, 0xF0, 0x03, 0xC0, 0xFE, 0x03, 0xF8, 0x00, 0x03, 0xC0, 0x03, 0xC0, + 0x07, 0xE0, 0x07, 0xE0, 0x0E, 0x70, 0x0E, 0x70, 0x1C, 0x78, 0x1C, 0x38, + 0x3C, 0x3C, 0x38, 0x1C, 0x78, 0x1E, 0x70, 0x0E, 0xF0, 0x0E, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xE1, 0xE3, 0xC1, 0xC1, 0xC0, 0xC0, 0x00, + 0xF7, 0x80, 0xFD, 0xE0, 0x7C, 0xF0, 0x3C, 0x3C, 0x1E, 0x0F, 0x0F, 0x83, + 0x83, 0xC1, 0xE1, 0xE0, 0x78, 0x78, 0x1C, 0x3E, 0x0F, 0x0F, 0x03, 0xC3, + 0xC1, 0xF0, 0xF0, 0xFC, 0xFE, 0x6F, 0x6F, 0xF3, 0xF1, 0xF8, 0xF8, 0x3C, + 0x1C, 0x00, 0x01, 0xE0, 0x1F, 0xC0, 0x07, 0xC0, 0x07, 0xC0, 0x07, 0x80, + 0x07, 0x80, 0x0F, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x3C, 0x1E, 0xFE, + 0x1F, 0x9F, 0x1F, 0x0F, 0x1E, 0x0F, 0x3E, 0x0F, 0x3C, 0x0F, 0x3C, 0x1F, + 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x3C, 0x78, 0x3C, 0xF0, 0x78, 0xF0, 0xF0, + 0xF1, 0xE0, 0x7F, 0xC0, 0x3F, 0x00, 0x01, 0xF0, 0x3F, 0xC3, 0xCE, 0x3C, + 0xF3, 0xC7, 0x1E, 0x01, 0xE0, 0x0F, 0x00, 0xF8, 0x07, 0x80, 0x3C, 0x01, + 0xE0, 0x0F, 0x03, 0x78, 0x31, 0xE3, 0x0F, 0xF0, 0x1E, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x78, 0x00, 0x0F, + 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x01, 0xEF, 0x00, 0x7F, 0xE0, 0x3E, 0x7C, + 0x07, 0x8F, 0x01, 0xE1, 0xE0, 0x78, 0x3C, 0x0F, 0x0F, 0x83, 0xC1, 0xE0, + 0x78, 0x3C, 0x1E, 0x0F, 0x83, 0xC1, 0xF0, 0x78, 0x7C, 0x0F, 0x0F, 0x91, + 0xE3, 0xF6, 0x3F, 0xDF, 0x83, 0xF3, 0xE0, 0x3C, 0x38, 0x00, 0x01, 0xE0, + 0x3F, 0x83, 0xCE, 0x3C, 0x73, 0xC3, 0x9E, 0x1D, 0xE1, 0xCF, 0x1C, 0xFB, + 0xC7, 0xF8, 0x3C, 0x01, 0xE0, 0x0F, 0x02, 0x78, 0x31, 0xE3, 0x0F, 0xF0, + 0x1E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x1D, 0xC0, 0x01, 0xCE, 0x00, 0x1C, + 0x70, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x07, 0x80, 0x00, + 0x3C, 0x00, 0x0F, 0xFC, 0x00, 0x7F, 0xE0, 0x00, 0xF0, 0x00, 0x07, 0x80, + 0x00, 0x3C, 0x00, 0x03, 0xE0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x07, + 0x80, 0x00, 0x7C, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, + 0x07, 0x80, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, + 0x00, 0x0F, 0x00, 0x0E, 0x70, 0x00, 0x77, 0x80, 0x03, 0xF8, 0x00, 0x0F, + 0x80, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0xFC, 0x1F, 0x1F, 0x87, 0xC3, 0xC1, + 0xF0, 0x78, 0x3C, 0x1F, 0x07, 0x83, 0xE0, 0xF0, 0xF8, 0x0E, 0x3E, 0x01, + 0xFF, 0x80, 0x3F, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x00, 0x7F, 0x80, 0x0F, + 0xFE, 0x00, 0x7F, 0xF0, 0x70, 0xFF, 0x1C, 0x03, 0xE3, 0x80, 0x3C, 0x70, + 0x07, 0x0F, 0x03, 0xE0, 0xFF, 0xF0, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x03, + 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, + 0x1F, 0x00, 0x0F, 0x0E, 0x07, 0x9F, 0x83, 0xDF, 0xC3, 0xE9, 0xE1, 0xE8, + 0xF0, 0xF8, 0xF8, 0x7C, 0x78, 0x7C, 0x3C, 0x3E, 0x3E, 0x1E, 0x1E, 0x1F, + 0x0F, 0x0F, 0x0F, 0x87, 0x87, 0xCB, 0xC3, 0xCB, 0xE1, 0xE9, 0xE0, 0xFC, + 0xF0, 0x38, 0x00, 0x03, 0x03, 0xC1, 0xE0, 0xF0, 0x30, 0x00, 0x00, 0x00, + 0x07, 0x3F, 0x87, 0x83, 0xC1, 0xE0, 0xF0, 0xF0, 0x78, 0x3C, 0x1E, 0x1E, + 0x0F, 0x27, 0x17, 0x93, 0xF1, 0xF8, 0x70, 0x00, 0x00, 0x06, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0xFE, 0x00, 0x3E, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, + 0x00, 0x7C, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0xF8, 0x00, 0xF0, + 0x00, 0xF0, 0x00, 0xF0, 0x01, 0xF0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, + 0x03, 0xC0, 0xE3, 0xC0, 0xE7, 0x80, 0xFF, 0x00, 0x7C, 0x00, 0x1F, 0xC0, + 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, + 0x00, 0x1F, 0x00, 0x0F, 0x3F, 0x87, 0x87, 0x83, 0xC3, 0x03, 0xE3, 0x01, + 0xE3, 0x00, 0xF3, 0x00, 0x7B, 0x80, 0x7B, 0xC0, 0x3F, 0xE0, 0x1E, 0xF0, + 0x1F, 0x78, 0x0F, 0x1E, 0x07, 0x8F, 0x13, 0xC7, 0x93, 0xE1, 0xF9, 0xE0, + 0xF8, 0xF0, 0x38, 0x00, 0x1F, 0xC0, 0xF8, 0x1F, 0x03, 0xC0, 0x78, 0x1F, + 0x03, 0xE0, 0x78, 0x0F, 0x01, 0xE0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x0F, + 0x01, 0xE0, 0x3C, 0x0F, 0x81, 0xE0, 0x3C, 0x8F, 0x31, 0xEC, 0x3F, 0x07, + 0xC0, 0x70, 0x00, 0x01, 0x87, 0x07, 0x0F, 0xE7, 0xE7, 0xE0, 0xF3, 0xF9, + 0xF8, 0x3D, 0x9E, 0x9E, 0x0F, 0x47, 0xC7, 0x83, 0xE1, 0xD1, 0xE1, 0xF8, + 0xF8, 0xF0, 0x7C, 0x3C, 0x3C, 0x1F, 0x0F, 0x1F, 0x0F, 0x87, 0xC7, 0x83, + 0xE1, 0xE1, 0xE0, 0xF0, 0x78, 0x78, 0x3C, 0x1E, 0x3C, 0x1F, 0x0F, 0x0F, + 0x27, 0x83, 0xC3, 0xD1, 0xE0, 0xF0, 0xFC, 0xF8, 0x78, 0x1C, 0x00, 0x01, + 0x8F, 0x0F, 0xE7, 0xE0, 0xF3, 0xF8, 0x3C, 0x9E, 0x0F, 0x47, 0x87, 0xA3, + 0xC1, 0xE8, 0xF0, 0x7C, 0x3C, 0x1E, 0x1E, 0x0F, 0x87, 0x83, 0xE1, 0xE0, + 0xF0, 0xF8, 0x3C, 0x3C, 0x1F, 0x0F, 0x27, 0x83, 0xD1, 0xE0, 0xFC, 0x78, + 0x1C, 0x00, 0x01, 0xF0, 0x0E, 0x30, 0x38, 0x70, 0xF0, 0xF3, 0xC1, 0xE7, + 0x83, 0xDE, 0x07, 0xBC, 0x1F, 0xF8, 0x3F, 0xE0, 0x7B, 0xC0, 0xF7, 0x83, + 0xCF, 0x07, 0x9E, 0x1E, 0x1C, 0x38, 0x1C, 0xE0, 0x1F, 0x00, 0x00, 0xE3, + 0x80, 0xFD, 0xF8, 0x0F, 0xFF, 0x81, 0xE8, 0xF0, 0x3E, 0x1E, 0x07, 0x83, + 0xC0, 0xF0, 0x78, 0x3E, 0x1F, 0x07, 0x83, 0xC0, 0xF0, 0x78, 0x1E, 0x1F, + 0x07, 0x83, 0xC0, 0xF0, 0xF8, 0x1E, 0x1E, 0x03, 0xC7, 0x80, 0xFF, 0xE0, + 0x1E, 0xF0, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x03, 0xC0, 0x00, + 0xF8, 0x00, 0x3F, 0xC0, 0x00, 0x01, 0xEF, 0x07, 0xFF, 0x0F, 0x1E, 0x1E, + 0x1E, 0x1E, 0x1E, 0x3C, 0x1E, 0x7C, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0xF0, + 0x7C, 0xF0, 0x78, 0xF0, 0xF8, 0xF0, 0xF8, 0xF1, 0xF0, 0xFE, 0xF0, 0x7E, + 0xF0, 0x39, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xC0, 0x03, + 0xC0, 0x1F, 0xF8, 0x03, 0x9C, 0x7F, 0x7C, 0x3D, 0xF8, 0x7A, 0xE0, 0xF8, + 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF0, 0x01, + 0xE0, 0x07, 0xC0, 0x0F, 0x00, 0x1E, 0x00, 0x7C, 0x00, 0x07, 0x18, 0xFF, + 0xC7, 0x1C, 0x70, 0x63, 0x81, 0x1E, 0x08, 0xF8, 0x07, 0xE0, 0x1F, 0x00, + 0x7C, 0x01, 0xF0, 0x07, 0x84, 0x3C, 0x20, 0xE1, 0x87, 0x1C, 0x70, 0x9E, + 0x00, 0x00, 0x80, 0x60, 0x30, 0x1C, 0x1F, 0x1F, 0xF7, 0xFC, 0x78, 0x1E, + 0x07, 0x83, 0xC0, 0xF0, 0x3C, 0x1F, 0x07, 0x81, 0xE0, 0x79, 0x3C, 0x4F, + 0x23, 0xF0, 0xFC, 0x1C, 0x00, 0x0F, 0x0F, 0x3F, 0x87, 0x8F, 0x83, 0xC7, + 0xC1, 0xE3, 0xE1, 0xE1, 0xE0, 0xF0, 0xF0, 0x78, 0xF8, 0x78, 0x78, 0x3C, + 0x3C, 0x3E, 0x1E, 0x1F, 0x1E, 0x1F, 0x0F, 0x17, 0x97, 0x9B, 0xCB, 0xF9, + 0xF9, 0xF8, 0xF8, 0x78, 0x38, 0x00, 0x18, 0x37, 0xC3, 0xDE, 0x1E, 0x78, + 0x73, 0xC1, 0x9E, 0x08, 0xF0, 0xC7, 0x84, 0x3C, 0x41, 0xE4, 0x0F, 0x40, + 0x7C, 0x03, 0xC0, 0x1C, 0x00, 0xC0, 0x04, 0x00, 0x38, 0x10, 0xDF, 0x06, + 0x3D, 0xE0, 0xC7, 0xBC, 0x38, 0x73, 0xC7, 0x06, 0x79, 0xF0, 0x8F, 0x3E, + 0x11, 0xEB, 0xC4, 0x3F, 0x79, 0x07, 0xCF, 0x60, 0xF9, 0xE8, 0x1E, 0x3E, + 0x03, 0x87, 0x80, 0x70, 0xF0, 0x0C, 0x0C, 0x01, 0x01, 0x00, 0x03, 0x83, + 0x87, 0xF1, 0xF0, 0x3C, 0xF8, 0x0F, 0x60, 0x03, 0xD0, 0x00, 0xF8, 0x00, + 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x1F, 0x00, 0x0F, + 0xC0, 0x02, 0xF1, 0x39, 0x3C, 0xCF, 0xCF, 0xE3, 0xE1, 0xF0, 0x70, 0x38, + 0x00, 0x01, 0x83, 0x07, 0xE3, 0xC1, 0xF1, 0xE0, 0x78, 0xF0, 0x3E, 0x18, + 0x1F, 0x08, 0x07, 0x84, 0x03, 0xC6, 0x01, 0xE2, 0x00, 0xFB, 0x00, 0x3D, + 0x00, 0x1F, 0x80, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xC0, 0x01, 0xE0, 0x00, + 0xE0, 0x00, 0x60, 0x00, 0x60, 0x0E, 0x60, 0x0F, 0xE0, 0x07, 0xE0, 0x01, + 0xC0, 0x00, 0x1F, 0xFC, 0x3F, 0xF8, 0x7F, 0xE1, 0x81, 0x82, 0x06, 0x00, + 0x08, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x10, 0x00, 0x40, + 0x01, 0x80, 0x07, 0xC0, 0x1F, 0x86, 0x3F, 0x8E, 0xCF, 0x9C, 0x07, 0x30, + 0x03, 0xC0, 0x00, 0x1E, 0x00, 0xF8, 0x03, 0xC0, 0x0F, 0x00, 0x1E, 0x00, + 0x38, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x1E, 0x00, 0x3C, + 0x00, 0x78, 0x01, 0xE0, 0x03, 0xC0, 0x1F, 0x00, 0x7E, 0x00, 0x30, 0x00, + 0x60, 0x00, 0xE0, 0x01, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x1E, 0x00, 0x38, + 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x0E, 0x00, 0x0C, 0x00, + 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x00, 0xF0, 0x00, 0x70, 0x00, 0x70, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, + 0x07, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xE0, 0x03, 0xC0, 0x07, + 0x80, 0x0F, 0x00, 0x1C, 0x00, 0x18, 0x00, 0x10, 0x00, 0xF0, 0x03, 0xF0, + 0x0F, 0x00, 0x1E, 0x00, 0x38, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, + 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x70, 0x01, 0xE0, 0x0F, 0x80, 0x7C, 0x00, + 0x3E, 0x00, 0x7F, 0xC6, 0xFF, 0xFF, 0x61, 0xFE, 0x00, 0x7C}; + +const GFXglyph FreeSerifBoldItalic18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 9, 0, 1}, // 0x20 ' ' + {0, 11, 25, 14, 2, -23}, // 0x21 '!' + {35, 14, 10, 19, 4, -23}, // 0x22 '"' + {53, 20, 25, 17, -1, -24}, // 0x23 '#' + {116, 17, 29, 18, 0, -25}, // 0x24 '$' + {178, 27, 25, 29, 1, -23}, // 0x25 '%' + {263, 25, 25, 27, 0, -23}, // 0x26 '&' + {342, 5, 10, 10, 4, -23}, // 0x27 ''' + {349, 11, 30, 12, 1, -23}, // 0x28 '(' + {391, 11, 30, 12, -2, -23}, // 0x29 ')' + {433, 13, 15, 18, 2, -23}, // 0x2A '*' + {458, 17, 17, 20, 1, -16}, // 0x2B '+' + {495, 7, 11, 9, -2, -4}, // 0x2C ',' + {505, 9, 4, 12, 0, -9}, // 0x2D '-' + {510, 6, 5, 9, 0, -3}, // 0x2E '.' + {514, 14, 25, 12, 0, -23}, // 0x2F '/' + {558, 15, 25, 18, 1, -23}, // 0x30 '0' + {605, 15, 25, 17, 0, -23}, // 0x31 '1' + {652, 16, 25, 18, 0, -23}, // 0x32 '2' + {702, 15, 25, 17, 1, -23}, // 0x33 '3' + {749, 18, 24, 17, 0, -23}, // 0x34 '4' + {803, 17, 25, 18, 0, -23}, // 0x35 '5' + {857, 17, 25, 18, 1, -23}, // 0x36 '6' + {911, 16, 24, 17, 3, -23}, // 0x37 '7' + {959, 17, 25, 18, 0, -23}, // 0x38 '8' + {1013, 17, 25, 18, 0, -23}, // 0x39 '9' + {1067, 10, 17, 9, 0, -15}, // 0x3A ':' + {1089, 11, 22, 9, -1, -15}, // 0x3B ';' + {1120, 18, 19, 20, 1, -18}, // 0x3C '<' + {1163, 18, 10, 20, 2, -13}, // 0x3D '=' + {1186, 18, 19, 20, 2, -18}, // 0x3E '>' + {1229, 13, 25, 17, 3, -23}, // 0x3F '?' + {1270, 25, 25, 29, 2, -23}, // 0x40 '@' + {1349, 23, 25, 24, 0, -23}, // 0x41 'A' + {1421, 24, 25, 22, 0, -23}, // 0x42 'B' + {1496, 23, 25, 22, 1, -23}, // 0x43 'C' + {1568, 26, 25, 25, 0, -23}, // 0x44 'D' + {1650, 23, 25, 22, 0, -23}, // 0x45 'E' + {1722, 23, 25, 21, 0, -23}, // 0x46 'F' + {1794, 24, 25, 25, 2, -23}, // 0x47 'G' + {1869, 29, 25, 26, 0, -23}, // 0x48 'H' + {1960, 15, 25, 13, 0, -23}, // 0x49 'I' + {2007, 20, 27, 17, 0, -23}, // 0x4A 'J' + {2075, 25, 25, 23, 0, -23}, // 0x4B 'K' + {2154, 22, 25, 21, 0, -23}, // 0x4C 'L' + {2223, 33, 25, 31, 0, -23}, // 0x4D 'M' + {2327, 27, 25, 25, 0, -23}, // 0x4E 'N' + {2412, 23, 25, 24, 1, -23}, // 0x4F 'O' + {2484, 23, 25, 21, 0, -23}, // 0x50 'P' + {2556, 23, 31, 24, 1, -23}, // 0x51 'Q' + {2646, 24, 25, 23, 0, -23}, // 0x52 'R' + {2721, 18, 25, 18, 0, -23}, // 0x53 'S' + {2778, 21, 25, 21, 3, -23}, // 0x54 'T' + {2844, 24, 25, 25, 4, -23}, // 0x55 'U' + {2919, 24, 25, 25, 4, -23}, // 0x56 'V' + {2994, 31, 25, 32, 4, -23}, // 0x57 'W' + {3091, 25, 25, 24, 0, -23}, // 0x58 'X' + {3170, 21, 25, 22, 4, -23}, // 0x59 'Y' + {3236, 21, 25, 20, 0, -23}, // 0x5A 'Z' + {3302, 14, 30, 12, -1, -23}, // 0x5B '[' + {3355, 10, 25, 14, 4, -23}, // 0x5C '\' + {3387, 14, 30, 12, -2, -23}, // 0x5D ']' + {3440, 16, 13, 20, 2, -23}, // 0x5E '^' + {3466, 18, 3, 17, 0, 3}, // 0x5F '_' + {3473, 7, 6, 12, 3, -23}, // 0x60 '`' + {3479, 18, 17, 18, 0, -15}, // 0x61 'a' + {3518, 16, 26, 17, 1, -24}, // 0x62 'b' + {3570, 13, 17, 15, 1, -15}, // 0x63 'c' + {3598, 19, 25, 18, 1, -23}, // 0x64 'd' + {3658, 13, 17, 15, 1, -15}, // 0x65 'e' + {3686, 21, 32, 17, -3, -24}, // 0x66 'f' + {3770, 19, 23, 17, -1, -15}, // 0x67 'g' + {3825, 17, 25, 19, 1, -23}, // 0x68 'h' + {3879, 9, 25, 10, 1, -23}, // 0x69 'i' + {3908, 16, 31, 12, -3, -23}, // 0x6A 'j' + {3970, 17, 25, 18, 1, -23}, // 0x6B 'k' + {4024, 11, 25, 10, 1, -23}, // 0x6C 'l' + {4059, 26, 17, 27, 0, -15}, // 0x6D 'm' + {4115, 18, 17, 18, 0, -15}, // 0x6E 'n' + {4154, 15, 17, 17, 1, -15}, // 0x6F 'o' + {4186, 19, 23, 17, -2, -15}, // 0x70 'p' + {4241, 16, 23, 17, 1, -15}, // 0x71 'q' + {4287, 15, 16, 14, 0, -15}, // 0x72 'r' + {4317, 13, 17, 12, 0, -15}, // 0x73 's' + {4345, 10, 22, 10, 1, -20}, // 0x74 't' + {4373, 17, 17, 19, 1, -15}, // 0x75 'u' + {4410, 13, 16, 15, 2, -15}, // 0x76 'v' + {4436, 19, 16, 23, 3, -15}, // 0x77 'w' + {4474, 18, 17, 17, -1, -15}, // 0x78 'x' + {4513, 17, 23, 15, -2, -15}, // 0x79 'y' + {4562, 15, 19, 14, 0, -15}, // 0x7A 'z' + {4598, 15, 32, 12, 0, -24}, // 0x7B '{' + {4658, 3, 25, 9, 4, -23}, // 0x7C '|' + {4668, 15, 32, 12, -5, -24}, // 0x7D '}' + {4728, 16, 5, 20, 2, -11}}; // 0x7E '~' + +const GFXfont FreeSerifBoldItalic18pt7b PROGMEM = { + (uint8_t *)FreeSerifBoldItalic18pt7bBitmaps, + (GFXglyph *)FreeSerifBoldItalic18pt7bGlyphs, 0x20, 0x7E, 42}; + +// Approx. 5410 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic24pt7b.h new file mode 100644 index 0000000..f5b7ed8 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic24pt7b.h @@ -0,0 +1,792 @@ +const uint8_t FreeSerifBoldItalic24pt7bBitmaps[] PROGMEM = { + 0x00, 0x3C, 0x00, 0xFC, 0x01, 0xF8, 0x07, 0xF0, 0x0F, 0xE0, 0x1F, 0xC0, + 0x3F, 0x00, 0x7E, 0x00, 0xF8, 0x01, 0xF0, 0x07, 0xC0, 0x0F, 0x80, 0x1E, + 0x00, 0x3C, 0x00, 0x70, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x00, 0x0E, 0x00, + 0x18, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF0, 0x03, 0xF0, 0x0F, 0xF0, 0x1F, 0xE0, 0x3F, 0xC0, 0x3F, 0x00, + 0x3C, 0x00, 0x1C, 0x01, 0xC7, 0xC0, 0x7D, 0xF8, 0x1F, 0xBF, 0x03, 0xF7, + 0xC0, 0x7C, 0xF8, 0x0F, 0x9E, 0x01, 0xE3, 0xC0, 0x3C, 0x70, 0x07, 0x1E, + 0x00, 0xE3, 0x80, 0x38, 0x70, 0x07, 0x0C, 0x00, 0xC0, 0x00, 0x03, 0xC1, + 0xE0, 0x00, 0x70, 0x38, 0x00, 0x1E, 0x0F, 0x00, 0x03, 0xC1, 0xE0, 0x00, + 0x70, 0x38, 0x00, 0x1E, 0x0F, 0x00, 0x03, 0x81, 0xC0, 0x00, 0xF0, 0x78, + 0x00, 0x1E, 0x0F, 0x00, 0x07, 0x83, 0xC0, 0x1F, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xF0, 0x7F, 0xFF, 0xFC, 0x00, 0xE0, 0x70, 0x00, 0x3C, 0x1E, 0x00, + 0x07, 0x83, 0xC0, 0x00, 0xE0, 0x70, 0x00, 0x3C, 0x1E, 0x00, 0x07, 0x83, + 0xC0, 0x00, 0xE0, 0x70, 0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFC, 0x1F, + 0xFF, 0xFF, 0x00, 0x38, 0x1C, 0x00, 0x0F, 0x07, 0x80, 0x01, 0xE0, 0xF0, + 0x00, 0x38, 0x1C, 0x00, 0x0F, 0x07, 0x80, 0x01, 0xC0, 0xE0, 0x00, 0x78, + 0x3C, 0x00, 0x0F, 0x07, 0x80, 0x01, 0xC0, 0xE0, 0x00, 0x78, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x1F, + 0xE0, 0x00, 0x7F, 0xF8, 0x01, 0xF1, 0x9E, 0x01, 0xC1, 0x8F, 0x03, 0x83, + 0x8F, 0x03, 0x83, 0x06, 0x07, 0x83, 0x06, 0x07, 0x87, 0x06, 0x07, 0xC7, + 0x04, 0x07, 0xE6, 0x04, 0x07, 0xFE, 0x00, 0x03, 0xFE, 0x00, 0x03, 0xFF, + 0x00, 0x01, 0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x1F, + 0xE0, 0x00, 0x1F, 0xF0, 0x00, 0x3F, 0xF0, 0x00, 0x3B, 0xF8, 0x20, 0x31, + 0xF8, 0x20, 0x30, 0xF8, 0x60, 0x70, 0xF8, 0x60, 0x60, 0xF8, 0x60, 0x60, + 0xF8, 0xF0, 0xE0, 0xF0, 0xF0, 0xE1, 0xE0, 0x78, 0xC3, 0xE0, 0x3C, 0xC7, + 0xC0, 0x0F, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0xF0, + 0x00, 0x70, 0x00, 0xFF, 0x80, 0x1C, 0x00, 0x3F, 0x38, 0x1F, 0x00, 0x0F, + 0xC7, 0xFF, 0xE0, 0x03, 0xF0, 0x3F, 0xB8, 0x00, 0x7E, 0x04, 0x07, 0x00, + 0x1F, 0x80, 0x81, 0xC0, 0x03, 0xF0, 0x10, 0x38, 0x00, 0xFC, 0x02, 0x0E, + 0x00, 0x1F, 0x80, 0x81, 0x80, 0x03, 0xF0, 0x10, 0x70, 0x00, 0x7C, 0x06, + 0x1C, 0x00, 0x0F, 0x80, 0x83, 0x80, 0x01, 0xF0, 0x30, 0xE0, 0x00, 0x1E, + 0x0C, 0x1C, 0x07, 0xC3, 0xE3, 0x07, 0x03, 0xFC, 0x3F, 0xC0, 0xC0, 0xFC, + 0x43, 0xE0, 0x38, 0x3E, 0x0C, 0x00, 0x0E, 0x0F, 0xC0, 0x80, 0x01, 0xC3, + 0xF0, 0x10, 0x00, 0x70, 0xFC, 0x02, 0x00, 0x0C, 0x1F, 0x80, 0x40, 0x03, + 0x83, 0xE0, 0x08, 0x00, 0x60, 0xFC, 0x02, 0x00, 0x1C, 0x1F, 0x80, 0x40, + 0x07, 0x03, 0xE0, 0x10, 0x00, 0xE0, 0x7C, 0x02, 0x00, 0x38, 0x0F, 0x80, + 0xC0, 0x06, 0x01, 0xF0, 0x30, 0x01, 0xC0, 0x1F, 0x0C, 0x00, 0x30, 0x01, + 0xFF, 0x00, 0x0E, 0x00, 0x1F, 0x80, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x01, 0xF1, 0xE0, 0x00, 0x00, 0xF0, 0x78, 0x00, 0x00, + 0xF0, 0x3C, 0x00, 0x00, 0x78, 0x1E, 0x00, 0x00, 0x7C, 0x0F, 0x00, 0x00, + 0x3E, 0x0F, 0x80, 0x00, 0x1F, 0x07, 0x80, 0x00, 0x0F, 0x87, 0x80, 0x00, + 0x07, 0xC7, 0x80, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, + 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0x07, 0xFE, + 0x03, 0xCF, 0xC0, 0xFE, 0x03, 0xC7, 0xE0, 0x3C, 0x07, 0xC3, 0xF0, 0x1C, + 0x07, 0xC0, 0xFC, 0x0C, 0x03, 0xC0, 0x7E, 0x0E, 0x03, 0xE0, 0x3F, 0x0E, + 0x01, 0xF0, 0x1F, 0xC6, 0x01, 0xF8, 0x07, 0xF6, 0x00, 0xFC, 0x03, 0xFF, + 0x00, 0x7E, 0x00, 0xFF, 0x00, 0x3F, 0x80, 0x7F, 0x80, 0x1F, 0xC0, 0x1F, + 0xC0, 0x07, 0xF0, 0x0F, 0xF0, 0x13, 0xFE, 0x0F, 0xFE, 0x18, 0xFF, 0xFE, + 0xFF, 0xF8, 0x3F, 0xFE, 0x3F, 0xF8, 0x07, 0xF8, 0x03, 0xF0, 0x00, 0x1C, + 0x7D, 0xFB, 0xF7, 0xCF, 0x9E, 0x3C, 0x71, 0xE3, 0x87, 0x0C, 0x00, 0x00, + 0x04, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x3C, 0x01, + 0xE0, 0x0F, 0x00, 0x3C, 0x01, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, + 0xC0, 0x1E, 0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF0, 0x03, + 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, + 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x30, 0x00, 0xE0, 0x01, 0x80, + 0x06, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x60, 0x01, 0x80, 0x00, 0x00, 0x01, + 0x00, 0x06, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, 0x01, 0x80, 0x06, 0x00, + 0x1C, 0x00, 0x30, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x00, 0xF0, + 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x7C, + 0x01, 0xF0, 0x07, 0xC0, 0x1E, 0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, + 0x01, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0xF0, 0x03, 0x80, 0x1E, 0x00, 0xF0, + 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x06, 0x00, 0x30, 0x00, 0x80, 0x00, 0x00, + 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x07, 0x0E, 0x1D, 0xF1, + 0xC7, 0xFF, 0x11, 0xFF, 0xE2, 0x3F, 0x7E, 0x4F, 0xC0, 0x3E, 0x00, 0x07, + 0xC0, 0x3F, 0x27, 0xEF, 0xC4, 0x7F, 0xF8, 0x8F, 0xFE, 0x38, 0xFB, 0x87, + 0x0E, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0x70, 0x00, 0x00, + 0x78, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x78, + 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, + 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, + 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, + 0x07, 0x80, 0x00, 0x0F, 0x07, 0xE1, 0xFC, 0x7F, 0x1F, 0xC3, 0xF0, 0x7C, + 0x0E, 0x03, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x1C, 0x04, 0x00, 0x7F, 0xF7, + 0xFF, 0x7F, 0xEF, 0xFE, 0xFF, 0xE0, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0x7E, + 0x3C, 0x00, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, + 0x78, 0x00, 0x1E, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x07, + 0xC0, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x3C, + 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x01, 0xE0, + 0x00, 0x7C, 0x00, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x78, 0x00, 0x0F, 0x00, + 0x03, 0xC0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x03, 0xC0, 0x00, 0xF8, 0x00, + 0x1E, 0x00, 0x07, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0xE3, 0x80, 0x0F, 0x07, 0x00, 0x7C, 0x1C, 0x03, 0xE0, 0x78, 0x0F, 0x81, + 0xE0, 0x7C, 0x07, 0x83, 0xF0, 0x1F, 0x0F, 0xC0, 0xFC, 0x7E, 0x03, 0xF1, + 0xF8, 0x0F, 0xCF, 0xE0, 0x3F, 0x3F, 0x00, 0xFD, 0xFC, 0x07, 0xF7, 0xF0, + 0x1F, 0xDF, 0xC0, 0x7F, 0x7E, 0x01, 0xFB, 0xF8, 0x0F, 0xEF, 0xE0, 0x3F, + 0xBF, 0x80, 0xFE, 0xFC, 0x03, 0xF3, 0xF0, 0x1F, 0xCF, 0xC0, 0x7F, 0x3F, + 0x01, 0xF8, 0xFC, 0x07, 0xE3, 0xE0, 0x3F, 0x0F, 0x80, 0xFC, 0x1E, 0x07, + 0xE0, 0x78, 0x1F, 0x00, 0xE0, 0x78, 0x03, 0x83, 0xC0, 0x07, 0x1E, 0x00, + 0x07, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x01, 0xFE, 0x01, 0xFF, 0xE0, 0x00, + 0xFE, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x01, 0xFC, 0x00, + 0x1F, 0x80, 0x01, 0xF8, 0x00, 0x3F, 0x80, 0x03, 0xF8, 0x00, 0x3F, 0x00, + 0x03, 0xF0, 0x00, 0x7F, 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x07, 0xE0, + 0x00, 0xFE, 0x00, 0x0F, 0xC0, 0x00, 0xFC, 0x00, 0x1F, 0xC0, 0x01, 0xFC, + 0x00, 0x1F, 0x80, 0x01, 0xF8, 0x00, 0x3F, 0x80, 0x03, 0xF0, 0x00, 0x3F, + 0x00, 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x1F, 0xF8, 0x0F, 0xFF, 0xF0, 0x00, + 0x0F, 0x80, 0x01, 0xFF, 0x80, 0x0F, 0xFF, 0x00, 0x7F, 0xFE, 0x03, 0x83, + 0xF8, 0x0C, 0x07, 0xF0, 0x60, 0x1F, 0xC3, 0x00, 0x3F, 0x00, 0x00, 0xFC, + 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3E, 0x00, 0x01, 0xF8, 0x00, + 0x07, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x1E, + 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x3C, 0x00, 0x01, 0xE0, 0x00, + 0x0E, 0x00, 0x00, 0x70, 0x06, 0x03, 0x80, 0x10, 0x1C, 0x00, 0xC0, 0xE0, + 0x06, 0x07, 0xFF, 0xF8, 0x3F, 0xFF, 0xE1, 0xFF, 0xFF, 0x0F, 0xFF, 0xFC, + 0x3F, 0xFF, 0xE0, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0xC0, 0x0F, 0xFF, 0x80, + 0x60, 0xFE, 0x03, 0x01, 0xFC, 0x08, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, + 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0xFC, + 0x00, 0x07, 0xC0, 0x00, 0x3E, 0x00, 0x07, 0xF8, 0x00, 0x7F, 0xF0, 0x00, + 0x7F, 0xE0, 0x00, 0x3F, 0xC0, 0x00, 0x7F, 0x00, 0x01, 0xFC, 0x00, 0x03, + 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xE0, + 0x00, 0x0F, 0x80, 0x00, 0x3C, 0x1C, 0x01, 0xF0, 0xF8, 0x07, 0x83, 0xF0, + 0x3C, 0x0F, 0xE1, 0xE0, 0x1F, 0xFE, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xE0, 0x00, + 0x07, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x07, 0xFC, 0x00, + 0x06, 0xFC, 0x00, 0x06, 0x7E, 0x00, 0x06, 0x3F, 0x00, 0x06, 0x3F, 0x00, + 0x06, 0x1F, 0x80, 0x06, 0x0F, 0xC0, 0x06, 0x07, 0xE0, 0x03, 0x07, 0xE0, + 0x03, 0x03, 0xF0, 0x03, 0x01, 0xF8, 0x03, 0x01, 0xFC, 0x03, 0x00, 0xFC, + 0x03, 0x00, 0x7E, 0x03, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x07, 0xE0, 0x00, 0x03, + 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFE, 0x00, + 0x7F, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x07, 0xFE, 0x00, 0x07, + 0xFF, 0x00, 0x07, 0xFF, 0x80, 0x0F, 0xFF, 0xC0, 0x00, 0xFF, 0xE0, 0x00, + 0x1F, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF0, 0x00, + 0x03, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, + 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x78, 0x03, 0xC0, 0xFC, + 0x07, 0x80, 0xFC, 0x0F, 0x00, 0xFE, 0x1E, 0x00, 0x7F, 0xF8, 0x00, 0x1F, + 0xC0, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x01, 0xF8, 0x00, 0x0F, 0x80, 0x00, + 0x7E, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x01, 0xFC, + 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFE, 0x00, + 0x01, 0xFF, 0xF0, 0x07, 0xFF, 0xF0, 0x0F, 0xE1, 0xF0, 0x3F, 0x81, 0xF0, + 0x7F, 0x03, 0xF0, 0xFC, 0x07, 0xE3, 0xF8, 0x0F, 0xC7, 0xF0, 0x1F, 0x8F, + 0xC0, 0x7F, 0x1F, 0x80, 0xFE, 0x3F, 0x01, 0xFC, 0x7C, 0x03, 0xF0, 0xF8, + 0x0F, 0xE1, 0xF0, 0x1F, 0xC1, 0xE0, 0x3F, 0x03, 0xC0, 0xFC, 0x07, 0x81, + 0xF0, 0x07, 0x87, 0xC0, 0x07, 0xFF, 0x00, 0x03, 0xF8, 0x00, 0x0F, 0xFF, + 0xFC, 0x1F, 0xFF, 0xF8, 0x3F, 0xFF, 0xE0, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0x07, 0x00, 0x1C, 0x08, 0x00, 0x78, 0x30, 0x01, 0xE0, 0x40, 0x03, 0xC0, + 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, + 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x01, + 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, + 0x00, 0x01, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x78, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x80, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x03, 0xFE, 0x00, 0x3C, 0x78, + 0x03, 0xC1, 0xE0, 0x3C, 0x07, 0x81, 0xE0, 0x3C, 0x1F, 0x01, 0xE0, 0xF8, + 0x0F, 0x07, 0xC0, 0x78, 0x3F, 0x03, 0xC1, 0xF8, 0x3C, 0x0F, 0xE1, 0xE0, + 0x3F, 0x9E, 0x01, 0xFF, 0xC0, 0x07, 0xFC, 0x00, 0x3F, 0xC0, 0x00, 0xFF, + 0x00, 0x1F, 0xFC, 0x03, 0xCF, 0xF0, 0x3C, 0x3F, 0x83, 0xC0, 0xFC, 0x3C, + 0x03, 0xF1, 0xE0, 0x1F, 0x9E, 0x00, 0x7C, 0xF0, 0x03, 0xE7, 0x80, 0x1F, + 0x3C, 0x00, 0xF9, 0xE0, 0x07, 0x87, 0x00, 0x3C, 0x3C, 0x03, 0xC0, 0xF0, + 0x3C, 0x03, 0xC3, 0xC0, 0x07, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0xFF, + 0xE0, 0x03, 0xF1, 0xE0, 0x0F, 0xC1, 0xC0, 0x3F, 0x03, 0xC0, 0xFE, 0x07, + 0x81, 0xF8, 0x0F, 0x87, 0xF0, 0x1F, 0x0F, 0xC0, 0x3E, 0x3F, 0x80, 0xFC, + 0x7F, 0x01, 0xF8, 0xFC, 0x03, 0xF1, 0xF8, 0x07, 0xE3, 0xF0, 0x1F, 0xC7, + 0xE0, 0x3F, 0x8F, 0xC0, 0x7E, 0x0F, 0x81, 0xFC, 0x1F, 0x03, 0xF8, 0x1F, + 0x0F, 0xE0, 0x1F, 0xFF, 0xC0, 0x1F, 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x03, + 0xF8, 0x00, 0x0F, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x7E, 0x00, 0x01, 0xF8, + 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, + 0x1F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x01, 0xE0, 0x1F, 0x81, 0xFE, 0x0F, + 0xF0, 0x7F, 0x81, 0xF8, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x7E, 0x07, 0xF8, 0x3F, + 0xC1, 0xFE, 0x07, 0xE0, 0x1E, 0x00, 0x00, 0x78, 0x01, 0xF8, 0x07, 0xF8, + 0x0F, 0xF0, 0x1F, 0xE0, 0x1F, 0x80, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, + 0x1F, 0x80, 0x3F, 0x80, 0x7F, 0x00, 0xFE, 0x00, 0xFC, 0x00, 0xF8, 0x00, + 0xE0, 0x01, 0xC0, 0x07, 0x00, 0x0C, 0x00, 0x30, 0x01, 0xC0, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x7F, 0x00, 0x03, 0xFF, 0x00, 0x0F, 0xFC, 0x00, 0x3F, 0xF0, + 0x01, 0xFF, 0xC0, 0x07, 0xFE, 0x00, 0x1F, 0xF8, 0x00, 0x7F, 0xE0, 0x00, + 0xFF, 0x80, 0x00, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xE0, 0x00, + 0x1F, 0xF8, 0x00, 0x07, 0xFE, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, + 0x00, 0x0F, 0xFC, 0x00, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0x00, 0x0F, 0xFC, 0x00, 0x03, 0xFF, 0x80, + 0x00, 0x7F, 0xE0, 0x00, 0x1F, 0xF8, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x03, 0xFF, 0x00, 0x1F, 0xFC, + 0x00, 0x7F, 0xE0, 0x01, 0xFF, 0x80, 0x0F, 0xFE, 0x00, 0x3F, 0xF0, 0x00, + 0xFF, 0xC0, 0x00, 0xFF, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x01, 0xF8, 0x01, 0xFF, 0x80, 0xF1, 0xF0, 0x38, 0x3E, + 0x1E, 0x0F, 0xC7, 0xC3, 0xF1, 0xF0, 0xFC, 0x7C, 0x3F, 0x0E, 0x0F, 0xC0, + 0x07, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x07, + 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x0C, 0x00, + 0x06, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xC0, 0x01, 0xF8, 0x00, 0xFF, 0x00, 0x3F, 0xC0, 0x0F, 0xF0, + 0x01, 0xF8, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1F, + 0xFF, 0xC0, 0x00, 0x3F, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x7C, + 0x00, 0x03, 0x80, 0x7C, 0x00, 0x00, 0xE0, 0x7C, 0x00, 0x00, 0x38, 0x3C, + 0x00, 0xF0, 0x4C, 0x3E, 0x00, 0xFD, 0xE7, 0x1E, 0x00, 0xF3, 0xF1, 0x9F, + 0x00, 0xF1, 0xF0, 0xEF, 0x80, 0xF0, 0x78, 0x3F, 0x80, 0xF0, 0x3C, 0x1F, + 0xC0, 0x78, 0x1E, 0x0F, 0xE0, 0x78, 0x1E, 0x07, 0xF0, 0x3C, 0x0F, 0x03, + 0xF8, 0x3E, 0x07, 0x81, 0xFC, 0x1E, 0x07, 0x81, 0xFE, 0x0F, 0x03, 0xC0, + 0xDF, 0x07, 0x83, 0xC0, 0x6F, 0x83, 0xC3, 0xE0, 0x63, 0xE1, 0xF3, 0xF0, + 0x71, 0xF0, 0x7E, 0x78, 0x70, 0xF8, 0x1E, 0x3F, 0xF0, 0x3E, 0x00, 0x07, + 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x03, 0x80, 0x03, 0xF0, + 0x07, 0xC0, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, + 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x03, + 0x7E, 0x00, 0x00, 0x06, 0xFC, 0x00, 0x00, 0x19, 0xF8, 0x00, 0x00, 0x63, + 0xF8, 0x00, 0x00, 0xC7, 0xF0, 0x00, 0x03, 0x07, 0xE0, 0x00, 0x06, 0x0F, + 0xC0, 0x00, 0x18, 0x1F, 0x80, 0x00, 0x60, 0x3F, 0x00, 0x00, 0xC0, 0x7F, + 0x00, 0x03, 0x00, 0xFE, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x60, 0x03, 0xF0, 0x00, 0xC0, 0x07, 0xE0, 0x03, 0x00, 0x0F, 0xE0, + 0x0E, 0x00, 0x1F, 0xC0, 0x18, 0x00, 0x3F, 0x80, 0x70, 0x00, 0x7F, 0x01, + 0xC0, 0x00, 0xFE, 0x03, 0x80, 0x01, 0xFE, 0x1F, 0x80, 0x07, 0xFE, 0x7F, + 0xC0, 0x3F, 0xFF, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xE0, 0x00, + 0xFE, 0x1F, 0xE0, 0x01, 0xFC, 0x1F, 0xE0, 0x03, 0xF8, 0x1F, 0xE0, 0x0F, + 0xE0, 0x3F, 0xC0, 0x1F, 0xC0, 0x7F, 0x80, 0x3F, 0x80, 0xFF, 0x00, 0x7F, + 0x01, 0xFE, 0x01, 0xFC, 0x03, 0xF8, 0x03, 0xF8, 0x0F, 0xF0, 0x07, 0xF0, + 0x1F, 0xC0, 0x0F, 0xC0, 0x7F, 0x00, 0x3F, 0x87, 0xF0, 0x00, 0x7F, 0xFF, + 0x00, 0x00, 0xFE, 0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x07, 0xF0, 0x0F, + 0xE0, 0x0F, 0xE0, 0x1F, 0xC0, 0x1F, 0xC0, 0x3F, 0xC0, 0x7F, 0x00, 0x7F, + 0x80, 0xFE, 0x00, 0xFF, 0x01, 0xFC, 0x01, 0xFE, 0x03, 0xF0, 0x07, 0xFC, + 0x0F, 0xE0, 0x0F, 0xF0, 0x1F, 0xC0, 0x3F, 0xE0, 0x3F, 0x80, 0x7F, 0x80, + 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x0F, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x3F, + 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x08, 0x00, 0x7F, 0xFE, 0xC0, + 0x0F, 0xF0, 0x7E, 0x00, 0xFE, 0x01, 0xF0, 0x1F, 0xE0, 0x07, 0x01, 0xFE, + 0x00, 0x38, 0x1F, 0xE0, 0x00, 0xC0, 0xFE, 0x00, 0x06, 0x0F, 0xF0, 0x00, + 0x30, 0xFF, 0x00, 0x01, 0x07, 0xF8, 0x00, 0x08, 0x7F, 0x80, 0x00, 0x03, + 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x0F, 0xF0, + 0x00, 0x00, 0xFF, 0x80, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x03, + 0xFC, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x60, 0x7F, 0x00, 0x06, 0x03, 0xFC, 0x00, + 0x70, 0x0F, 0xE0, 0x07, 0x00, 0x1F, 0xC0, 0xE0, 0x00, 0x7F, 0xFE, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xFE, + 0x00, 0x00, 0xFE, 0x07, 0xF0, 0x00, 0x1F, 0xC0, 0x3F, 0x00, 0x03, 0xF8, + 0x07, 0xF0, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x07, 0xF0, 0x03, + 0xF8, 0x00, 0xFE, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x1F, 0xC0, 0x03, 0xFC, + 0x03, 0xF8, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x0F, 0xF0, 0x0F, 0xC0, 0x01, + 0xFE, 0x03, 0xF8, 0x00, 0x3F, 0xC0, 0x7F, 0x00, 0x07, 0xF8, 0x0F, 0xE0, + 0x01, 0xFF, 0x03, 0xF8, 0x00, 0x3F, 0xE0, 0x7F, 0x00, 0x07, 0xF8, 0x0F, + 0xE0, 0x00, 0xFF, 0x01, 0xFC, 0x00, 0x3F, 0xE0, 0x7F, 0x00, 0x07, 0xF8, + 0x0F, 0xE0, 0x01, 0xFF, 0x01, 0xFC, 0x00, 0x3F, 0xC0, 0x3F, 0x00, 0x0F, + 0xF0, 0x0F, 0xE0, 0x01, 0xFC, 0x01, 0xFC, 0x00, 0x7F, 0x00, 0x3F, 0x80, + 0x1F, 0xC0, 0x0F, 0xE0, 0x0F, 0xF0, 0x01, 0xFE, 0x07, 0xF8, 0x00, 0x7F, + 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, + 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x3F, 0xC0, 0x7E, 0x00, 0x3F, 0x80, 0x1E, + 0x00, 0x3F, 0x80, 0x0E, 0x00, 0x7F, 0x00, 0x06, 0x00, 0x7F, 0x00, 0x04, + 0x00, 0x7F, 0x00, 0x04, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x80, + 0x00, 0xFE, 0x01, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0x00, + 0x01, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x01, 0xFC, 0x3E, 0x00, + 0x03, 0xF8, 0x1E, 0x00, 0x03, 0xF8, 0x0C, 0x00, 0x03, 0xF8, 0x0C, 0x00, + 0x03, 0xF8, 0x0C, 0x00, 0x07, 0xF0, 0x08, 0x00, 0x07, 0xF0, 0x00, 0x08, + 0x07, 0xF0, 0x00, 0x18, 0x07, 0xE0, 0x00, 0x30, 0x0F, 0xE0, 0x00, 0x30, + 0x0F, 0xE0, 0x00, 0x70, 0x0F, 0xE0, 0x01, 0xE0, 0x1F, 0xC0, 0x07, 0xE0, + 0x1F, 0xE0, 0x3F, 0xE0, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xC0, + 0x01, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0x03, 0xF0, + 0x01, 0xFC, 0x01, 0xE0, 0x03, 0xF8, 0x01, 0xC0, 0x0F, 0xE0, 0x01, 0x80, + 0x1F, 0xC0, 0x02, 0x00, 0x3F, 0x80, 0x04, 0x00, 0x7F, 0x00, 0x00, 0x01, + 0xFC, 0x03, 0x00, 0x03, 0xF8, 0x04, 0x00, 0x07, 0xF0, 0x18, 0x00, 0x0F, + 0xC0, 0xF0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0xFE, + 0x1F, 0x00, 0x03, 0xF8, 0x1E, 0x00, 0x07, 0xF0, 0x18, 0x00, 0x0F, 0xE0, + 0x30, 0x00, 0x1F, 0xC0, 0x60, 0x00, 0x7F, 0x00, 0x80, 0x00, 0xFE, 0x01, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x01, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x02, 0x00, 0x0F, 0xFF, 0xEE, 0x00, 0x3F, 0xC0, + 0xFC, 0x00, 0x7F, 0x00, 0x7C, 0x01, 0xFE, 0x00, 0x3C, 0x03, 0xFC, 0x00, + 0x38, 0x07, 0xF8, 0x00, 0x18, 0x07, 0xF0, 0x00, 0x18, 0x0F, 0xF0, 0x00, + 0x10, 0x1F, 0xE0, 0x00, 0x10, 0x1F, 0xE0, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x7F, 0x80, 0x00, + 0x00, 0x7F, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x1F, + 0xFF, 0xFF, 0x00, 0x07, 0xFC, 0xFF, 0x00, 0x03, 0xF8, 0xFF, 0x00, 0x03, + 0xF8, 0xFF, 0x00, 0x03, 0xF0, 0xFF, 0x00, 0x03, 0xF0, 0xFF, 0x00, 0x07, + 0xF0, 0x7F, 0x00, 0x07, 0xF0, 0x7F, 0x00, 0x07, 0xE0, 0x7F, 0x80, 0x07, + 0xE0, 0x3F, 0x80, 0x0F, 0xE0, 0x1F, 0xC0, 0x0F, 0xC0, 0x0F, 0xE0, 0x0F, + 0xC0, 0x07, 0xF0, 0x3F, 0x80, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x01, 0xFF, 0xFC, 0x7F, 0xFE, 0x00, 0xFF, 0xC0, 0x3F, 0xF0, 0x00, + 0xFE, 0x00, 0x3F, 0xC0, 0x01, 0xFC, 0x00, 0x7F, 0x00, 0x03, 0xF8, 0x00, + 0xFE, 0x00, 0x0F, 0xE0, 0x01, 0xFC, 0x00, 0x1F, 0xC0, 0x07, 0xF0, 0x00, + 0x3F, 0x80, 0x0F, 0xE0, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x01, 0xFC, 0x00, + 0x7F, 0x00, 0x03, 0xF8, 0x00, 0xFE, 0x00, 0x07, 0xF0, 0x01, 0xFC, 0x00, + 0x0F, 0xC0, 0x03, 0xF8, 0x00, 0x3F, 0x80, 0x0F, 0xE0, 0x00, 0x7F, 0xFF, + 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xF8, 0x00, 0x7F, 0x00, + 0x07, 0xF0, 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x03, 0xF8, 0x00, 0x1F, 0xC0, + 0x07, 0xF0, 0x00, 0x7F, 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, 0x3F, 0x80, + 0x01, 0xFC, 0x00, 0x7F, 0x00, 0x03, 0xF0, 0x00, 0xFE, 0x00, 0x0F, 0xE0, + 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x07, 0xF0, 0x00, 0x3F, 0x80, 0x0F, 0xE0, + 0x00, 0xFF, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x7F, 0x80, 0x07, 0xFC, + 0x01, 0xFF, 0x00, 0x3F, 0xFF, 0x1F, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xF8, + 0x03, 0xFE, 0x00, 0x0F, 0xE0, 0x00, 0x7F, 0x00, 0x03, 0xF8, 0x00, 0x3F, + 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0x7E, 0x00, 0x07, 0xF0, 0x00, + 0x3F, 0x80, 0x01, 0xFC, 0x00, 0x0F, 0xC0, 0x00, 0xFE, 0x00, 0x07, 0xF0, + 0x00, 0x3F, 0x80, 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFE, 0x00, 0x07, + 0xE0, 0x00, 0x7F, 0x00, 0x03, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xFC, 0x00, + 0x0F, 0xE0, 0x00, 0x7F, 0x00, 0x03, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xFC, + 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, + 0x3F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0xE0, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x07, 0x03, 0xF0, 0x01, 0xF0, 0xFE, 0x00, + 0x3E, 0x1F, 0xC0, 0x07, 0xC3, 0xF0, 0x00, 0xF8, 0xFC, 0x00, 0x0F, 0x3F, + 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, + 0xFF, 0xC0, 0x1F, 0xF8, 0x0F, 0xC0, 0x03, 0xF8, 0x01, 0xC0, 0x00, 0xFE, + 0x00, 0xE0, 0x00, 0x3F, 0x80, 0x70, 0x00, 0x1F, 0xC0, 0x38, 0x00, 0x07, + 0xF0, 0x1C, 0x00, 0x01, 0xFC, 0x0E, 0x00, 0x00, 0x7F, 0x07, 0x00, 0x00, + 0x3F, 0x83, 0x80, 0x00, 0x0F, 0xE1, 0xC0, 0x00, 0x03, 0xF8, 0xE0, 0x00, + 0x00, 0xFC, 0x60, 0x00, 0x00, 0x7F, 0x7C, 0x00, 0x00, 0x1F, 0xFF, 0x00, + 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x03, 0xFB, 0xF8, 0x00, 0x00, 0xFE, 0x7F, + 0x00, 0x00, 0x3F, 0x9F, 0xC0, 0x00, 0x0F, 0xE3, 0xF8, 0x00, 0x07, 0xF0, + 0xFE, 0x00, 0x01, 0xFC, 0x1F, 0xC0, 0x00, 0x7F, 0x07, 0xF0, 0x00, 0x1F, + 0x80, 0xFE, 0x00, 0x0F, 0xE0, 0x3F, 0x80, 0x03, 0xF8, 0x0F, 0xE0, 0x00, + 0xFE, 0x01, 0xFC, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x1F, 0xE0, 0x0F, 0xE0, + 0x0F, 0xF8, 0x07, 0xFC, 0x0F, 0xFF, 0xC7, 0xFF, 0xC0, 0x01, 0xFF, 0xF8, + 0x00, 0x03, 0xFF, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x0F, + 0xE0, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x03, 0xF8, 0x00, 0x04, 0x1F, 0xC0, 0x00, 0x60, 0xFC, 0x00, 0x06, + 0x0F, 0xE0, 0x00, 0x30, 0x7F, 0x00, 0x03, 0x83, 0xF8, 0x00, 0x7C, 0x3F, + 0x80, 0x0F, 0xC1, 0xFE, 0x03, 0xFE, 0x1F, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x3F, 0xF0, 0x03, 0xFC, 0x00, 0x03, + 0xFC, 0x00, 0x3F, 0xC0, 0x00, 0x7F, 0x80, 0x03, 0xFC, 0x00, 0x0F, 0xF8, + 0x00, 0x3F, 0xC0, 0x00, 0xFF, 0x80, 0x03, 0xFC, 0x00, 0x1F, 0xF0, 0x00, + 0x6F, 0xC0, 0x03, 0xFF, 0x00, 0x06, 0xFC, 0x00, 0x37, 0xF0, 0x00, 0x6F, + 0xE0, 0x06, 0x7E, 0x00, 0x04, 0xFE, 0x00, 0xEF, 0xE0, 0x00, 0xCF, 0xE0, + 0x0C, 0xFE, 0x00, 0x0C, 0xFE, 0x01, 0x8F, 0xE0, 0x00, 0xCF, 0xE0, 0x38, + 0xFC, 0x00, 0x18, 0x7E, 0x03, 0x1F, 0xC0, 0x01, 0x87, 0xE0, 0x61, 0xFC, + 0x00, 0x18, 0x7E, 0x0E, 0x1F, 0xC0, 0x01, 0x87, 0xE0, 0xC3, 0xF8, 0x00, + 0x30, 0x7F, 0x18, 0x3F, 0x80, 0x03, 0x07, 0xF3, 0x83, 0xF8, 0x00, 0x30, + 0x7F, 0x30, 0x3F, 0x00, 0x06, 0x07, 0xF7, 0x07, 0xF0, 0x00, 0x60, 0x3F, + 0xE0, 0x7F, 0x00, 0x06, 0x03, 0xFC, 0x07, 0xF0, 0x00, 0xE0, 0x3F, 0xC0, + 0x7E, 0x00, 0x0C, 0x03, 0xF8, 0x0F, 0xE0, 0x00, 0xC0, 0x3F, 0x00, 0xFE, + 0x00, 0x0C, 0x03, 0xF0, 0x0F, 0xE0, 0x01, 0xC0, 0x3E, 0x01, 0xFC, 0x00, + 0x1C, 0x03, 0xC0, 0x1F, 0xC0, 0x07, 0xE0, 0x3C, 0x03, 0xFE, 0x00, 0xFF, + 0xC1, 0x81, 0xFF, 0xFC, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0x00, 0x1F, 0xF8, 0x03, 0xF8, 0x00, 0x3F, 0x00, 0x0F, 0xE0, 0x00, 0xF0, + 0x00, 0x7F, 0x00, 0x07, 0x00, 0x03, 0xFC, 0x00, 0x38, 0x00, 0x1F, 0xE0, + 0x01, 0x80, 0x01, 0xBF, 0x80, 0x0C, 0x00, 0x0D, 0xFC, 0x00, 0x60, 0x00, + 0x67, 0xF0, 0x07, 0x00, 0x02, 0x3F, 0x80, 0x30, 0x00, 0x30, 0xFE, 0x01, + 0x80, 0x01, 0x87, 0xF0, 0x0C, 0x00, 0x0C, 0x1F, 0xC0, 0xC0, 0x00, 0xC0, + 0xFE, 0x06, 0x00, 0x06, 0x07, 0xF8, 0x30, 0x00, 0x30, 0x1F, 0xC1, 0x80, + 0x01, 0x80, 0xFF, 0x18, 0x00, 0x18, 0x03, 0xF8, 0xC0, 0x00, 0xC0, 0x1F, + 0xC6, 0x00, 0x06, 0x00, 0x7F, 0x60, 0x00, 0x60, 0x03, 0xFB, 0x00, 0x03, + 0x00, 0x0F, 0xF8, 0x00, 0x18, 0x00, 0x7F, 0xC0, 0x01, 0xC0, 0x01, 0xFC, + 0x00, 0x0C, 0x00, 0x0F, 0xE0, 0x00, 0x60, 0x00, 0x3F, 0x00, 0x03, 0x00, + 0x01, 0xF0, 0x00, 0x38, 0x00, 0x07, 0x80, 0x01, 0xC0, 0x00, 0x3C, 0x00, + 0x3F, 0x00, 0x01, 0xE0, 0x03, 0xFF, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x7E, + 0x1F, 0x80, 0x01, 0xF0, 0x0F, 0x80, 0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x00, + 0x1F, 0x80, 0xFE, 0x00, 0x3F, 0x03, 0xF8, 0x00, 0x7E, 0x07, 0xF0, 0x00, + 0xFE, 0x1F, 0xC0, 0x01, 0xFC, 0x7F, 0x80, 0x03, 0xF8, 0xFE, 0x00, 0x07, + 0xF3, 0xFC, 0x00, 0x1F, 0xE7, 0xF0, 0x00, 0x3F, 0xDF, 0xE0, 0x00, 0x7F, + 0xBF, 0xC0, 0x00, 0xFE, 0x7F, 0x80, 0x03, 0xFC, 0xFE, 0x00, 0x07, 0xFB, + 0xFC, 0x00, 0x0F, 0xF7, 0xF8, 0x00, 0x3F, 0xCF, 0xF0, 0x00, 0x7F, 0x9F, + 0xC0, 0x00, 0xFE, 0x3F, 0x80, 0x03, 0xFC, 0x7F, 0x00, 0x07, 0xF0, 0xFE, + 0x00, 0x1F, 0xC0, 0xFC, 0x00, 0x3F, 0x81, 0xF8, 0x00, 0xFE, 0x03, 0xF0, + 0x03, 0xF8, 0x03, 0xF0, 0x07, 0xE0, 0x03, 0xE0, 0x1F, 0x00, 0x03, 0xE0, + 0xFC, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0xFE, 0x1F, 0xE0, 0x01, 0xFC, + 0x1F, 0xE0, 0x03, 0xF0, 0x1F, 0xC0, 0x0F, 0xE0, 0x3F, 0xC0, 0x1F, 0xC0, + 0x7F, 0x80, 0x3F, 0x80, 0xFF, 0x00, 0x7E, 0x01, 0xFE, 0x01, 0xFC, 0x03, + 0xFC, 0x03, 0xF8, 0x0F, 0xF8, 0x07, 0xF0, 0x1F, 0xE0, 0x0F, 0xC0, 0x7F, + 0x80, 0x3F, 0x81, 0xFE, 0x00, 0x7F, 0x07, 0xF8, 0x00, 0xFF, 0xFF, 0xC0, + 0x03, 0xFF, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, 0xFC, + 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x7E, 0x1F, 0x80, 0x01, + 0xF0, 0x0F, 0x80, 0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x80, 0x1F, 0x80, 0xFE, + 0x00, 0x3F, 0x03, 0xF8, 0x00, 0x7E, 0x07, 0xF0, 0x00, 0xFE, 0x1F, 0xC0, + 0x01, 0xFC, 0x7F, 0x80, 0x03, 0xF8, 0xFE, 0x00, 0x07, 0xF3, 0xFC, 0x00, + 0x1F, 0xE7, 0xF8, 0x00, 0x3F, 0xDF, 0xE0, 0x00, 0x7F, 0xBF, 0xC0, 0x00, + 0xFF, 0x7F, 0x80, 0x01, 0xFC, 0xFE, 0x00, 0x07, 0xFB, 0xFC, 0x00, 0x0F, + 0xF7, 0xF8, 0x00, 0x1F, 0xCF, 0xF0, 0x00, 0x7F, 0x9F, 0xC0, 0x00, 0xFE, + 0x3F, 0x80, 0x01, 0xFC, 0x7F, 0x00, 0x07, 0xF0, 0xFE, 0x00, 0x0F, 0xE1, + 0xFC, 0x00, 0x3F, 0x81, 0xF8, 0x00, 0x7E, 0x03, 0xF0, 0x01, 0xF8, 0x03, + 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x1F, 0x80, 0x03, 0xE0, 0x7E, 0x00, 0x03, + 0xF3, 0xF0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0xC0, 0x7F, 0xE0, 0x03, 0x03, 0xFF, + 0xF8, 0x1C, 0x0F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xE0, 0x3F, + 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, + 0xFF, 0xF8, 0x00, 0x3F, 0xC3, 0xFC, 0x00, 0x3F, 0x81, 0xFE, 0x00, 0x3F, + 0x80, 0xFF, 0x00, 0x7F, 0x80, 0xFF, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0x7F, + 0x00, 0xFF, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFE, 0x00, 0xFE, + 0x01, 0xFE, 0x00, 0xFE, 0x03, 0xFC, 0x00, 0xFE, 0x07, 0xF8, 0x01, 0xFC, + 0x1F, 0xF0, 0x01, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x03, 0xFD, + 0xFE, 0x00, 0x03, 0xF8, 0xFF, 0x00, 0x03, 0xF8, 0xFF, 0x00, 0x03, 0xF8, + 0xFF, 0x00, 0x07, 0xF8, 0x7F, 0x80, 0x07, 0xF0, 0x7F, 0x80, 0x07, 0xF0, + 0x3F, 0x80, 0x07, 0xF0, 0x3F, 0xC0, 0x0F, 0xE0, 0x3F, 0xC0, 0x0F, 0xE0, + 0x1F, 0xC0, 0x0F, 0xE0, 0x1F, 0xE0, 0x1F, 0xE0, 0x1F, 0xE0, 0x1F, 0xE0, + 0x0F, 0xF0, 0x3F, 0xF0, 0x0F, 0xF8, 0xFF, 0xFC, 0x0F, 0xFE, 0x00, 0x1F, + 0x83, 0x00, 0x7F, 0xF7, 0x00, 0xF8, 0x7E, 0x01, 0xE0, 0x1E, 0x03, 0xC0, + 0x0E, 0x03, 0xC0, 0x0E, 0x07, 0xC0, 0x0E, 0x07, 0xC0, 0x04, 0x07, 0xC0, + 0x04, 0x07, 0xE0, 0x04, 0x07, 0xF0, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xFC, + 0x00, 0x03, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x7F, + 0xE0, 0x00, 0x3F, 0xE0, 0x00, 0x1F, 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x07, + 0xF8, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xF8, 0x20, 0x00, 0xF8, 0x20, 0x00, + 0xF8, 0x20, 0x00, 0xF8, 0x70, 0x00, 0xF8, 0x70, 0x00, 0xF0, 0x78, 0x01, + 0xF0, 0x78, 0x03, 0xE0, 0x7E, 0x07, 0xC0, 0x47, 0xFF, 0x80, 0xC0, 0xFC, + 0x00, 0x3F, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFC, 0xFE, 0x3F, 0x8F, 0x9E, + 0x07, 0xF0, 0xF3, 0x81, 0xFC, 0x0E, 0x60, 0x3F, 0x81, 0x98, 0x07, 0xF0, + 0x13, 0x00, 0xFC, 0x02, 0x00, 0x3F, 0x80, 0x40, 0x07, 0xF0, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, + 0x00, 0x1F, 0x80, 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, + 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, + 0x03, 0xFF, 0x0F, 0xFC, 0x00, 0xFC, 0x07, 0xF0, 0x00, 0x38, 0x07, 0xF0, + 0x00, 0x38, 0x07, 0xF0, 0x00, 0x30, 0x0F, 0xE0, 0x00, 0x30, 0x0F, 0xE0, + 0x00, 0x70, 0x0F, 0xE0, 0x00, 0x60, 0x0F, 0xE0, 0x00, 0x60, 0x1F, 0xC0, + 0x00, 0xE0, 0x1F, 0xC0, 0x00, 0xC0, 0x1F, 0xC0, 0x00, 0xC0, 0x3F, 0x80, + 0x00, 0xC0, 0x3F, 0x80, 0x01, 0x80, 0x3F, 0x80, 0x01, 0x80, 0x3F, 0x80, + 0x01, 0x80, 0x7F, 0x00, 0x01, 0x80, 0x7F, 0x00, 0x03, 0x00, 0x7F, 0x00, + 0x03, 0x00, 0x7E, 0x00, 0x03, 0x00, 0xFE, 0x00, 0x06, 0x00, 0xFE, 0x00, + 0x06, 0x00, 0xFC, 0x00, 0x06, 0x00, 0xFC, 0x00, 0x0E, 0x00, 0xFC, 0x00, + 0x0C, 0x00, 0xFC, 0x00, 0x1C, 0x00, 0xFC, 0x00, 0x18, 0x00, 0x7E, 0x00, + 0x38, 0x00, 0x7E, 0x00, 0x70, 0x00, 0x3F, 0x81, 0xE0, 0x00, 0x0F, 0xFF, + 0x80, 0x00, 0x03, 0xFE, 0x00, 0x00, 0xFF, 0xFC, 0x03, 0xFE, 0x7F, 0xE0, + 0x01, 0xF8, 0x7F, 0x80, 0x01, 0xC0, 0xFF, 0x00, 0x03, 0x80, 0xFE, 0x00, + 0x0E, 0x01, 0xFC, 0x00, 0x18, 0x03, 0xF8, 0x00, 0x70, 0x07, 0xF0, 0x00, + 0xC0, 0x0F, 0xF0, 0x03, 0x80, 0x1F, 0xE0, 0x0E, 0x00, 0x1F, 0xC0, 0x18, + 0x00, 0x3F, 0x80, 0x70, 0x00, 0x7F, 0x00, 0xC0, 0x00, 0xFE, 0x03, 0x00, + 0x01, 0xFC, 0x0E, 0x00, 0x03, 0xF8, 0x18, 0x00, 0x07, 0xF8, 0x60, 0x00, + 0x07, 0xF1, 0xC0, 0x00, 0x0F, 0xE3, 0x00, 0x00, 0x1F, 0xCC, 0x00, 0x00, + 0x3F, 0xB8, 0x00, 0x00, 0x7F, 0x60, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xFF, 0xF8, + 0xFF, 0xF0, 0xFF, 0x9F, 0xF8, 0x1F, 0xE0, 0x0F, 0x87, 0xF8, 0x07, 0xE0, + 0x07, 0x03, 0xF8, 0x03, 0xF0, 0x03, 0x80, 0xFE, 0x01, 0xF8, 0x01, 0x80, + 0x7F, 0x00, 0xFC, 0x00, 0xC0, 0x3F, 0x80, 0x7F, 0x00, 0xC0, 0x1F, 0xC0, + 0x7F, 0x80, 0x60, 0x0F, 0xE0, 0x3F, 0xC0, 0x60, 0x07, 0xF0, 0x37, 0xE0, + 0x30, 0x03, 0xF8, 0x1B, 0xF0, 0x30, 0x00, 0xFC, 0x19, 0xF8, 0x18, 0x00, + 0x7E, 0x0C, 0xFE, 0x18, 0x00, 0x3F, 0x84, 0x7F, 0x0C, 0x00, 0x1F, 0xC6, + 0x3F, 0x8C, 0x00, 0x0F, 0xE2, 0x1F, 0xC6, 0x00, 0x07, 0xF3, 0x07, 0xE6, + 0x00, 0x03, 0xF9, 0x83, 0xF3, 0x00, 0x01, 0xFD, 0x81, 0xFB, 0x00, 0x00, + 0x7E, 0xC0, 0xFD, 0x80, 0x00, 0x3F, 0xC0, 0x7F, 0x80, 0x00, 0x1F, 0xE0, + 0x3F, 0xC0, 0x00, 0x0F, 0xE0, 0x1F, 0xC0, 0x00, 0x07, 0xF0, 0x0F, 0xE0, + 0x00, 0x03, 0xF0, 0x07, 0xE0, 0x00, 0x01, 0xF8, 0x01, 0xF0, 0x00, 0x00, + 0x78, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x00, 0x1C, 0x00, + 0x38, 0x00, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x00, 0x06, 0x00, 0x0C, 0x00, + 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x03, 0xFF, 0xF0, 0xFF, 0xC0, 0x3F, + 0xE0, 0x0F, 0xC0, 0x03, 0xF8, 0x01, 0xE0, 0x00, 0xFE, 0x00, 0xE0, 0x00, + 0x3F, 0x80, 0x70, 0x00, 0x07, 0xE0, 0x18, 0x00, 0x01, 0xFC, 0x0C, 0x00, + 0x00, 0x7F, 0x06, 0x00, 0x00, 0x0F, 0xC3, 0x00, 0x00, 0x03, 0xF9, 0x80, + 0x00, 0x00, 0xFE, 0xC0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x07, 0xF8, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x0F, + 0xC0, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x77, 0xF0, 0x00, 0x00, 0x39, 0xFC, 0x00, 0x00, + 0x1C, 0x3F, 0x00, 0x00, 0x06, 0x0F, 0xE0, 0x00, 0x03, 0x03, 0xF8, 0x00, + 0x01, 0x80, 0x7E, 0x00, 0x00, 0xE0, 0x1F, 0xC0, 0x00, 0x70, 0x07, 0xF0, + 0x00, 0x38, 0x01, 0xFC, 0x00, 0x1E, 0x00, 0x7F, 0x80, 0x1F, 0xC0, 0x1F, + 0xF0, 0x0F, 0xFC, 0x3F, 0xFF, 0x80, 0xFF, 0xF8, 0x3F, 0xF3, 0xFC, 0x00, + 0xFC, 0x1F, 0xC0, 0x07, 0x81, 0xFC, 0x00, 0x70, 0x0F, 0xC0, 0x0E, 0x00, + 0xFE, 0x00, 0xC0, 0x0F, 0xE0, 0x1C, 0x00, 0x7E, 0x03, 0x80, 0x07, 0xF0, + 0x30, 0x00, 0x7F, 0x06, 0x00, 0x03, 0xF0, 0xE0, 0x00, 0x3F, 0x8C, 0x00, + 0x03, 0xF9, 0x80, 0x00, 0x1F, 0xB0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x1F, + 0xE0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x1F, 0x80, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x07, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x01, + 0xFF, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF0, 0x3F, + 0xFF, 0xFF, 0x03, 0xF8, 0x0F, 0xF0, 0x7C, 0x01, 0xFE, 0x07, 0x80, 0x3F, + 0xC0, 0x70, 0x03, 0xF8, 0x06, 0x00, 0x7F, 0x80, 0xC0, 0x0F, 0xF0, 0x08, + 0x01, 0xFE, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x7F, + 0x80, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, 0xE0, 0x00, + 0x03, 0xFC, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x07, 0xF8, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0x00, + 0x07, 0xF8, 0x00, 0xC0, 0xFF, 0x00, 0x0C, 0x1F, 0xE0, 0x01, 0x81, 0xFE, + 0x00, 0x38, 0x3F, 0xC0, 0x07, 0x87, 0xF8, 0x01, 0xF0, 0xFF, 0x00, 0xFF, + 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xE0, 0x0F, + 0xFC, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, + 0x00, 0x07, 0x80, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF0, + 0x00, 0x1E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1E, 0x00, 0x03, 0xC0, + 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x1F, 0x00, + 0x03, 0xE0, 0x00, 0x78, 0x00, 0x0F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, + 0x0F, 0x00, 0x01, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xE0, 0x00, + 0x3C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xFE, 0x01, + 0xFF, 0xC0, 0x00, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x1C, + 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, + 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x07, 0x00, 0x3C, 0x01, 0xE0, 0x0F, 0x00, + 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x01, + 0xC0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x0F, 0x00, 0x78, 0x00, 0x7F, 0xE0, + 0x0F, 0xFC, 0x00, 0x0F, 0x80, 0x01, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, + 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, + 0x07, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF0, 0x00, + 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1E, 0x00, 0x07, 0xC0, 0x00, + 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, + 0xE0, 0x00, 0x78, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, + 0x00, 0x01, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xE0, 0x07, 0xFC, + 0x01, 0xFF, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0x00, 0x7F, 0x00, + 0x03, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0xEF, 0x00, 0x1E, 0x78, 0x00, 0xF1, + 0xE0, 0x0F, 0x0F, 0x00, 0x78, 0x3C, 0x07, 0xC1, 0xE0, 0x3C, 0x07, 0x83, + 0xE0, 0x3C, 0x1E, 0x00, 0xF1, 0xF0, 0x07, 0x8F, 0x00, 0x1E, 0xF8, 0x00, + 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x70, 0x3E, + 0x0F, 0x83, 0xF0, 0x3E, 0x07, 0x80, 0xF0, 0x0E, 0x01, 0xC0, 0x00, 0x3C, + 0x0C, 0x03, 0xF9, 0xF0, 0x1F, 0x3F, 0x80, 0xF8, 0x7E, 0x07, 0xC1, 0xF8, + 0x3F, 0x07, 0xC0, 0xF8, 0x1F, 0x07, 0xE0, 0x7C, 0x3F, 0x01, 0xF0, 0xFC, + 0x0F, 0x87, 0xE0, 0x3E, 0x1F, 0x80, 0xF8, 0x7E, 0x03, 0xC3, 0xF8, 0x1F, + 0x0F, 0xC0, 0x7C, 0x3F, 0x03, 0xF0, 0xFC, 0x0F, 0x83, 0xF0, 0x7E, 0x3F, + 0xC2, 0xF8, 0xBF, 0x9B, 0xE4, 0x7F, 0xCF, 0xE0, 0xFE, 0x3F, 0x01, 0xE0, + 0x78, 0x00, 0x00, 0x7C, 0x00, 0x3F, 0xF0, 0x00, 0x1F, 0x80, 0x00, 0x7E, + 0x00, 0x01, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, + 0x03, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x3E, 0x3E, 0x01, 0xF9, 0xFC, 0x07, + 0xEF, 0xF8, 0x1F, 0x47, 0xF0, 0x7E, 0x0F, 0xC3, 0xF8, 0x3F, 0x0F, 0xC0, + 0xFC, 0x3F, 0x03, 0xF1, 0xF8, 0x0F, 0xC7, 0xE0, 0x3F, 0x1F, 0x01, 0xF8, + 0x7C, 0x07, 0xE3, 0xF0, 0x1F, 0x8F, 0xC0, 0xFC, 0x3E, 0x03, 0xF1, 0xF8, + 0x0F, 0x87, 0xE0, 0x7C, 0x1F, 0x03, 0xE0, 0xFC, 0x0F, 0x03, 0xF0, 0x78, + 0x0F, 0xC7, 0xC0, 0x1F, 0xFE, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, + 0x3F, 0xE0, 0x1E, 0x3C, 0x0F, 0x0F, 0x07, 0x87, 0xC3, 0xE1, 0xF1, 0xF0, + 0x38, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x80, 0x07, 0xE0, 0x01, 0xF8, 0x00, + 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0xC0, 0x03, 0xF0, 0x00, 0xFC, 0x03, 0x3F, + 0x00, 0xCF, 0xE0, 0x61, 0xFC, 0x70, 0x3F, 0xF8, 0x07, 0xFC, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x7F, 0xE0, 0x00, + 0x07, 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x80, + 0x07, 0x9F, 0x80, 0x0F, 0xFF, 0xC0, 0x0F, 0x9F, 0xE0, 0x0F, 0x87, 0xF0, + 0x0F, 0x83, 0xF0, 0x0F, 0xC1, 0xF8, 0x07, 0xC0, 0xFC, 0x07, 0xE0, 0x7C, + 0x07, 0xE0, 0x7E, 0x03, 0xF0, 0x3F, 0x03, 0xF0, 0x1F, 0x81, 0xF8, 0x0F, + 0x80, 0xFC, 0x0F, 0xC0, 0xFE, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x3F, 0x03, + 0xF0, 0x1F, 0x83, 0xF8, 0x0F, 0xC1, 0xF8, 0xC7, 0xE1, 0xFC, 0xC3, 0xF9, + 0xBE, 0xC0, 0xFF, 0x9F, 0xC0, 0x7F, 0x8F, 0xC0, 0x0F, 0x83, 0xC0, 0x00, + 0x00, 0x3F, 0x00, 0x3F, 0xE0, 0x1E, 0x3C, 0x0F, 0x0F, 0x07, 0x83, 0xC3, + 0xE0, 0xF1, 0xF0, 0x3C, 0xFC, 0x1E, 0x3F, 0x0F, 0x9F, 0x83, 0xC7, 0xE3, + 0xE1, 0xFB, 0xE0, 0xFF, 0xE0, 0x3F, 0xC0, 0x0F, 0xC0, 0x03, 0xF0, 0x00, + 0xFC, 0x03, 0x3F, 0x01, 0x8F, 0xC0, 0xC1, 0xF8, 0x70, 0x7F, 0xF8, 0x07, + 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x03, 0xCE, 0x00, + 0x00, 0x78, 0xF0, 0x00, 0x0F, 0x8F, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x1F, + 0x06, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x3F, + 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x01, + 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x03, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xC0, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x06, 0x1F, 0x00, 0x00, 0xF1, 0xE0, + 0x00, 0x0F, 0x3E, 0x00, 0x00, 0xF3, 0xC0, 0x00, 0x07, 0xF8, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x7F, 0xF0, 0x00, 0x7E, + 0x3F, 0xE0, 0x7C, 0x0F, 0xF0, 0x7E, 0x07, 0xC0, 0x7E, 0x03, 0xE0, 0x3F, + 0x01, 0xF0, 0x1F, 0x01, 0xF8, 0x0F, 0x80, 0xFC, 0x07, 0xC0, 0xFC, 0x01, + 0xE0, 0xFC, 0x00, 0x78, 0xFC, 0x00, 0x1F, 0xFC, 0x00, 0x0F, 0xF0, 0x00, + 0x1C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xF8, 0x00, + 0x07, 0xFF, 0x80, 0x01, 0xFF, 0xF8, 0x00, 0x7F, 0xFE, 0x00, 0x77, 0xFF, + 0x80, 0xF0, 0x7F, 0xC0, 0xF0, 0x07, 0xE0, 0xF0, 0x01, 0xF0, 0x78, 0x00, + 0xF8, 0x3C, 0x00, 0x78, 0x1F, 0x00, 0x7C, 0x07, 0xC0, 0x78, 0x01, 0xFF, + 0xF8, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x04, 0x00, 0x01, 0xF8, 0x00, 0x1F, + 0xF0, 0x00, 0x07, 0xE0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0xFC, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x0F, 0xC0, + 0x00, 0x1F, 0x87, 0xC0, 0x3E, 0x1F, 0xC0, 0xFC, 0x7F, 0x81, 0xF9, 0x9F, + 0x03, 0xE6, 0x3E, 0x07, 0xD8, 0x7C, 0x1F, 0xA0, 0xF8, 0x3F, 0x83, 0xF0, + 0x7F, 0x07, 0xE0, 0xFC, 0x0F, 0xC3, 0xF8, 0x3F, 0x07, 0xE0, 0x7E, 0x0F, + 0xC0, 0xFC, 0x3F, 0x03, 0xF0, 0x7E, 0x07, 0xE0, 0xFC, 0x0F, 0xC1, 0xF0, + 0x3F, 0x17, 0xE0, 0x7E, 0x6F, 0xC0, 0xF9, 0x9F, 0x01, 0xF6, 0x3E, 0x03, + 0xF8, 0xFC, 0x07, 0xF1, 0xC0, 0x07, 0x80, 0x01, 0xE0, 0x3F, 0x03, 0xF0, + 0x3F, 0x03, 0xF0, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC7, + 0xFC, 0x1F, 0xC0, 0xF8, 0x0F, 0x81, 0xF8, 0x1F, 0x81, 0xF0, 0x1F, 0x03, + 0xF0, 0x3E, 0x03, 0xE0, 0x3E, 0x07, 0xE0, 0x7C, 0x07, 0xC0, 0xFC, 0x2F, + 0x84, 0xF8, 0xCF, 0x98, 0xFF, 0x0F, 0xE0, 0x78, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0xFC, 0x00, 0x1F, 0xF0, 0x00, 0x1F, 0xC0, + 0x00, 0x3E, 0x00, 0x01, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x80, 0x00, + 0x7C, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xF8, + 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x7E, 0x00, 0x01, 0xF0, 0x00, + 0x0F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xE0, 0x00, 0x1F, + 0x80, 0x00, 0x7E, 0x00, 0x01, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x3F, 0x00, + 0x60, 0xF8, 0x03, 0xC3, 0xC0, 0x0F, 0x1F, 0x00, 0x3C, 0xF8, 0x00, 0x7F, + 0xC0, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xFC, 0x00, 0x07, + 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x01, + 0xF8, 0x00, 0x01, 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x03, + 0xF0, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xE3, 0xFF, 0x03, 0xE0, 0xFC, 0x07, + 0xE0, 0xF0, 0x07, 0xE0, 0xE0, 0x07, 0xC1, 0xC0, 0x0F, 0xC3, 0x80, 0x0F, + 0xC7, 0x00, 0x0F, 0x8E, 0x00, 0x0F, 0xBE, 0x00, 0x1F, 0xFE, 0x00, 0x1F, + 0xFE, 0x00, 0x1F, 0xFE, 0x00, 0x1F, 0x3E, 0x00, 0x3F, 0x3F, 0x00, 0x3F, + 0x1F, 0x00, 0x3E, 0x1F, 0x00, 0x7E, 0x1F, 0x04, 0x7E, 0x1F, 0x8C, 0x7E, + 0x0F, 0x98, 0x7C, 0x0F, 0xF0, 0xFC, 0x07, 0xE0, 0xE0, 0x03, 0xC0, 0x00, + 0x08, 0x0F, 0xC7, 0xFE, 0x07, 0xF0, 0x3F, 0x01, 0xF8, 0x0F, 0xC0, 0x7C, + 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x0F, 0x80, 0x7C, 0x07, 0xE0, 0x3E, 0x01, + 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xC0, 0x3E, 0x03, 0xF0, 0x1F, 0x80, 0xF8, + 0x0F, 0xC0, 0x7E, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x8F, 0x8C, 0x7C, 0x43, + 0xE4, 0x1F, 0xE0, 0xFE, 0x03, 0xC0, 0x00, 0x00, 0x70, 0x78, 0x0F, 0x83, + 0xFE, 0x3F, 0x87, 0xF8, 0x1F, 0xCF, 0xF1, 0xFF, 0x03, 0xF1, 0x3E, 0x73, + 0xE0, 0x7E, 0x47, 0xD8, 0x7C, 0x0F, 0xD0, 0xFB, 0x1F, 0x81, 0xF4, 0x3E, + 0xC3, 0xF0, 0x3E, 0x87, 0xF0, 0x7C, 0x0F, 0xE0, 0xFE, 0x1F, 0x81, 0xF4, + 0x1F, 0x83, 0xF0, 0x3F, 0x07, 0xE0, 0x7C, 0x07, 0xE0, 0xFC, 0x1F, 0x81, + 0xF8, 0x1F, 0x83, 0xF0, 0x3F, 0x07, 0xE0, 0x7C, 0x07, 0xE0, 0xFC, 0x0F, + 0x80, 0xF8, 0x1F, 0x03, 0xF0, 0x3F, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0xFC, + 0x0F, 0x88, 0xF8, 0x1F, 0x81, 0xF3, 0x3F, 0x03, 0xE0, 0x3E, 0x47, 0xE0, + 0xFC, 0x07, 0xF0, 0xFC, 0x1F, 0x80, 0xFE, 0x18, 0x00, 0x00, 0x0F, 0x00, + 0x00, 0x70, 0xF8, 0x7F, 0xC3, 0xF8, 0x1F, 0x8F, 0xF0, 0x3F, 0x33, 0xE0, + 0x7C, 0x87, 0xC1, 0xF9, 0x0F, 0x83, 0xF4, 0x1F, 0x07, 0xD0, 0x3E, 0x0F, + 0xE0, 0xFC, 0x3F, 0x81, 0xF8, 0x7F, 0x03, 0xE0, 0xFC, 0x0F, 0xC1, 0xF8, + 0x1F, 0x87, 0xE0, 0x3E, 0x0F, 0xC0, 0xFC, 0x1F, 0x81, 0xF0, 0x3E, 0x03, + 0xE0, 0xFC, 0x0F, 0xC9, 0xF8, 0x1F, 0x33, 0xE0, 0x3E, 0x47, 0xC0, 0x7F, + 0x1F, 0x80, 0xFE, 0x38, 0x00, 0xF0, 0x00, 0x00, 0x3F, 0x00, 0x0E, 0x38, + 0x03, 0xC1, 0xC0, 0x78, 0x1E, 0x0F, 0x81, 0xF0, 0xF0, 0x1F, 0x1F, 0x01, + 0xF3, 0xE0, 0x1F, 0x3E, 0x03, 0xF7, 0xC0, 0x3F, 0x7C, 0x03, 0xF7, 0xC0, + 0x3E, 0xFC, 0x03, 0xEF, 0xC0, 0x7E, 0xF8, 0x07, 0xCF, 0x80, 0x7C, 0xF8, + 0x0F, 0x8F, 0x80, 0xF8, 0xF8, 0x1F, 0x07, 0x81, 0xE0, 0x78, 0x3C, 0x03, + 0xC7, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x0F, 0x1F, 0x00, 0x3F, 0xE7, 0xF8, + 0x01, 0xF9, 0xFF, 0x00, 0x1F, 0x47, 0xF0, 0x07, 0xF0, 0x7E, 0x00, 0xFE, + 0x0F, 0xC0, 0x1F, 0x81, 0xF8, 0x03, 0xF0, 0x3F, 0x00, 0xFC, 0x07, 0xE0, + 0x1F, 0x81, 0xFC, 0x03, 0xE0, 0x3F, 0x00, 0x7C, 0x07, 0xE0, 0x1F, 0x81, + 0xFC, 0x03, 0xF0, 0x3F, 0x00, 0x7C, 0x07, 0xE0, 0x0F, 0x81, 0xF8, 0x03, + 0xF0, 0x3E, 0x00, 0x7E, 0x0F, 0xC0, 0x0F, 0x81, 0xF0, 0x01, 0xF0, 0x7C, + 0x00, 0x7F, 0x1F, 0x00, 0x0F, 0xFF, 0xC0, 0x01, 0xF3, 0xE0, 0x00, 0x3E, + 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x3F, 0xFC, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0xF9, 0xF0, 0x1F, 0x1F, 0xC0, 0xF8, + 0x7E, 0x07, 0xC1, 0xF8, 0x3F, 0x07, 0xE0, 0xF8, 0x1F, 0x87, 0xE0, 0x7C, + 0x3F, 0x01, 0xF0, 0xFC, 0x0F, 0xC7, 0xE0, 0x3E, 0x1F, 0x80, 0xF8, 0x7E, + 0x07, 0xE3, 0xF0, 0x1F, 0x8F, 0xC0, 0x7C, 0x3F, 0x03, 0xF0, 0xFC, 0x0F, + 0xC3, 0xF0, 0x7E, 0x0F, 0xC3, 0xF8, 0x3F, 0x9B, 0xE0, 0x7F, 0xDF, 0x01, + 0xFE, 0x7C, 0x01, 0xF1, 0xF0, 0x00, 0x0F, 0xC0, 0x00, 0x3E, 0x00, 0x00, + 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x7C, 0x00, 0x03, 0xF8, + 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x71, 0xE1, 0xFF, 0x3E, 0x07, 0xE7, 0xF0, + 0x7E, 0xFF, 0x07, 0xE9, 0xE0, 0x7D, 0x0E, 0x07, 0xD0, 0x00, 0xFE, 0x00, + 0x0F, 0xE0, 0x00, 0xFC, 0x00, 0x0F, 0xC0, 0x01, 0xFC, 0x00, 0x1F, 0x80, + 0x01, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x03, 0xF0, + 0x00, 0x7E, 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x07, 0xC0, 0x00, 0x01, + 0xF1, 0x07, 0xFF, 0x0F, 0x0F, 0x0E, 0x07, 0x1E, 0x06, 0x1E, 0x06, 0x1F, + 0x02, 0x1F, 0x02, 0x1F, 0x80, 0x0F, 0xC0, 0x0F, 0xE0, 0x0F, 0xF0, 0x07, + 0xF8, 0x03, 0xF8, 0x01, 0xFC, 0x00, 0xFC, 0x40, 0x7C, 0x40, 0x7C, 0x60, + 0x3C, 0xE0, 0x38, 0xF0, 0x38, 0xF8, 0xF0, 0xDF, 0xC0, 0x00, 0x20, 0x03, + 0x00, 0x38, 0x03, 0x80, 0x3C, 0x03, 0xE0, 0x7F, 0x07, 0xFF, 0x3F, 0xF8, + 0x7C, 0x07, 0xE0, 0x3F, 0x01, 0xF0, 0x0F, 0x80, 0xFC, 0x07, 0xC0, 0x3E, + 0x03, 0xF0, 0x1F, 0x80, 0xF8, 0x07, 0xC0, 0x7E, 0x03, 0xF1, 0x1F, 0x08, + 0xF8, 0x87, 0xC8, 0x3F, 0xC1, 0xFC, 0x07, 0x80, 0x00, 0x00, 0x40, 0x00, + 0x1F, 0x03, 0xF7, 0xF8, 0x0F, 0x87, 0xE0, 0x3E, 0x1F, 0x81, 0xF8, 0x7E, + 0x07, 0xC1, 0xF0, 0x1F, 0x07, 0xC0, 0xFC, 0x3F, 0x03, 0xE0, 0xF8, 0x0F, + 0x83, 0xE0, 0x7E, 0x0F, 0x81, 0xF8, 0x7E, 0x0F, 0xC1, 0xF0, 0x3F, 0x07, + 0xC1, 0xFC, 0x1F, 0x07, 0xE0, 0xF8, 0x2F, 0x83, 0xE1, 0x3C, 0x6F, 0x8D, + 0xF1, 0x3E, 0x67, 0xC8, 0xFF, 0x1F, 0xE3, 0xF8, 0x7F, 0x07, 0xC0, 0xF0, + 0x00, 0x06, 0x07, 0x1F, 0x07, 0xBF, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF9, + 0xF8, 0x3C, 0x7C, 0x0C, 0x3E, 0x06, 0x1F, 0x03, 0x0F, 0x83, 0x07, 0xC1, + 0x83, 0xE1, 0x81, 0xF1, 0x80, 0xF9, 0x80, 0x7C, 0xC0, 0x3E, 0xC0, 0x1F, + 0xC0, 0x0F, 0xC0, 0x07, 0xC0, 0x03, 0xC0, 0x01, 0xC0, 0x00, 0xC0, 0x00, + 0x40, 0x00, 0x06, 0x01, 0x81, 0xC7, 0xC0, 0x30, 0x7F, 0xF8, 0x0E, 0x0F, + 0x9F, 0x01, 0xC1, 0xF3, 0xE0, 0x78, 0x3E, 0x7C, 0x1F, 0x03, 0xCF, 0xC3, + 0xE0, 0x30, 0xF8, 0xFC, 0x06, 0x1F, 0x1F, 0xC0, 0x83, 0xE7, 0xF8, 0x30, + 0x7C, 0xFF, 0x04, 0x0F, 0xB7, 0xE1, 0x81, 0xF6, 0xFC, 0x60, 0x3F, 0x8F, + 0x98, 0x07, 0xE1, 0xF3, 0x00, 0xFC, 0x3E, 0xC0, 0x1F, 0x07, 0xF0, 0x03, + 0xE0, 0xFC, 0x00, 0x78, 0x1F, 0x80, 0x0F, 0x03, 0xE0, 0x01, 0xC0, 0x78, + 0x00, 0x30, 0x0E, 0x00, 0x06, 0x01, 0x80, 0x00, 0x00, 0xF0, 0x1E, 0x0F, + 0xF0, 0x3E, 0x01, 0xF8, 0x7F, 0x01, 0xF8, 0xFF, 0x00, 0xF9, 0x8E, 0x00, + 0xFB, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xBF, 0x00, 0x01, + 0xBF, 0x08, 0x73, 0x1F, 0x18, 0xFF, 0x1F, 0x30, 0xFE, 0x1F, 0xE0, 0xFC, + 0x0F, 0xC0, 0x78, 0x07, 0x80, 0x00, 0x30, 0x1C, 0x0F, 0xF0, 0x7C, 0x07, + 0xE0, 0xF8, 0x0F, 0xC1, 0xF0, 0x0F, 0xC1, 0xE0, 0x1F, 0x81, 0xC0, 0x3F, + 0x03, 0x00, 0x3E, 0x06, 0x00, 0x7E, 0x08, 0x00, 0xFC, 0x30, 0x01, 0xF8, + 0x60, 0x01, 0xF1, 0x80, 0x03, 0xE3, 0x00, 0x07, 0xCC, 0x00, 0x0F, 0xD8, + 0x00, 0x1F, 0xE0, 0x00, 0x1F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0xF8, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x01, 0xC1, 0x80, 0x07, 0xE6, + 0x00, 0x0F, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, + 0xFF, 0xE1, 0xFF, 0xF8, 0x3F, 0xFF, 0x07, 0xFF, 0xC0, 0x80, 0x70, 0x30, + 0x1C, 0x04, 0x07, 0x00, 0x00, 0xC0, 0x00, 0x38, 0x00, 0x0E, 0x00, 0x03, + 0x80, 0x00, 0x60, 0x00, 0x18, 0x00, 0x06, 0x00, 0x01, 0xC0, 0x00, 0x30, + 0x00, 0x0C, 0x00, 0x03, 0xE0, 0x00, 0xFE, 0x00, 0x1F, 0xE0, 0xC7, 0xFC, + 0x3D, 0xCF, 0xC7, 0x90, 0xF8, 0xF0, 0x07, 0x9C, 0x00, 0x3E, 0x00, 0x00, + 0x01, 0xF0, 0x00, 0xFC, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, + 0x07, 0xC0, 0x00, 0x78, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, 0x80, + 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x03, 0xE0, + 0x00, 0x3E, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x0F, 0xC0, + 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, + 0x80, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x03, + 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, + 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7E, 0x00, + 0x03, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, + 0x3E, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xE0, + 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x07, 0xC0, 0x00, 0x7C, + 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, 0x0F, + 0x80, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x00, + 0xF8, 0x00, 0x03, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x03, 0xE0, 0x00, + 0x7C, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x00, 0xF8, 0x00, + 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, + 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x0F, 0x80, + 0x03, 0xF0, 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x03, 0xFF, 0x01, 0x3F, + 0xFE, 0x1D, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0x00, 0x1F, 0xF0, 0x00, 0x1F, + 0x00}; + +const GFXglyph FreeSerifBoldItalic24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 12, 0, 1}, // 0x20 ' ' + {0, 15, 33, 18, 3, -31}, // 0x21 '!' + {62, 19, 13, 26, 6, -31}, // 0x22 '"' + {93, 27, 33, 23, -2, -32}, // 0x23 '#' + {205, 24, 39, 24, -1, -33}, // 0x24 '$' + {322, 35, 32, 39, 2, -30}, // 0x25 '%' + {462, 33, 33, 37, 0, -31}, // 0x26 '&' + {599, 7, 13, 13, 6, -31}, // 0x27 ''' + {611, 14, 41, 16, 1, -31}, // 0x28 '(' + {683, 14, 41, 16, -2, -31}, // 0x29 ')' + {755, 19, 20, 23, 3, -31}, // 0x2A '*' + {803, 22, 23, 27, 2, -22}, // 0x2B '+' + {867, 10, 15, 12, -3, -5}, // 0x2C ',' + {886, 12, 5, 16, 0, -12}, // 0x2D '-' + {894, 8, 7, 12, 0, -5}, // 0x2E '.' + {901, 19, 33, 16, 0, -31}, // 0x2F '/' + {980, 22, 33, 23, 1, -31}, // 0x30 '0' + {1071, 20, 32, 23, 0, -31}, // 0x31 '1' + {1151, 22, 32, 23, 1, -31}, // 0x32 '2' + {1239, 22, 33, 24, 0, -31}, // 0x33 '3' + {1330, 25, 32, 23, 0, -31}, // 0x34 '4' + {1430, 24, 32, 24, 0, -30}, // 0x35 '5' + {1526, 23, 32, 24, 1, -30}, // 0x36 '6' + {1618, 23, 31, 23, 3, -30}, // 0x37 '7' + {1708, 21, 33, 23, 1, -31}, // 0x38 '8' + {1795, 23, 33, 23, 0, -31}, // 0x39 '9' + {1890, 13, 22, 12, 0, -20}, // 0x3A ':' + {1926, 15, 30, 12, -2, -20}, // 0x3B ';' + {1983, 24, 25, 27, 1, -23}, // 0x3C '<' + {2058, 24, 14, 27, 3, -18}, // 0x3D '=' + {2100, 24, 25, 27, 3, -23}, // 0x3E '>' + {2175, 18, 33, 24, 4, -31}, // 0x3F '?' + {2250, 33, 33, 39, 3, -31}, // 0x40 '@' + {2387, 31, 32, 33, 0, -31}, // 0x41 'A' + {2511, 31, 31, 30, 0, -30}, // 0x42 'B' + {2632, 29, 33, 29, 2, -31}, // 0x43 'C' + {2752, 35, 31, 34, 0, -30}, // 0x44 'D' + {2888, 32, 31, 30, 0, -30}, // 0x45 'E' + {3012, 31, 31, 29, 0, -30}, // 0x46 'F' + {3133, 32, 33, 33, 2, -31}, // 0x47 'G' + {3265, 39, 31, 35, 0, -30}, // 0x48 'H' + {3417, 21, 31, 18, 0, -30}, // 0x49 'I' + {3499, 27, 36, 23, 0, -30}, // 0x4A 'J' + {3621, 34, 31, 31, 0, -30}, // 0x4B 'K' + {3753, 29, 31, 29, 0, -30}, // 0x4C 'L' + {3866, 44, 32, 41, 0, -30}, // 0x4D 'M' + {4042, 37, 32, 33, 0, -30}, // 0x4E 'N' + {4190, 31, 33, 32, 2, -31}, // 0x4F 'O' + {4318, 31, 31, 28, 0, -30}, // 0x50 'P' + {4439, 31, 42, 32, 2, -31}, // 0x51 'Q' + {4602, 32, 31, 31, 0, -30}, // 0x52 'R' + {4726, 24, 33, 24, 0, -31}, // 0x53 'S' + {4825, 27, 31, 28, 4, -30}, // 0x54 'T' + {4930, 32, 32, 34, 5, -30}, // 0x55 'U' + {5058, 31, 32, 33, 6, -30}, // 0x56 'V' + {5182, 41, 32, 44, 6, -30}, // 0x57 'W' + {5346, 34, 31, 33, 0, -30}, // 0x58 'X' + {5478, 28, 31, 30, 6, -30}, // 0x59 'Y' + {5587, 28, 31, 26, 0, -30}, // 0x5A 'Z' + {5696, 19, 38, 16, -2, -30}, // 0x5B '[' + {5787, 13, 33, 19, 6, -31}, // 0x5C '\' + {5841, 19, 38, 16, -3, -30}, // 0x5D ']' + {5932, 21, 17, 27, 3, -30}, // 0x5E '^' + {5977, 24, 3, 23, 0, 5}, // 0x5F '_' + {5986, 10, 9, 16, 4, -32}, // 0x60 '`' + {5998, 22, 23, 24, 1, -21}, // 0x61 'a' + {6062, 22, 33, 23, 1, -31}, // 0x62 'b' + {6153, 18, 23, 20, 1, -21}, // 0x63 'c' + {6205, 25, 34, 24, 1, -32}, // 0x64 'd' + {6312, 18, 23, 20, 1, -21}, // 0x65 'e' + {6364, 28, 41, 23, -4, -31}, // 0x66 'f' + {6508, 25, 31, 23, -1, -21}, // 0x67 'g' + {6605, 23, 34, 26, 1, -32}, // 0x68 'h' + {6703, 12, 33, 14, 2, -31}, // 0x69 'i' + {6753, 22, 42, 16, -4, -31}, // 0x6A 'j' + {6869, 24, 34, 24, 1, -32}, // 0x6B 'k' + {6971, 13, 34, 14, 2, -32}, // 0x6C 'l' + {7027, 35, 23, 36, 0, -21}, // 0x6D 'm' + {7128, 23, 23, 25, 0, -21}, // 0x6E 'n' + {7195, 20, 23, 22, 1, -21}, // 0x6F 'o' + {7253, 27, 31, 23, -4, -21}, // 0x70 'p' + {7358, 22, 31, 23, 1, -21}, // 0x71 'q' + {7444, 20, 22, 19, 0, -21}, // 0x72 'r' + {7499, 16, 23, 17, 0, -21}, // 0x73 's' + {7545, 13, 29, 13, 2, -27}, // 0x74 't' + {7593, 22, 23, 25, 2, -21}, // 0x75 'u' + {7657, 17, 23, 21, 3, -21}, // 0x76 'v' + {7706, 27, 23, 31, 3, -21}, // 0x77 'w' + {7784, 24, 23, 22, -1, -21}, // 0x78 'x' + {7853, 23, 31, 20, -3, -21}, // 0x79 'y' + {7943, 19, 25, 19, 0, -20}, // 0x7A 'z' + {8003, 20, 41, 16, 0, -31}, // 0x7B '{' + {8106, 4, 33, 13, 5, -31}, // 0x7C '|' + {8123, 20, 41, 16, -6, -31}, // 0x7D '}' + {8226, 21, 7, 27, 3, -14}}; // 0x7E '~' + +const GFXfont FreeSerifBoldItalic24pt7b PROGMEM = { + (uint8_t *)FreeSerifBoldItalic24pt7bBitmaps, + (GFXglyph *)FreeSerifBoldItalic24pt7bGlyphs, 0x20, 0x7E, 56}; + +// Approx. 8917 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic9pt7b.h new file mode 100644 index 0000000..50a2ebe --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifBoldItalic9pt7b.h @@ -0,0 +1,214 @@ +const uint8_t FreeSerifBoldItalic9pt7bBitmaps[] PROGMEM = { + 0x0C, 0x31, 0xC6, 0x18, 0x41, 0x08, 0x20, 0x0E, 0x38, 0xE0, 0xCF, 0x38, + 0xA2, 0x88, 0x02, 0x40, 0xC8, 0x13, 0x06, 0x43, 0xFC, 0x32, 0x06, 0x40, + 0x98, 0x7F, 0x84, 0xC0, 0x90, 0x32, 0x04, 0xC0, 0x01, 0x01, 0xF0, 0x4B, + 0x99, 0x33, 0x24, 0x78, 0x07, 0x80, 0x38, 0x0B, 0x89, 0x31, 0x26, 0x64, + 0xC7, 0x30, 0x3C, 0x04, 0x00, 0x38, 0x41, 0x9F, 0x06, 0x48, 0x31, 0x60, + 0xCD, 0x03, 0x2C, 0x07, 0x27, 0x81, 0x39, 0x05, 0xC4, 0x26, 0x10, 0x98, + 0x84, 0x66, 0x10, 0xE0, 0x03, 0x80, 0x22, 0x03, 0x10, 0x19, 0x00, 0xF0, + 0x0F, 0x3C, 0xF8, 0xCC, 0xC4, 0xE7, 0x47, 0x3E, 0x38, 0xE1, 0xE7, 0x97, + 0xCF, 0x00, 0xFA, 0x80, 0x08, 0x88, 0x84, 0x62, 0x10, 0x84, 0x21, 0x08, + 0x41, 0x00, 0x20, 0x84, 0x10, 0x84, 0x21, 0x08, 0xC6, 0x23, 0x11, 0x00, + 0x18, 0x18, 0xD6, 0x38, 0x18, 0xF7, 0x18, 0x18, 0x08, 0x04, 0x02, 0x01, + 0x0F, 0xF8, 0x40, 0x20, 0x10, 0x08, 0x00, 0x6D, 0x95, 0x00, 0xFF, 0xC0, + 0xFF, 0x80, 0x06, 0x0C, 0x30, 0x60, 0x83, 0x04, 0x18, 0x20, 0xC1, 0x06, + 0x00, 0x0F, 0x0C, 0x8C, 0x6E, 0x37, 0x1B, 0x1F, 0x8F, 0xC7, 0xC7, 0x63, + 0xB1, 0x89, 0x83, 0x80, 0x06, 0x1E, 0x0E, 0x0E, 0x0C, 0x0C, 0x1C, 0x18, + 0x18, 0x18, 0x38, 0x38, 0xFC, 0x1F, 0x13, 0xD0, 0xE0, 0x70, 0x38, 0x38, + 0x18, 0x18, 0x18, 0x08, 0x08, 0x4F, 0xCF, 0xE0, 0x1F, 0x11, 0xC0, 0xE0, + 0x60, 0xC1, 0xF0, 0x38, 0x0C, 0x06, 0x03, 0x01, 0x19, 0x8F, 0x00, 0x00, + 0x80, 0xC0, 0xE1, 0xE0, 0xB0, 0x98, 0x9C, 0x8C, 0xFF, 0x07, 0x03, 0x01, + 0x80, 0x0F, 0x88, 0x08, 0x07, 0x83, 0xE0, 0x78, 0x1C, 0x06, 0x03, 0x01, + 0x80, 0x9C, 0x87, 0x80, 0x03, 0x87, 0x07, 0x07, 0x07, 0x03, 0xE3, 0x99, + 0xCC, 0xC6, 0x63, 0x33, 0x89, 0x87, 0x80, 0x3F, 0xBF, 0x90, 0x80, 0xC0, + 0x40, 0x60, 0x20, 0x30, 0x30, 0x10, 0x18, 0x08, 0x00, 0x1E, 0x13, 0x31, + 0x31, 0x3A, 0x1C, 0x1C, 0x6E, 0xC6, 0xC6, 0xC6, 0x44, 0x38, 0x0E, 0x1C, + 0x8C, 0x6C, 0x36, 0x3B, 0x1D, 0x8E, 0x7E, 0x0E, 0x07, 0x07, 0x0E, 0x0C, + 0x00, 0x39, 0xCE, 0x00, 0x03, 0x9C, 0xE0, 0x39, 0xCE, 0x00, 0x01, 0x8C, + 0x22, 0x20, 0x00, 0x01, 0xC3, 0xC7, 0x8E, 0x06, 0x01, 0xE0, 0x3C, 0x07, + 0x80, 0x40, 0xFF, 0x80, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x60, 0x1E, 0x03, + 0xC0, 0x78, 0x1C, 0x3C, 0x78, 0xF0, 0x40, 0x00, 0x1C, 0x27, 0x37, 0x07, + 0x0E, 0x1C, 0x30, 0x60, 0x40, 0x00, 0xE0, 0xE0, 0xE0, 0x0F, 0x80, 0xC3, + 0x08, 0x04, 0xC3, 0x3C, 0x24, 0xE2, 0x27, 0x33, 0x39, 0x11, 0xC9, 0x93, + 0x77, 0x18, 0x00, 0x70, 0x40, 0xFC, 0x00, 0x00, 0x80, 0x18, 0x01, 0x80, + 0x38, 0x05, 0x80, 0x5C, 0x09, 0xC1, 0x1C, 0x1F, 0xC2, 0x0C, 0x20, 0xC4, + 0x0E, 0xF3, 0xF0, 0x3F, 0xE0, 0xC7, 0x0C, 0x71, 0xC7, 0x1C, 0xE1, 0xF0, + 0x39, 0xC3, 0x8E, 0x38, 0xE3, 0x0E, 0x71, 0xE7, 0x1C, 0xFF, 0x00, 0x07, + 0xD1, 0xC7, 0x38, 0x27, 0x02, 0x70, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0xE0, + 0x0E, 0x00, 0x60, 0x87, 0x18, 0x1E, 0x00, 0x3F, 0xE0, 0x30, 0xE0, 0xC1, + 0x87, 0x07, 0x1C, 0x1C, 0x60, 0x73, 0x81, 0xCE, 0x07, 0x38, 0x38, 0xC0, + 0xE7, 0x07, 0x1C, 0x78, 0xFF, 0x80, 0x1F, 0xF8, 0x61, 0xC3, 0x04, 0x38, + 0x81, 0xCC, 0x0F, 0xE0, 0xE2, 0x07, 0x10, 0x38, 0x81, 0x81, 0x1C, 0x18, + 0xE3, 0x8F, 0xFC, 0x00, 0x3F, 0xF8, 0x61, 0xC3, 0x04, 0x38, 0x81, 0xCC, + 0x0F, 0xE0, 0xE2, 0x07, 0x10, 0x38, 0x81, 0x80, 0x1C, 0x00, 0xE0, 0x0F, + 0x80, 0x00, 0x07, 0x91, 0xC7, 0x38, 0x27, 0x00, 0x70, 0x0F, 0x00, 0xE1, + 0xFE, 0x0E, 0xE0, 0xCE, 0x0C, 0x60, 0xC7, 0x1C, 0x1F, 0x00, 0x1F, 0x7E, + 0x1C, 0x38, 0x30, 0x60, 0xE1, 0xC1, 0xC3, 0x83, 0x06, 0x0F, 0xFC, 0x1C, + 0x38, 0x38, 0x70, 0x60, 0xC1, 0xC3, 0x83, 0x87, 0x0F, 0x9F, 0x00, 0x3F, + 0x0C, 0x0C, 0x1C, 0x1C, 0x18, 0x38, 0x38, 0x38, 0x30, 0x70, 0x70, 0xF8, + 0x07, 0xC0, 0xE0, 0x38, 0x0C, 0x07, 0x01, 0xC0, 0x70, 0x18, 0x0E, 0x03, + 0x80, 0xC3, 0x30, 0xDC, 0x1E, 0x00, 0x1F, 0x78, 0x71, 0x83, 0x18, 0x39, + 0x81, 0xD0, 0x0D, 0x00, 0xFC, 0x07, 0x60, 0x3B, 0x81, 0x8C, 0x1C, 0x70, + 0xE1, 0x8F, 0xBE, 0x00, 0x1F, 0x00, 0xC0, 0x0C, 0x01, 0xC0, 0x1C, 0x01, + 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, 0x01, 0x70, 0x37, 0x0E, 0xFF, 0xE0, + 0x1E, 0x07, 0x87, 0x07, 0x83, 0x83, 0x82, 0xC3, 0xC1, 0x62, 0xE0, 0xB1, + 0x70, 0x99, 0x30, 0x4D, 0xB8, 0x27, 0x9C, 0x13, 0x8C, 0x11, 0xC6, 0x0C, + 0xC7, 0x0F, 0x47, 0xC0, 0x3C, 0x3C, 0x38, 0x20, 0xE0, 0x85, 0xC4, 0x13, + 0x10, 0x4E, 0x42, 0x3A, 0x08, 0x78, 0x21, 0xE0, 0x83, 0x84, 0x0C, 0x18, + 0x10, 0x00, 0x40, 0x07, 0xC1, 0xCE, 0x38, 0x73, 0x87, 0x70, 0x77, 0x07, + 0xF0, 0xFE, 0x0E, 0xE0, 0xEE, 0x1C, 0xE1, 0xC6, 0x38, 0x3E, 0x00, 0x3F, + 0xC0, 0xC7, 0x0C, 0x71, 0xC7, 0x1C, 0x71, 0x8E, 0x3F, 0xC3, 0x80, 0x30, + 0x03, 0x00, 0x70, 0x07, 0x00, 0xF8, 0x00, 0x07, 0xC0, 0xCE, 0x38, 0x73, + 0x87, 0x70, 0x77, 0x07, 0xF0, 0x7E, 0x0E, 0xE0, 0xEE, 0x0C, 0xE1, 0xC6, + 0x38, 0x36, 0x01, 0x80, 0x3C, 0x2D, 0xFC, 0x3F, 0xC0, 0xE7, 0x0C, 0x71, + 0xC7, 0x1C, 0x71, 0x8E, 0x3F, 0x83, 0xB8, 0x3B, 0x83, 0x3C, 0x71, 0xC7, + 0x1C, 0xF9, 0xF0, 0x0C, 0x89, 0x8C, 0x46, 0x23, 0x80, 0xE0, 0x78, 0x0E, + 0x03, 0x21, 0x90, 0xCC, 0xC9, 0xC0, 0x7F, 0xE9, 0xDF, 0x31, 0x4E, 0x21, + 0xC0, 0x38, 0x06, 0x01, 0xC0, 0x38, 0x06, 0x00, 0xC0, 0x38, 0x0F, 0xC0, + 0x7C, 0xF3, 0x82, 0x30, 0x27, 0x04, 0x70, 0x46, 0x04, 0xE0, 0x4E, 0x08, + 0xE0, 0x8E, 0x08, 0xE1, 0x0F, 0x30, 0x3C, 0x00, 0xFC, 0x73, 0x82, 0x38, + 0x23, 0x84, 0x38, 0x83, 0x90, 0x39, 0x01, 0xA0, 0x1C, 0x01, 0xC0, 0x18, + 0x01, 0x00, 0xF9, 0xF7, 0x30, 0xE2, 0x30, 0xC2, 0x38, 0xC4, 0x3B, 0xC4, + 0x3A, 0xE8, 0x3C, 0xE8, 0x3C, 0xF0, 0x18, 0xF0, 0x18, 0x60, 0x10, 0x60, + 0x10, 0x40, 0x3F, 0x78, 0x61, 0x83, 0x98, 0x1D, 0x00, 0x70, 0x03, 0x80, + 0x1C, 0x01, 0x60, 0x0B, 0x80, 0x9C, 0x08, 0x60, 0xC3, 0x8F, 0x7E, 0x00, + 0xF9, 0xE6, 0x18, 0xC2, 0x1C, 0x81, 0xA0, 0x34, 0x07, 0x00, 0xC0, 0x18, + 0x07, 0x00, 0xE0, 0x1C, 0x0F, 0xC0, 0x3F, 0xE6, 0x19, 0x87, 0x21, 0xC0, + 0x30, 0x0E, 0x03, 0x80, 0x60, 0x1C, 0x07, 0x05, 0xC1, 0x38, 0xEF, 0xFC, + 0x0E, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30, 0x30, 0x20, 0x60, 0x60, + 0x60, 0x40, 0xF0, 0xC6, 0x10, 0xC6, 0x10, 0x86, 0x30, 0x86, 0x30, 0x1E, + 0x0C, 0x18, 0x20, 0xC1, 0x83, 0x04, 0x18, 0x30, 0x60, 0x83, 0x06, 0x3C, + 0x00, 0x18, 0x1C, 0x34, 0x26, 0x66, 0x43, 0xC3, 0xFF, 0x80, 0xC6, 0x30, + 0x0D, 0x9D, 0x8C, 0xCC, 0x6E, 0x26, 0x33, 0x19, 0xBE, 0x66, 0x00, 0x00, + 0x78, 0x18, 0x30, 0x30, 0x3E, 0x73, 0x63, 0x63, 0x63, 0xC6, 0xC6, 0xCC, + 0x70, 0x0F, 0x3B, 0x70, 0x70, 0xE0, 0xE0, 0xE2, 0xE4, 0x78, 0x00, 0x00, + 0xF0, 0x1C, 0x06, 0x01, 0x83, 0xE3, 0x30, 0xCC, 0x63, 0x19, 0xCC, 0x63, + 0x38, 0xCF, 0x1D, 0x80, 0x0E, 0x75, 0xCB, 0xBE, 0xDE, 0x38, 0x72, 0x78, + 0x00, 0xE0, 0x34, 0x0C, 0x01, 0x80, 0x30, 0x1F, 0x01, 0x80, 0x30, 0x06, + 0x01, 0xC0, 0x30, 0x06, 0x00, 0xC0, 0x30, 0x06, 0x04, 0x80, 0xE0, 0x00, + 0x1C, 0x19, 0xD8, 0xCC, 0x66, 0x60, 0xE1, 0x80, 0xF0, 0x7E, 0x43, 0x21, + 0x8F, 0x00, 0x00, 0x1E, 0x07, 0x03, 0x01, 0x80, 0xD8, 0xFC, 0x76, 0x33, + 0x19, 0x99, 0xCC, 0xD6, 0x77, 0x30, 0x39, 0xC0, 0x0F, 0x31, 0x8C, 0xC6, + 0x31, 0xAE, 0x00, 0x03, 0x81, 0xC0, 0x00, 0x00, 0xE0, 0x30, 0x18, 0x18, + 0x0C, 0x06, 0x03, 0x03, 0x01, 0x80, 0xC2, 0xC1, 0xC0, 0x00, 0x0F, 0x00, + 0xC0, 0x60, 0x18, 0x06, 0xF3, 0x90, 0xC8, 0x34, 0x0F, 0x06, 0xC1, 0x98, + 0x66, 0xB9, 0xC0, 0x03, 0xCC, 0x63, 0x39, 0x8C, 0x66, 0x31, 0x8E, 0x70, + 0x7B, 0x99, 0xAF, 0xCE, 0x66, 0x63, 0x67, 0x33, 0x31, 0x99, 0x8C, 0xCC, + 0xE7, 0xC6, 0x30, 0x73, 0x7F, 0x73, 0x73, 0x63, 0x67, 0xE6, 0xC7, 0xC6, + 0x1E, 0x33, 0x63, 0x63, 0xC3, 0xC6, 0xC6, 0xCC, 0x78, 0x1D, 0xC3, 0xB1, + 0xCC, 0x63, 0x19, 0xCE, 0x63, 0x18, 0xCC, 0x3E, 0x1C, 0x06, 0x03, 0xE0, + 0x0D, 0x99, 0x8C, 0xCC, 0x6E, 0x76, 0x33, 0x19, 0x9C, 0x7C, 0x06, 0x07, + 0x07, 0xC0, 0x76, 0x3A, 0x30, 0x70, 0x60, 0x60, 0x60, 0xE0, 0x3D, 0x14, + 0x58, 0x38, 0x60, 0xA2, 0xF0, 0x08, 0xCC, 0xF6, 0x31, 0x98, 0xC6, 0x35, + 0xC0, 0xE3, 0x63, 0x66, 0x66, 0x66, 0xCC, 0xCC, 0xFE, 0xEC, 0xE6, 0xCD, + 0x8B, 0x26, 0x8E, 0x18, 0x20, 0xE4, 0xD9, 0x36, 0xE5, 0xDA, 0x77, 0x19, + 0xC6, 0x61, 0x10, 0x39, 0xC7, 0xB0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xE1, + 0x5A, 0x67, 0x00, 0x39, 0x8C, 0xC3, 0x21, 0xA0, 0xD0, 0x68, 0x38, 0x0C, + 0x04, 0x04, 0x14, 0x0C, 0x00, 0x3E, 0x46, 0x0C, 0x08, 0x10, 0x20, 0x70, + 0x1A, 0x0E, 0x03, 0x0E, 0x0C, 0x0C, 0x08, 0x18, 0x18, 0x10, 0x60, 0x30, + 0x30, 0x30, 0x60, 0x60, 0x60, 0x30, 0xFF, 0xF0, 0x0C, 0x06, 0x06, 0x06, + 0x04, 0x0C, 0x0C, 0x0C, 0x06, 0x18, 0x18, 0x18, 0x30, 0x30, 0x30, 0xE0, + 0x71, 0x8F}; + +const GFXglyph FreeSerifBoldItalic9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 6, 13, 7, 1, -11}, // 0x21 '!' + {10, 6, 5, 10, 3, -11}, // 0x22 '"' + {14, 11, 13, 9, -1, -12}, // 0x23 '#' + {32, 11, 15, 9, -1, -12}, // 0x24 '$' + {53, 14, 13, 15, 1, -11}, // 0x25 '%' + {76, 13, 13, 14, 0, -11}, // 0x26 '&' + {98, 2, 5, 5, 3, -11}, // 0x27 ''' + {100, 5, 16, 6, 1, -11}, // 0x28 '(' + {110, 5, 16, 6, -1, -11}, // 0x29 ')' + {120, 8, 8, 9, 1, -11}, // 0x2A '*' + {128, 9, 9, 10, 0, -8}, // 0x2B '+' + {139, 3, 6, 5, -1, -2}, // 0x2C ',' + {142, 5, 2, 6, 0, -4}, // 0x2D '-' + {144, 3, 3, 4, 0, -1}, // 0x2E '.' + {146, 7, 12, 6, 0, -11}, // 0x2F '/' + {157, 9, 13, 9, 0, -11}, // 0x30 '0' + {172, 8, 13, 9, 0, -11}, // 0x31 '1' + {185, 9, 13, 9, 0, -11}, // 0x32 '2' + {200, 9, 13, 9, 0, -11}, // 0x33 '3' + {215, 9, 12, 9, 0, -11}, // 0x34 '4' + {229, 9, 13, 9, 0, -11}, // 0x35 '5' + {244, 9, 13, 9, 1, -11}, // 0x36 '6' + {259, 9, 12, 9, 1, -11}, // 0x37 '7' + {273, 8, 13, 9, 0, -11}, // 0x38 '8' + {286, 9, 13, 9, 0, -11}, // 0x39 '9' + {301, 5, 9, 5, 0, -7}, // 0x3A ':' + {307, 5, 11, 5, 0, -7}, // 0x3B ';' + {314, 9, 10, 10, 1, -9}, // 0x3C '<' + {326, 9, 5, 10, 1, -6}, // 0x3D '=' + {332, 9, 10, 10, 1, -9}, // 0x3E '>' + {344, 8, 13, 9, 1, -11}, // 0x3F '?' + {357, 13, 13, 15, 1, -12}, // 0x40 '@' + {379, 12, 13, 13, 0, -11}, // 0x41 'A' + {399, 12, 13, 12, 0, -11}, // 0x42 'B' + {419, 12, 13, 11, 1, -11}, // 0x43 'C' + {439, 14, 13, 13, 0, -11}, // 0x44 'D' + {462, 13, 13, 11, 0, -11}, // 0x45 'E' + {484, 13, 13, 11, 0, -11}, // 0x46 'F' + {506, 12, 13, 13, 1, -11}, // 0x47 'G' + {526, 15, 13, 14, 0, -11}, // 0x48 'H' + {551, 8, 13, 7, 0, -11}, // 0x49 'I' + {564, 10, 14, 9, 0, -11}, // 0x4A 'J' + {582, 13, 13, 12, 0, -11}, // 0x4B 'K' + {604, 12, 13, 11, 0, -11}, // 0x4C 'L' + {624, 17, 13, 16, 0, -11}, // 0x4D 'M' + {652, 14, 13, 13, 0, -11}, // 0x4E 'N' + {675, 12, 13, 12, 1, -11}, // 0x4F 'O' + {695, 12, 13, 11, 0, -11}, // 0x50 'P' + {715, 12, 16, 12, 1, -11}, // 0x51 'Q' + {739, 12, 13, 12, 0, -11}, // 0x52 'R' + {759, 9, 13, 9, 0, -11}, // 0x53 'S' + {774, 11, 13, 11, 2, -11}, // 0x54 'T' + {792, 12, 13, 13, 2, -11}, // 0x55 'U' + {812, 12, 12, 13, 2, -11}, // 0x56 'V' + {830, 16, 12, 17, 2, -11}, // 0x57 'W' + {854, 13, 13, 13, 0, -11}, // 0x58 'X' + {876, 11, 13, 11, 2, -11}, // 0x59 'Y' + {894, 11, 13, 10, 0, -11}, // 0x5A 'Z' + {912, 8, 15, 6, -1, -11}, // 0x5B '[' + {927, 5, 12, 7, 2, -11}, // 0x5C '\' + {935, 7, 15, 6, -1, -11}, // 0x5D ']' + {949, 8, 7, 10, 1, -11}, // 0x5E '^' + {956, 9, 1, 9, 0, 3}, // 0x5F '_' + {958, 4, 3, 6, 2, -11}, // 0x60 '`' + {960, 9, 9, 9, 0, -7}, // 0x61 'a' + {971, 8, 14, 9, 0, -12}, // 0x62 'b' + {985, 8, 9, 8, 0, -7}, // 0x63 'c' + {994, 10, 14, 9, 0, -12}, // 0x64 'd' + {1012, 7, 9, 7, 0, -7}, // 0x65 'e' + {1020, 11, 17, 9, -2, -12}, // 0x66 'f' + {1044, 9, 12, 9, 0, -7}, // 0x67 'g' + {1058, 9, 14, 10, 0, -12}, // 0x68 'h' + {1074, 5, 13, 5, 1, -11}, // 0x69 'i' + {1083, 9, 16, 6, -1, -11}, // 0x6A 'j' + {1101, 10, 14, 9, 0, -12}, // 0x6B 'k' + {1119, 5, 14, 5, 1, -12}, // 0x6C 'l' + {1128, 13, 9, 14, 0, -7}, // 0x6D 'm' + {1143, 8, 9, 9, 0, -7}, // 0x6E 'n' + {1152, 8, 9, 9, 0, -7}, // 0x6F 'o' + {1161, 10, 12, 9, -2, -7}, // 0x70 'p' + {1176, 9, 12, 9, 0, -7}, // 0x71 'q' + {1190, 8, 8, 7, 0, -7}, // 0x72 'r' + {1198, 6, 9, 6, 0, -7}, // 0x73 's' + {1205, 5, 12, 5, 1, -10}, // 0x74 't' + {1213, 8, 9, 10, 1, -7}, // 0x75 'u' + {1222, 7, 8, 8, 1, -7}, // 0x76 'v' + {1229, 10, 8, 12, 1, -7}, // 0x77 'w' + {1239, 10, 9, 9, -1, -7}, // 0x78 'x' + {1251, 9, 12, 8, -1, -7}, // 0x79 'y' + {1265, 8, 9, 7, 0, -7}, // 0x7A 'z' + {1274, 8, 16, 6, 0, -12}, // 0x7B '{' + {1290, 1, 12, 5, 2, -11}, // 0x7C '|' + {1292, 8, 16, 6, -2, -12}, // 0x7D '}' + {1308, 8, 2, 10, 1, -4}}; // 0x7E '~' + +const GFXfont FreeSerifBoldItalic9pt7b PROGMEM = { + (uint8_t *)FreeSerifBoldItalic9pt7bBitmaps, + (GFXglyph *)FreeSerifBoldItalic9pt7bGlyphs, 0x20, 0x7E, 22}; + +// Approx. 1982 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic12pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic12pt7b.h new file mode 100644 index 0000000..967a52f --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic12pt7b.h @@ -0,0 +1,270 @@ +const uint8_t FreeSerifItalic12pt7bBitmaps[] PROGMEM = { + 0x0C, 0x31, 0xC6, 0x18, 0x43, 0x0C, 0x20, 0x84, 0x10, 0x03, 0x0C, 0x30, + 0x66, 0xCD, 0x12, 0x24, 0x51, 0x00, 0x03, 0x10, 0x11, 0x80, 0x8C, 0x0C, + 0x40, 0x46, 0x1F, 0xFC, 0x21, 0x01, 0x18, 0x18, 0x80, 0x84, 0x3F, 0xF8, + 0x62, 0x02, 0x30, 0x31, 0x01, 0x08, 0x08, 0xC0, 0x00, 0x40, 0x08, 0x07, + 0xC0, 0xCA, 0x18, 0xA1, 0x92, 0x19, 0x01, 0xD0, 0x0F, 0x00, 0x78, 0x03, + 0xC0, 0x2E, 0x02, 0x64, 0x46, 0x44, 0x64, 0x46, 0x64, 0xC1, 0xF0, 0x08, + 0x00, 0x80, 0x00, 0x08, 0x0F, 0x0C, 0x0C, 0x7C, 0x0C, 0x22, 0x06, 0x12, + 0x06, 0x09, 0x03, 0x09, 0x01, 0x84, 0x80, 0xC4, 0x8F, 0x3C, 0x4C, 0x40, + 0x4C, 0x20, 0x4E, 0x10, 0x26, 0x08, 0x23, 0x08, 0x11, 0x84, 0x10, 0xC4, + 0x08, 0x3C, 0x00, 0x00, 0xE0, 0x02, 0x60, 0x0C, 0xC0, 0x19, 0x80, 0x36, + 0x00, 0x70, 0x00, 0xC0, 0x07, 0x9F, 0x33, 0x08, 0xC3, 0x13, 0x06, 0x46, + 0x0D, 0x0C, 0x0C, 0x18, 0x1C, 0x1C, 0x5C, 0x9F, 0x1E, 0xFA, 0xA0, 0x02, + 0x08, 0x20, 0xC3, 0x06, 0x18, 0x30, 0xE1, 0x83, 0x06, 0x0C, 0x18, 0x30, + 0x60, 0x40, 0x80, 0x81, 0x00, 0x08, 0x10, 0x10, 0x20, 0x40, 0xC1, 0x83, + 0x06, 0x0C, 0x18, 0x70, 0xC1, 0x83, 0x0C, 0x10, 0x41, 0x04, 0x00, 0x18, + 0x18, 0x18, 0x93, 0x74, 0x38, 0xD7, 0x93, 0x18, 0x18, 0x04, 0x00, 0x80, + 0x10, 0x02, 0x00, 0x41, 0xFF, 0xC1, 0x00, 0x20, 0x04, 0x00, 0x80, 0x10, + 0x00, 0x6C, 0x95, 0x00, 0xF8, 0xFC, 0x00, 0x40, 0x18, 0x02, 0x00, 0xC0, + 0x30, 0x06, 0x01, 0x80, 0x20, 0x0C, 0x01, 0x00, 0x60, 0x18, 0x03, 0x00, + 0xC0, 0x10, 0x06, 0x00, 0x07, 0x81, 0x98, 0x61, 0x18, 0x33, 0x06, 0xC0, + 0xD8, 0x1B, 0x03, 0xE0, 0xF8, 0x1F, 0x03, 0x60, 0x6C, 0x19, 0x83, 0x10, + 0xC3, 0x30, 0x3C, 0x00, 0x01, 0x87, 0xC0, 0xC0, 0x60, 0x30, 0x18, 0x18, + 0x0C, 0x06, 0x07, 0x03, 0x01, 0x80, 0xC0, 0xC0, 0x60, 0x30, 0xFE, 0x00, + 0x0F, 0x0C, 0x64, 0x0C, 0x03, 0x00, 0xC0, 0x20, 0x18, 0x0C, 0x02, 0x01, + 0x00, 0x80, 0x40, 0x20, 0x10, 0x2F, 0xF0, 0x07, 0x86, 0x30, 0x0C, 0x03, + 0x01, 0x81, 0x81, 0xF0, 0x1E, 0x03, 0x80, 0x60, 0x18, 0x06, 0x01, 0x00, + 0xCC, 0x63, 0xE0, 0x00, 0x20, 0x0C, 0x03, 0x80, 0xA0, 0x2C, 0x09, 0x82, + 0x30, 0x84, 0x31, 0x8C, 0x33, 0x06, 0x7F, 0xE0, 0x30, 0x06, 0x00, 0x80, + 0x30, 0x03, 0xE1, 0x80, 0x20, 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x60, 0x06, + 0x00, 0xC0, 0x18, 0x03, 0x00, 0x40, 0x18, 0x02, 0x30, 0x87, 0xE0, 0x00, + 0x70, 0x3C, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x7F, 0x07, 0x18, 0x60, + 0xCE, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x18, 0x41, 0x86, 0x30, 0x3E, + 0x00, 0x7F, 0xF0, 0x18, 0x03, 0x00, 0xC0, 0x10, 0x06, 0x01, 0x80, 0x30, + 0x0C, 0x01, 0x00, 0x60, 0x08, 0x03, 0x00, 0xC0, 0x10, 0x06, 0x00, 0x0F, + 0x83, 0x18, 0xC1, 0x98, 0x33, 0x06, 0x71, 0x87, 0x60, 0x70, 0x17, 0x0C, + 0x71, 0x07, 0x60, 0x6C, 0x0D, 0x81, 0xB0, 0x63, 0x1C, 0x3E, 0x00, 0x07, + 0x83, 0x18, 0xC1, 0x18, 0x36, 0x06, 0xC0, 0xD8, 0x1B, 0x07, 0x60, 0xE6, + 0x38, 0x7F, 0x00, 0xC0, 0x30, 0x0C, 0x07, 0x03, 0xC0, 0xC0, 0x00, 0x33, + 0x30, 0x00, 0x00, 0xCC, 0xC0, 0x18, 0xC6, 0x00, 0x00, 0x00, 0x03, 0x18, + 0x44, 0x40, 0x00, 0x00, 0x03, 0x00, 0xF0, 0x38, 0x1E, 0x07, 0x80, 0xE0, + 0x0F, 0x00, 0x1C, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x00, 0x10, 0xFF, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x0C, 0x00, 0xF0, 0x01, + 0xC0, 0x07, 0x80, 0x1E, 0x00, 0x70, 0x0F, 0x03, 0xC1, 0xE0, 0x78, 0x0E, + 0x00, 0x80, 0x00, 0x3E, 0x21, 0x90, 0x60, 0x30, 0x38, 0x38, 0x30, 0x30, + 0x20, 0x20, 0x10, 0x00, 0x00, 0x06, 0x03, 0x01, 0x80, 0x07, 0xE0, 0x1C, + 0x18, 0x30, 0x04, 0x60, 0x02, 0x61, 0xDA, 0xC3, 0x31, 0xC6, 0x31, 0xC4, + 0x31, 0xCC, 0x31, 0xCC, 0x21, 0xCC, 0x62, 0x6C, 0xE4, 0x67, 0x38, 0x30, + 0x00, 0x1C, 0x08, 0x07, 0xF0, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x80, 0x0B, + 0x00, 0x16, 0x00, 0x4E, 0x00, 0x9C, 0x02, 0x18, 0x08, 0x30, 0x1F, 0xE0, + 0x40, 0xC1, 0x81, 0xC2, 0x03, 0x8C, 0x07, 0x3C, 0x1F, 0x80, 0x1F, 0xF0, + 0x1C, 0x60, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x38, 0x60, 0xC3, 0x03, 0xF0, + 0x1C, 0x30, 0x60, 0x61, 0x81, 0x86, 0x06, 0x38, 0x18, 0xC0, 0xC3, 0x06, + 0x3F, 0xF0, 0x01, 0xF9, 0x06, 0x0F, 0x1C, 0x06, 0x38, 0x02, 0x30, 0x02, + 0x60, 0x00, 0x60, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, + 0xC0, 0x00, 0xC0, 0x08, 0x60, 0x10, 0x30, 0x60, 0x1F, 0x80, 0x1F, 0xF0, + 0x07, 0x0C, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0x0E, 0x03, 0x0C, 0x03, + 0x0C, 0x03, 0x1C, 0x03, 0x1C, 0x07, 0x18, 0x06, 0x18, 0x06, 0x38, 0x0C, + 0x30, 0x18, 0x30, 0x70, 0xFF, 0x80, 0x1F, 0xFF, 0x07, 0x07, 0x06, 0x02, + 0x06, 0x02, 0x06, 0x00, 0x0E, 0x10, 0x0C, 0x30, 0x0F, 0xF0, 0x1C, 0x20, + 0x18, 0x20, 0x18, 0x00, 0x18, 0x00, 0x38, 0x04, 0x30, 0x08, 0x30, 0x38, + 0xFF, 0xF8, 0x1F, 0xFF, 0x07, 0x07, 0x07, 0x02, 0x06, 0x02, 0x06, 0x00, + 0x0E, 0x10, 0x0C, 0x30, 0x0F, 0xF0, 0x1C, 0x20, 0x1C, 0x20, 0x18, 0x00, + 0x18, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0xFC, 0x00, 0x01, 0xF1, + 0x06, 0x0F, 0x18, 0x07, 0x38, 0x02, 0x30, 0x02, 0x60, 0x00, 0x60, 0x00, + 0xE0, 0x00, 0xC0, 0x7F, 0xC0, 0x1C, 0xC0, 0x1C, 0xC0, 0x18, 0xC0, 0x18, + 0x60, 0x18, 0x30, 0x38, 0x0F, 0xC0, 0x1F, 0xC7, 0xE0, 0xE0, 0x70, 0x18, + 0x0E, 0x03, 0x01, 0x80, 0x60, 0x30, 0x1C, 0x0E, 0x03, 0x01, 0x80, 0x7F, + 0xF0, 0x1C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x81, + 0xC0, 0x60, 0x38, 0x0C, 0x06, 0x07, 0xE3, 0xF0, 0x1F, 0x83, 0x81, 0x80, + 0xC0, 0x60, 0x70, 0x30, 0x18, 0x1C, 0x0C, 0x06, 0x03, 0x03, 0x81, 0x80, + 0xC1, 0xF8, 0x03, 0xF0, 0x0C, 0x00, 0xC0, 0x1C, 0x01, 0x80, 0x18, 0x03, + 0x80, 0x30, 0x03, 0x00, 0x30, 0x07, 0x00, 0x60, 0x06, 0x0C, 0xE0, 0xCC, + 0x07, 0x80, 0x1F, 0xCF, 0x83, 0x83, 0x81, 0x81, 0x00, 0xC3, 0x00, 0x62, + 0x00, 0x72, 0x00, 0x36, 0x00, 0x1E, 0x00, 0x1D, 0x80, 0x0C, 0xE0, 0x06, + 0x30, 0x03, 0x1C, 0x03, 0x87, 0x01, 0x81, 0x80, 0xC0, 0xE1, 0xF9, 0xFC, + 0x1F, 0xC0, 0x1C, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, 0x38, 0x00, 0xC0, + 0x03, 0x00, 0x1C, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, 0x38, 0x0C, 0xC0, + 0x23, 0x03, 0xBF, 0xFE, 0x0F, 0x00, 0x78, 0x38, 0x07, 0x81, 0xC0, 0x38, + 0x0E, 0x02, 0xC0, 0x70, 0x3E, 0x05, 0xC1, 0x70, 0x2E, 0x13, 0x01, 0x31, + 0x98, 0x11, 0x89, 0xC0, 0x8C, 0x8C, 0x04, 0x6C, 0x60, 0x23, 0x43, 0x02, + 0x1C, 0x38, 0x10, 0xE1, 0x81, 0x86, 0x1C, 0x1F, 0x23, 0xF8, 0x1E, 0x07, + 0xC1, 0xC0, 0x60, 0x70, 0x10, 0x1C, 0x0C, 0x05, 0x82, 0x02, 0x60, 0x80, + 0x9C, 0x60, 0x23, 0x10, 0x10, 0xC4, 0x04, 0x19, 0x01, 0x06, 0xC0, 0x40, + 0xE0, 0x20, 0x38, 0x08, 0x0E, 0x06, 0x01, 0x03, 0xE0, 0x40, 0x01, 0xF0, + 0x0C, 0x10, 0x30, 0x10, 0xC0, 0x33, 0x00, 0x6E, 0x00, 0xD8, 0x01, 0xF0, + 0x03, 0xC0, 0x0D, 0x80, 0x1B, 0x00, 0x76, 0x00, 0xCC, 0x03, 0x08, 0x0C, + 0x18, 0x70, 0x0F, 0x80, 0x1F, 0xF0, 0x1C, 0x60, 0x60, 0xC1, 0x83, 0x06, + 0x0C, 0x38, 0x30, 0xC1, 0x83, 0x0E, 0x1F, 0xE0, 0x60, 0x01, 0x80, 0x06, + 0x00, 0x38, 0x00, 0xC0, 0x03, 0x00, 0x3F, 0x00, 0x01, 0xF0, 0x06, 0x10, + 0x30, 0x30, 0xC0, 0x33, 0x00, 0x66, 0x00, 0xD8, 0x01, 0xB0, 0x03, 0xE0, + 0x0F, 0x80, 0x1B, 0x00, 0x36, 0x00, 0xCC, 0x03, 0x98, 0x06, 0x18, 0x18, + 0x18, 0xC0, 0x0E, 0x00, 0x20, 0x01, 0xF8, 0x36, 0x7F, 0x80, 0x1F, 0xF0, + 0x1C, 0x60, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x38, 0x70, 0xC3, 0x83, 0xF8, + 0x1D, 0xC0, 0x63, 0x01, 0x8C, 0x06, 0x18, 0x38, 0x60, 0xC1, 0xC3, 0x03, + 0x3F, 0x0F, 0x07, 0x90, 0xC7, 0x18, 0x21, 0x82, 0x18, 0x01, 0xC0, 0x0E, + 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xC4, 0x0C, 0x40, 0xC6, 0x08, 0xE1, + 0x89, 0xE0, 0x7F, 0xFE, 0xC7, 0x1D, 0x0C, 0x14, 0x18, 0x20, 0x70, 0x00, + 0xE0, 0x01, 0x80, 0x03, 0x00, 0x0E, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, + 0x01, 0xC0, 0x03, 0x00, 0x0E, 0x00, 0x7F, 0x80, 0x7E, 0x1F, 0x38, 0x0C, + 0x38, 0x0C, 0x30, 0x08, 0x30, 0x08, 0x70, 0x08, 0x70, 0x10, 0x60, 0x10, + 0x60, 0x10, 0xE0, 0x10, 0xC0, 0x20, 0xC0, 0x20, 0xC0, 0x60, 0xC0, 0x40, + 0x61, 0x80, 0x3F, 0x00, 0xFC, 0x3E, 0xE0, 0x18, 0xC0, 0x21, 0x80, 0xC3, + 0x81, 0x07, 0x04, 0x0E, 0x08, 0x0C, 0x20, 0x18, 0x80, 0x31, 0x00, 0x64, + 0x00, 0xF0, 0x01, 0xE0, 0x01, 0x80, 0x02, 0x00, 0x04, 0x00, 0xFD, 0xF8, + 0xF7, 0x07, 0x06, 0x30, 0x60, 0x63, 0x07, 0x04, 0x30, 0x70, 0x83, 0x8F, + 0x08, 0x38, 0xB1, 0x03, 0x93, 0x10, 0x19, 0x32, 0x01, 0xA3, 0x20, 0x1A, + 0x34, 0x01, 0xC3, 0x40, 0x1C, 0x38, 0x01, 0x83, 0x00, 0x18, 0x30, 0x01, + 0x02, 0x00, 0x1F, 0x9F, 0x0E, 0x06, 0x06, 0x04, 0x07, 0x08, 0x03, 0x10, + 0x03, 0x20, 0x03, 0xC0, 0x01, 0x80, 0x01, 0xC0, 0x03, 0xC0, 0x06, 0xE0, + 0x0C, 0x60, 0x18, 0x60, 0x30, 0x70, 0x70, 0x78, 0xF8, 0xFC, 0xFC, 0xFB, + 0x81, 0x8C, 0x08, 0x60, 0x83, 0x8C, 0x0C, 0xC0, 0x64, 0x03, 0xC0, 0x0C, + 0x00, 0xE0, 0x07, 0x00, 0x30, 0x01, 0x80, 0x1C, 0x00, 0xC0, 0x1F, 0xC0, + 0x1F, 0xFE, 0x30, 0x38, 0xC0, 0xF1, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, + 0x70, 0x01, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x01, 0xC0, + 0x47, 0x01, 0x1C, 0x06, 0x7F, 0xF8, 0x07, 0x04, 0x08, 0x08, 0x08, 0x18, + 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x80, 0x80, + 0x80, 0xE0, 0xC0, 0xC0, 0x40, 0x60, 0x20, 0x30, 0x30, 0x18, 0x18, 0x08, + 0x0C, 0x04, 0x06, 0x06, 0x03, 0x03, 0x0E, 0x04, 0x08, 0x10, 0x60, 0x81, + 0x02, 0x04, 0x18, 0x20, 0x40, 0x81, 0x02, 0x08, 0x10, 0x20, 0x47, 0x80, + 0x0C, 0x03, 0x81, 0xE0, 0x4C, 0x33, 0x08, 0x66, 0x19, 0x03, 0xC0, 0xC0, + 0xFF, 0xF0, 0xCE, 0x63, 0x07, 0xA0, 0xCE, 0x18, 0x63, 0x04, 0x60, 0xC6, + 0x0C, 0xC0, 0xCC, 0x18, 0xC3, 0x8C, 0x5A, 0x79, 0xC0, 0x38, 0x06, 0x01, + 0x80, 0x40, 0x30, 0x0C, 0xE3, 0xCC, 0xC3, 0x70, 0xD8, 0x36, 0x19, 0x06, + 0xC3, 0x30, 0x8C, 0xC3, 0xE0, 0x0F, 0x0C, 0xCC, 0x6C, 0x06, 0x06, 0x03, + 0x01, 0x80, 0xC0, 0x73, 0x1E, 0x00, 0x00, 0x70, 0x01, 0x80, 0x0C, 0x00, + 0x60, 0x02, 0x03, 0xF0, 0x31, 0x83, 0x08, 0x30, 0xC3, 0x06, 0x18, 0x31, + 0x81, 0x8C, 0x18, 0x61, 0xCB, 0x16, 0x8F, 0x38, 0x07, 0x19, 0x31, 0x63, + 0x62, 0xEC, 0xD0, 0xC0, 0xC0, 0xE6, 0x78, 0x00, 0x38, 0x01, 0x30, 0x0C, + 0x00, 0x20, 0x01, 0x80, 0x06, 0x00, 0xFE, 0x00, 0x40, 0x03, 0x00, 0x0C, + 0x00, 0x30, 0x00, 0x80, 0x06, 0x00, 0x18, 0x00, 0x60, 0x01, 0x80, 0x04, + 0x00, 0x30, 0x00, 0xC0, 0x02, 0x00, 0x90, 0x03, 0x80, 0x00, 0x07, 0xC0, + 0xC7, 0x18, 0x61, 0x86, 0x18, 0xE1, 0x8C, 0x07, 0x80, 0x80, 0x1C, 0x00, + 0xF0, 0x33, 0x84, 0x18, 0x80, 0x88, 0x08, 0x61, 0x03, 0xE0, 0x1C, 0x00, + 0xC0, 0x0C, 0x00, 0xC0, 0x18, 0x01, 0x8E, 0x1B, 0x61, 0xC6, 0x38, 0x63, + 0x8C, 0x30, 0xC3, 0x0C, 0x60, 0xC6, 0x1A, 0x61, 0xA4, 0x1C, 0x18, 0xC6, + 0x00, 0x0B, 0xC6, 0x23, 0x18, 0x8C, 0x63, 0x5C, 0x01, 0x80, 0xC0, 0x60, + 0x00, 0x00, 0x0C, 0x1E, 0x02, 0x03, 0x01, 0x80, 0xC0, 0x40, 0x60, 0x30, + 0x18, 0x08, 0x0C, 0x06, 0x02, 0x1B, 0x0F, 0x00, 0x1C, 0x01, 0x80, 0x30, + 0x06, 0x01, 0x80, 0x33, 0xC6, 0x30, 0x88, 0x32, 0x06, 0x80, 0xF0, 0x1B, + 0x06, 0x60, 0xC4, 0x18, 0xD2, 0x0C, 0x3C, 0x61, 0x86, 0x18, 0xC3, 0x0C, + 0x21, 0x86, 0x18, 0x43, 0x2D, 0x38, 0x78, 0xE7, 0x0D, 0xB5, 0x8D, 0x1C, + 0xC7, 0x0C, 0x63, 0x8E, 0x31, 0x86, 0x30, 0xC3, 0x18, 0xC1, 0x0C, 0x61, + 0x84, 0xB0, 0xC6, 0xB0, 0x63, 0x80, 0x78, 0xE1, 0xB6, 0x14, 0x63, 0x84, + 0x38, 0xC3, 0x0C, 0x70, 0x86, 0x18, 0x61, 0x96, 0x1A, 0xC1, 0xC0, 0x0F, + 0x06, 0x63, 0x0D, 0x83, 0x60, 0xF0, 0x3C, 0x1B, 0x06, 0xC3, 0x39, 0x87, + 0x80, 0x1E, 0xF0, 0x39, 0xC1, 0x86, 0x0C, 0x30, 0xC1, 0x86, 0x0C, 0x30, + 0xC3, 0x06, 0x18, 0x60, 0xC6, 0x07, 0xC0, 0x60, 0x03, 0x00, 0x18, 0x00, + 0xC0, 0x1F, 0x00, 0x07, 0x81, 0x9C, 0x63, 0x98, 0x76, 0x0C, 0xC1, 0xB0, + 0x76, 0x0E, 0xC3, 0x98, 0xB1, 0xE6, 0x00, 0x80, 0x30, 0x06, 0x00, 0xC0, + 0xFC, 0x79, 0x8F, 0xC5, 0x07, 0x03, 0x01, 0x80, 0xC0, 0xC0, 0x60, 0x30, + 0x10, 0x00, 0x1E, 0x98, 0xCC, 0x27, 0x11, 0x80, 0xE0, 0x39, 0x0C, 0x86, + 0x62, 0x2E, 0x00, 0x08, 0x67, 0xCC, 0x30, 0xC6, 0x18, 0x61, 0x8C, 0x34, + 0xE0, 0xF0, 0xCC, 0x19, 0x83, 0x30, 0xC6, 0x18, 0x87, 0x31, 0x66, 0x3C, + 0xCB, 0x1A, 0x6B, 0x8E, 0x00, 0x70, 0xCC, 0x33, 0x04, 0xC2, 0x18, 0x86, + 0x41, 0x90, 0x68, 0x1C, 0x06, 0x01, 0x00, 0x61, 0x0F, 0x84, 0x36, 0x30, + 0xDC, 0xC1, 0x35, 0x08, 0xD4, 0x23, 0x91, 0x0E, 0x48, 0x30, 0xE0, 0xC3, + 0x02, 0x08, 0x00, 0x0C, 0x63, 0x4A, 0x07, 0x00, 0x70, 0x06, 0x00, 0x20, + 0x07, 0x00, 0xB0, 0x0B, 0x21, 0x14, 0xE1, 0x80, 0x38, 0x63, 0x0C, 0x30, + 0x86, 0x10, 0xC4, 0x0C, 0x81, 0xA0, 0x34, 0x07, 0x00, 0x60, 0x08, 0x02, + 0x00, 0x40, 0x10, 0x04, 0x07, 0x00, 0x1F, 0x90, 0x80, 0x80, 0xC0, 0xC0, + 0x40, 0x60, 0x60, 0x60, 0x38, 0x3E, 0x03, 0xA0, 0x60, 0x00, 0x83, 0x81, + 0x01, 0x80, 0xC0, 0x40, 0x60, 0x30, 0x10, 0x10, 0x1C, 0x06, 0x03, 0x03, + 0x01, 0x80, 0xC0, 0x40, 0x60, 0x30, 0x18, 0x07, 0x00, 0xFF, 0xFF, 0x07, + 0x00, 0xC0, 0x60, 0x30, 0x10, 0x18, 0x0C, 0x06, 0x06, 0x03, 0x01, 0x80, + 0x60, 0x40, 0x60, 0x30, 0x10, 0x18, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x78, + 0x18, 0x8C, 0x0F, 0x00}; + +const GFXglyph FreeSerifItalic12pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 6, 0, 1}, // 0x20 ' ' + {0, 6, 16, 8, 1, -15}, // 0x21 '!' + {12, 7, 6, 8, 3, -15}, // 0x22 '"' + {18, 13, 16, 12, 0, -15}, // 0x23 '#' + {44, 12, 20, 12, 0, -17}, // 0x24 '$' + {74, 17, 17, 20, 2, -16}, // 0x25 '%' + {111, 15, 16, 19, 2, -15}, // 0x26 '&' + {141, 2, 6, 5, 4, -15}, // 0x27 ''' + {143, 7, 20, 8, 1, -15}, // 0x28 '(' + {161, 7, 20, 8, 0, -15}, // 0x29 ')' + {179, 8, 10, 12, 4, -15}, // 0x2A '*' + {189, 11, 11, 16, 2, -10}, // 0x2B '+' + {205, 3, 6, 6, 0, -2}, // 0x2C ',' + {208, 5, 1, 8, 1, -5}, // 0x2D '-' + {209, 2, 3, 6, 1, -2}, // 0x2E '.' + {210, 11, 16, 7, 0, -15}, // 0x2F '/' + {232, 11, 17, 12, 1, -16}, // 0x30 '0' + {256, 9, 17, 12, 1, -16}, // 0x31 '1' + {276, 10, 15, 12, 1, -14}, // 0x32 '2' + {295, 10, 16, 12, 1, -15}, // 0x33 '3' + {315, 11, 16, 12, 0, -15}, // 0x34 '4' + {337, 11, 16, 12, 0, -15}, // 0x35 '5' + {359, 12, 17, 12, 1, -16}, // 0x36 '6' + {385, 11, 16, 12, 2, -15}, // 0x37 '7' + {407, 11, 17, 12, 1, -16}, // 0x38 '8' + {431, 11, 17, 12, 1, -16}, // 0x39 '9' + {455, 4, 11, 6, 1, -10}, // 0x3A ':' + {461, 5, 14, 6, 0, -10}, // 0x3B ';' + {470, 12, 13, 14, 1, -12}, // 0x3C '<' + {490, 12, 6, 16, 2, -8}, // 0x3D '=' + {499, 12, 13, 14, 2, -12}, // 0x3E '>' + {519, 9, 16, 11, 3, -15}, // 0x3F '?' + {537, 16, 16, 19, 2, -15}, // 0x40 '@' + {569, 15, 15, 16, 0, -14}, // 0x41 'A' + {598, 14, 16, 14, 0, -15}, // 0x42 'B' + {626, 16, 16, 15, 1, -15}, // 0x43 'C' + {658, 16, 16, 17, 0, -15}, // 0x44 'D' + {690, 16, 16, 14, 0, -15}, // 0x45 'E' + {722, 16, 16, 14, 0, -15}, // 0x46 'F' + {754, 16, 16, 17, 1, -15}, // 0x47 'G' + {786, 19, 16, 17, 0, -15}, // 0x48 'H' + {824, 9, 16, 8, 0, -15}, // 0x49 'I' + {842, 12, 16, 10, 0, -15}, // 0x4A 'J' + {866, 17, 16, 15, 0, -15}, // 0x4B 'K' + {900, 14, 16, 14, 0, -15}, // 0x4C 'L' + {928, 21, 16, 20, 0, -15}, // 0x4D 'M' + {970, 18, 16, 16, 0, -15}, // 0x4E 'N' + {1006, 15, 16, 16, 1, -15}, // 0x4F 'O' + {1036, 14, 16, 14, 0, -15}, // 0x50 'P' + {1064, 15, 20, 16, 1, -15}, // 0x51 'Q' + {1102, 14, 16, 15, 0, -15}, // 0x52 'R' + {1130, 12, 16, 11, 0, -15}, // 0x53 'S' + {1154, 15, 16, 14, 2, -15}, // 0x54 'T' + {1184, 16, 16, 17, 3, -15}, // 0x55 'U' + {1216, 15, 16, 16, 3, -15}, // 0x56 'V' + {1246, 20, 16, 21, 3, -15}, // 0x57 'W' + {1286, 16, 16, 16, 0, -15}, // 0x58 'X' + {1318, 13, 16, 14, 3, -15}, // 0x59 'Y' + {1344, 15, 16, 14, 0, -15}, // 0x5A 'Z' + {1374, 8, 20, 9, 1, -15}, // 0x5B '[' + {1394, 8, 16, 12, 3, -15}, // 0x5C '\' + {1410, 7, 20, 9, 1, -15}, // 0x5D ']' + {1428, 10, 9, 10, 0, -15}, // 0x5E '^' + {1440, 12, 1, 12, 0, 3}, // 0x5F '_' + {1442, 4, 4, 6, 3, -15}, // 0x60 '`' + {1444, 12, 11, 12, 0, -10}, // 0x61 'a' + {1461, 10, 16, 11, 1, -15}, // 0x62 'b' + {1481, 9, 11, 10, 1, -10}, // 0x63 'c' + {1494, 13, 16, 12, 0, -15}, // 0x64 'd' + {1520, 8, 11, 10, 1, -10}, // 0x65 'e' + {1531, 14, 22, 10, -2, -16}, // 0x66 'f' + {1570, 12, 16, 11, -1, -10}, // 0x67 'g' + {1594, 12, 16, 12, 0, -15}, // 0x68 'h' + {1618, 5, 16, 6, 1, -15}, // 0x69 'i' + {1628, 9, 21, 7, -2, -15}, // 0x6A 'j' + {1652, 11, 16, 11, 0, -15}, // 0x6B 'k' + {1674, 6, 16, 6, 1, -15}, // 0x6C 'l' + {1686, 17, 11, 17, 0, -10}, // 0x6D 'm' + {1710, 12, 11, 12, 0, -10}, // 0x6E 'n' + {1727, 10, 11, 11, 1, -10}, // 0x6F 'o' + {1741, 13, 16, 11, -2, -10}, // 0x70 'p' + {1767, 11, 16, 12, 0, -10}, // 0x71 'q' + {1789, 9, 11, 9, 0, -10}, // 0x72 'r' + {1802, 9, 11, 8, 0, -10}, // 0x73 's' + {1815, 6, 13, 6, 1, -12}, // 0x74 't' + {1825, 11, 11, 12, 1, -10}, // 0x75 'u' + {1841, 10, 11, 11, 1, -10}, // 0x76 'v' + {1855, 14, 11, 16, 2, -10}, // 0x77 'w' + {1875, 12, 11, 10, -1, -10}, // 0x78 'x' + {1892, 11, 16, 11, 0, -10}, // 0x79 'y' + {1914, 9, 13, 9, 0, -10}, // 0x7A 'z' + {1929, 9, 21, 10, 1, -16}, // 0x7B '{' + {1953, 1, 16, 7, 3, -15}, // 0x7C '|' + {1955, 9, 21, 10, 0, -16}, // 0x7D '}' + {1979, 11, 3, 13, 1, -6}}; // 0x7E '~' + +const GFXfont FreeSerifItalic12pt7b PROGMEM = { + (uint8_t *)FreeSerifItalic12pt7bBitmaps, + (GFXglyph *)FreeSerifItalic12pt7bGlyphs, 0x20, 0x7E, 29}; + +// Approx. 2656 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic18pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic18pt7b.h new file mode 100644 index 0000000..7200365 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic18pt7b.h @@ -0,0 +1,449 @@ +const uint8_t FreeSerifItalic18pt7bBitmaps[] PROGMEM = { + 0x01, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0x81, 0xE0, 0x70, 0x1C, 0x06, 0x01, + 0x80, 0xC0, 0x30, 0x0C, 0x02, 0x01, 0x80, 0x40, 0x10, 0x00, 0x00, 0x01, + 0x80, 0xF0, 0x3C, 0x06, 0x00, 0x38, 0x77, 0x8F, 0x78, 0xF7, 0x0E, 0x60, + 0xE6, 0x0C, 0xC1, 0x8C, 0x18, 0x81, 0x00, 0x00, 0x60, 0xC0, 0x0C, 0x38, + 0x03, 0x86, 0x00, 0x60, 0xC0, 0x0C, 0x38, 0x03, 0x06, 0x00, 0x60, 0xC0, + 0xFF, 0xFF, 0x1F, 0xFF, 0xE0, 0x61, 0xC0, 0x1C, 0x30, 0x03, 0x06, 0x00, + 0x61, 0xC0, 0x18, 0x30, 0x3F, 0xFF, 0xC7, 0xFF, 0xF8, 0x18, 0x30, 0x03, + 0x0E, 0x00, 0xE1, 0x80, 0x18, 0x30, 0x03, 0x0C, 0x00, 0xC1, 0x80, 0x18, + 0x70, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, 0x0F, 0xC0, 0x61, 0xE1, + 0x86, 0xC6, 0x0D, 0x8C, 0x1A, 0x18, 0x24, 0x38, 0xC0, 0x39, 0x80, 0x7F, + 0x00, 0x7E, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xDC, 0x03, 0x38, + 0x06, 0x32, 0x0C, 0x64, 0x18, 0xDC, 0x71, 0xB8, 0xC6, 0x39, 0x8C, 0x3F, + 0x30, 0x1F, 0x80, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x07, 0x80, 0x60, + 0x0F, 0xE0, 0xE0, 0x0F, 0x0F, 0xB0, 0x0E, 0x04, 0x30, 0x07, 0x02, 0x18, + 0x07, 0x01, 0x18, 0x03, 0x00, 0x8C, 0x01, 0x80, 0x8C, 0x00, 0xC0, 0x4C, + 0x00, 0x60, 0x66, 0x1F, 0x30, 0x66, 0x1F, 0xCC, 0x63, 0x1C, 0x67, 0xE3, + 0x1C, 0x19, 0xE1, 0x1C, 0x04, 0x01, 0x8C, 0x02, 0x00, 0x8E, 0x01, 0x00, + 0xC7, 0x00, 0x80, 0xC3, 0x00, 0x80, 0x61, 0x80, 0xC0, 0x60, 0xC0, 0xC0, + 0x20, 0x70, 0xE0, 0x30, 0x1F, 0xC0, 0x10, 0x07, 0xC0, 0x00, 0x1E, 0x00, + 0x00, 0xFC, 0x00, 0x07, 0x18, 0x00, 0x18, 0x60, 0x00, 0xE1, 0x80, 0x03, + 0x8C, 0x00, 0x0E, 0x60, 0x00, 0x3B, 0x00, 0x00, 0xF0, 0x00, 0x07, 0x80, + 0x00, 0x7F, 0x1F, 0xC3, 0x3C, 0x1C, 0x38, 0x70, 0x61, 0xE1, 0xE3, 0x87, + 0x07, 0x8C, 0x3C, 0x0F, 0x60, 0xF0, 0x3D, 0x03, 0xC0, 0x78, 0x0F, 0x01, + 0xE0, 0x3E, 0x07, 0xC0, 0x7C, 0x77, 0x84, 0xFF, 0x8F, 0xE1, 0xF8, 0x0F, + 0x00, 0x3B, 0xDE, 0xE7, 0x33, 0x18, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, + 0xC0, 0xC0, 0xE0, 0x60, 0x70, 0x38, 0x18, 0x0C, 0x0E, 0x07, 0x03, 0x01, + 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x01, 0x00, 0x80, 0x40, 0x30, + 0x08, 0x04, 0x02, 0x00, 0x04, 0x01, 0x00, 0x80, 0x60, 0x10, 0x08, 0x04, + 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x0E, 0x07, 0x03, 0x81, + 0x80, 0xC0, 0xE0, 0x60, 0x30, 0x30, 0x18, 0x18, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x06, 0x00, 0x60, 0x06, 0x0C, 0x43, 0xE4, 0xF1, 0x58, 0x0E, 0x00, + 0xF0, 0x74, 0xEE, 0x47, 0xC4, 0x30, 0x60, 0x06, 0x00, 0x60, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x31, 0xCE, + 0x31, 0x08, 0x98, 0xFF, 0xFF, 0x6F, 0xF6, 0x00, 0x06, 0x00, 0x0E, 0x00, + 0x0C, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x30, 0x00, 0x70, 0x00, 0x60, 0x00, + 0xE0, 0x00, 0xC0, 0x01, 0xC0, 0x03, 0x80, 0x03, 0x00, 0x07, 0x00, 0x06, + 0x00, 0x0E, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x30, 0x00, 0x70, + 0x00, 0x60, 0x00, 0xE0, 0x00, 0x00, 0x78, 0x00, 0xC3, 0x00, 0xC1, 0xC0, + 0xC0, 0x60, 0xE0, 0x30, 0xE0, 0x1C, 0x70, 0x0E, 0x70, 0x07, 0x38, 0x03, + 0xBC, 0x01, 0xDC, 0x01, 0xEE, 0x00, 0xFF, 0x00, 0x7F, 0x80, 0x3B, 0x80, + 0x1D, 0xC0, 0x1E, 0xE0, 0x0E, 0x70, 0x0F, 0x38, 0x07, 0x1C, 0x07, 0x06, + 0x03, 0x83, 0x83, 0x80, 0xC3, 0x00, 0x1F, 0x00, 0x00, 0xF0, 0x7F, 0x00, + 0x70, 0x07, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x1C, 0x01, + 0xC0, 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, 0x70, 0x07, 0x00, 0x70, 0x0E, + 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x1E, 0x0F, 0xF8, 0x01, 0xF0, 0x07, 0xFC, + 0x0C, 0x3E, 0x10, 0x1F, 0x20, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, + 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x30, 0x00, 0x70, 0x00, 0xE0, + 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x04, + 0x30, 0x0C, 0x7F, 0xF8, 0xFF, 0xF0, 0x00, 0x7C, 0x00, 0xFF, 0x00, 0xC3, + 0xC0, 0x80, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x1C, 0x00, + 0x38, 0x00, 0xF0, 0x03, 0xFC, 0x00, 0x1F, 0x00, 0x03, 0xC0, 0x01, 0xE0, + 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x07, + 0x00, 0x03, 0x07, 0x87, 0x03, 0xFF, 0x00, 0xFC, 0x00, 0x00, 0x01, 0x80, + 0x01, 0x80, 0x01, 0xC0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xB0, 0x01, 0xB8, + 0x01, 0x9C, 0x01, 0x8C, 0x00, 0x86, 0x00, 0x87, 0x00, 0x83, 0x80, 0x81, + 0x80, 0x81, 0xC0, 0xC0, 0xE0, 0xC0, 0x70, 0xFF, 0xFF, 0x7F, 0xFF, 0x00, + 0x1C, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0x80, + 0x01, 0xFF, 0x01, 0xFF, 0x02, 0x00, 0x02, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x0F, 0xC0, 0x0F, 0xF0, 0x00, 0xF8, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x1C, + 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x18, + 0x00, 0x30, 0x00, 0x30, 0x70, 0xE0, 0xFF, 0x80, 0x7E, 0x00, 0x00, 0x03, + 0x80, 0x1F, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, + 0x00, 0x3C, 0x00, 0x3D, 0xF0, 0x1F, 0xFE, 0x1F, 0x0F, 0x8E, 0x03, 0xC7, + 0x00, 0xF7, 0x00, 0x7B, 0x80, 0x3D, 0x80, 0x1E, 0xC0, 0x0F, 0x60, 0x0F, + 0xB0, 0x07, 0x98, 0x03, 0xC4, 0x03, 0xC3, 0x03, 0xC0, 0xC3, 0x80, 0x1F, + 0x00, 0x3F, 0xFF, 0x7F, 0xFE, 0x40, 0x0E, 0x80, 0x0C, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x30, 0x00, 0x70, 0x00, 0x60, 0x00, 0xC0, 0x01, 0xC0, 0x01, + 0x80, 0x03, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0x1C, + 0x00, 0x18, 0x00, 0x30, 0x00, 0x70, 0x00, 0x60, 0x00, 0xE0, 0x00, 0x00, + 0xF8, 0x03, 0x0E, 0x06, 0x06, 0x0C, 0x03, 0x0C, 0x03, 0x0C, 0x03, 0x0C, + 0x03, 0x0E, 0x06, 0x07, 0x8E, 0x07, 0xD8, 0x03, 0xE0, 0x07, 0xF0, 0x1C, + 0xF8, 0x30, 0x3C, 0x60, 0x1C, 0x60, 0x0E, 0xC0, 0x06, 0xC0, 0x06, 0xC0, + 0x06, 0xC0, 0x06, 0xE0, 0x0C, 0x60, 0x18, 0x38, 0x30, 0x0F, 0xC0, 0x01, + 0xF8, 0x07, 0x8C, 0x0E, 0x06, 0x1E, 0x02, 0x3C, 0x03, 0x3C, 0x03, 0x78, + 0x03, 0x78, 0x03, 0x78, 0x03, 0x78, 0x07, 0x78, 0x07, 0x78, 0x07, 0x3C, + 0x0E, 0x3E, 0x1E, 0x1F, 0xEE, 0x07, 0x9C, 0x00, 0x38, 0x00, 0x78, 0x00, + 0x70, 0x01, 0xE0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xE0, 0x00, 0x0C, + 0x3C, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x1E, 0x18, + 0x00, 0x07, 0x03, 0xC1, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x03, 0x81, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0x01, 0xF0, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF0, 0x01, + 0xF0, 0x03, 0xF0, 0x03, 0xF0, 0x00, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xE0, + 0x00, 0x7E, 0x00, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xF0, 0x00, 0x3F, + 0x00, 0x03, 0xC0, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC0, 0xC0, 0x00, 0x3C, 0x00, 0x07, 0xE0, 0x00, 0x7E, 0x00, 0x07, + 0xE0, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x3F, 0x00, 0x03, 0xC0, 0x01, + 0xF0, 0x01, 0xF8, 0x01, 0xF8, 0x01, 0xF0, 0x01, 0xF0, 0x03, 0xF0, 0x03, + 0xF0, 0x00, 0xF0, 0x00, 0x20, 0x00, 0x00, 0x0F, 0x81, 0x86, 0x30, 0x33, + 0x03, 0x30, 0x30, 0x03, 0x00, 0x60, 0x0E, 0x01, 0xC0, 0x38, 0x06, 0x00, + 0xC0, 0x08, 0x01, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0xF0, 0x0F, 0x00, 0x60, 0x00, 0x00, 0x7F, 0x00, 0x03, 0xFF, 0xE0, + 0x07, 0x80, 0xF0, 0x0E, 0x00, 0x38, 0x1C, 0x00, 0x0C, 0x38, 0x0E, 0x06, + 0x70, 0x3F, 0xE2, 0x70, 0x71, 0xE3, 0xF0, 0x60, 0xE1, 0xE0, 0xC0, 0xC1, + 0xE0, 0xC0, 0xC1, 0xE1, 0x81, 0xC1, 0xE1, 0x81, 0xC1, 0xE1, 0x81, 0x82, + 0xE1, 0x83, 0x82, 0x71, 0x83, 0x86, 0x71, 0xC7, 0x8C, 0x38, 0xF9, 0xF8, + 0x3C, 0xF0, 0xF0, 0x1E, 0x00, 0x00, 0x0F, 0x80, 0x30, 0x03, 0xFF, 0xE0, + 0x00, 0x7F, 0x00, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x01, 0xC0, 0x00, + 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x5E, 0x00, 0x04, 0xF0, + 0x00, 0x63, 0x80, 0x02, 0x1C, 0x00, 0x20, 0xE0, 0x01, 0x07, 0x00, 0x10, + 0x3C, 0x01, 0xFF, 0xE0, 0x0F, 0xFF, 0x00, 0xC0, 0x38, 0x04, 0x01, 0xC0, + 0x60, 0x0E, 0x06, 0x00, 0x78, 0x30, 0x03, 0xC3, 0x00, 0x1E, 0x38, 0x00, + 0xFB, 0xF0, 0x1F, 0xE0, 0x07, 0xFF, 0x80, 0x0F, 0xFF, 0x00, 0x78, 0x3C, + 0x03, 0xC0, 0xF0, 0x1E, 0x07, 0x80, 0xE0, 0x3C, 0x07, 0x01, 0xE0, 0x78, + 0x1E, 0x03, 0x83, 0xE0, 0x1F, 0xF8, 0x01, 0xFF, 0xC0, 0x0F, 0x0F, 0x00, + 0x70, 0x3C, 0x03, 0x80, 0xF0, 0x3C, 0x07, 0x81, 0xC0, 0x3C, 0x0E, 0x01, + 0xE0, 0xF0, 0x0F, 0x07, 0x80, 0xF0, 0x38, 0x0F, 0x81, 0xC1, 0xF8, 0x1F, + 0xFF, 0x83, 0xFF, 0xE0, 0x00, 0x00, 0x3F, 0x08, 0x07, 0xFF, 0xC0, 0xF8, + 0x3E, 0x0F, 0x00, 0x70, 0xF0, 0x03, 0x8F, 0x00, 0x08, 0xF0, 0x00, 0x47, + 0x80, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x01, 0xE0, 0x00, + 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xF0, + 0x00, 0x03, 0x80, 0x02, 0x1E, 0x00, 0x20, 0x78, 0x02, 0x03, 0xE0, 0x60, + 0x07, 0xFE, 0x00, 0x0F, 0xC0, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0xFF, 0xFC, + 0x00, 0x78, 0x1F, 0x00, 0x3C, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x0E, 0x00, + 0x78, 0x07, 0x00, 0x1E, 0x07, 0x80, 0x0F, 0x03, 0x80, 0x07, 0x81, 0xC0, + 0x03, 0xC1, 0xE0, 0x01, 0xE0, 0xF0, 0x00, 0xF0, 0x70, 0x00, 0x78, 0x38, + 0x00, 0x78, 0x3C, 0x00, 0x3C, 0x1E, 0x00, 0x3E, 0x0E, 0x00, 0x1E, 0x0F, + 0x00, 0x1E, 0x07, 0x80, 0x1E, 0x03, 0x80, 0x3E, 0x01, 0xC0, 0x7E, 0x01, + 0xFF, 0xFC, 0x03, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xF0, + 0x1E, 0x01, 0xC0, 0x78, 0x02, 0x01, 0xE0, 0x08, 0x07, 0x00, 0x00, 0x1C, + 0x08, 0x00, 0xF0, 0x60, 0x03, 0x83, 0x80, 0x0F, 0xFC, 0x00, 0x7F, 0xF0, + 0x01, 0xE0, 0xC0, 0x07, 0x03, 0x00, 0x1C, 0x08, 0x00, 0xF0, 0x20, 0x03, + 0x80, 0x00, 0x0E, 0x00, 0x00, 0x78, 0x00, 0x81, 0xE0, 0x06, 0x07, 0x00, + 0x38, 0x1C, 0x03, 0xC0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, + 0xFC, 0x07, 0xFF, 0xF0, 0x1E, 0x01, 0xC0, 0x78, 0x02, 0x01, 0xE0, 0x08, + 0x07, 0x00, 0x20, 0x1C, 0x00, 0x00, 0xF0, 0x20, 0x03, 0x81, 0x80, 0x0E, + 0x0C, 0x00, 0x7F, 0xF0, 0x01, 0xFF, 0xC0, 0x07, 0x03, 0x00, 0x1C, 0x0C, + 0x00, 0xF0, 0x20, 0x03, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0x78, 0x00, 0x01, + 0xE0, 0x00, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0xF8, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x00, 0x3F, 0x02, 0x01, 0xFF, 0x88, 0x0F, 0x81, 0xF0, 0x3C, + 0x01, 0xE0, 0xF0, 0x01, 0xC3, 0xC0, 0x01, 0x0F, 0x80, 0x02, 0x1E, 0x00, + 0x00, 0x7C, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x07, 0xC0, 0x00, + 0x0F, 0x00, 0x3F, 0xFE, 0x00, 0x1E, 0x3C, 0x00, 0x38, 0x78, 0x00, 0x70, + 0xF0, 0x00, 0xE0, 0xE0, 0x01, 0xC1, 0xE0, 0x07, 0x01, 0xE0, 0x0E, 0x01, + 0xF0, 0x3C, 0x01, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x3F, + 0xE0, 0x3E, 0x00, 0xF0, 0x07, 0x80, 0x1C, 0x00, 0xF0, 0x03, 0x80, 0x1C, + 0x00, 0xF0, 0x03, 0x80, 0x1E, 0x00, 0x70, 0x03, 0x80, 0x1E, 0x00, 0x70, + 0x03, 0x80, 0x1E, 0x00, 0x70, 0x03, 0x80, 0x1F, 0xFF, 0xF0, 0x03, 0xFF, + 0xFE, 0x00, 0x70, 0x03, 0xC0, 0x0E, 0x00, 0x70, 0x03, 0xC0, 0x0E, 0x00, + 0x70, 0x03, 0xC0, 0x0E, 0x00, 0x78, 0x03, 0xC0, 0x0E, 0x00, 0x78, 0x01, + 0xC0, 0x0E, 0x00, 0x78, 0x01, 0xC0, 0x0E, 0x00, 0x78, 0x03, 0xE0, 0x3F, + 0xE1, 0xFF, 0x00, 0x07, 0xFC, 0x07, 0xC0, 0x1E, 0x00, 0x78, 0x01, 0xC0, + 0x07, 0x00, 0x1C, 0x00, 0xF0, 0x03, 0x80, 0x0E, 0x00, 0x78, 0x01, 0xE0, + 0x07, 0x00, 0x1C, 0x00, 0xF0, 0x03, 0x80, 0x0E, 0x00, 0x78, 0x01, 0xE0, + 0x07, 0x00, 0x1C, 0x00, 0xF0, 0x0F, 0xF8, 0x00, 0x00, 0xFF, 0x80, 0x0F, + 0x00, 0x07, 0x80, 0x03, 0x80, 0x01, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, + 0x70, 0x00, 0x38, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x07, 0x80, 0x03, 0x80, 0x01, 0xC0, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, + 0x1E, 0x78, 0x0F, 0x38, 0x07, 0xF8, 0x01, 0xF0, 0x00, 0x07, 0xFC, 0x7F, + 0x80, 0xF8, 0x0F, 0x00, 0x38, 0x07, 0x00, 0x3C, 0x07, 0x00, 0x1C, 0x06, + 0x00, 0x0E, 0x06, 0x00, 0x07, 0x0C, 0x00, 0x07, 0x8C, 0x00, 0x03, 0x9C, + 0x00, 0x01, 0xD8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x73, + 0x80, 0x00, 0x39, 0xE0, 0x00, 0x3C, 0x78, 0x00, 0x1C, 0x1C, 0x00, 0x0E, + 0x0F, 0x00, 0x07, 0x03, 0x80, 0x07, 0x81, 0xE0, 0x03, 0x80, 0x70, 0x01, + 0xC0, 0x3C, 0x01, 0xE0, 0x1F, 0x03, 0xFE, 0x3F, 0xE0, 0x07, 0xFC, 0x00, + 0x1F, 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, + 0x01, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x07, 0x80, + 0x00, 0x78, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x0F, 0x00, 0x00, 0xE0, + 0x00, 0x0E, 0x00, 0x11, 0xE0, 0x03, 0x1E, 0x00, 0x61, 0xC0, 0x06, 0x1C, + 0x01, 0xE3, 0xFF, 0xFC, 0xFF, 0xFF, 0xC0, 0x07, 0xF0, 0x00, 0x7E, 0x03, + 0xE0, 0x01, 0xF0, 0x03, 0xC0, 0x03, 0xE0, 0x07, 0x80, 0x0F, 0x80, 0x1F, + 0x00, 0x37, 0x00, 0x2E, 0x00, 0x5E, 0x00, 0x5C, 0x01, 0xB8, 0x01, 0xB8, + 0x06, 0x70, 0x02, 0x78, 0x09, 0xE0, 0x04, 0x70, 0x33, 0xC0, 0x08, 0xE0, + 0xC7, 0x00, 0x31, 0xC1, 0x0E, 0x00, 0x43, 0x86, 0x3C, 0x00, 0x87, 0x18, + 0x70, 0x03, 0x0E, 0x20, 0xE0, 0x06, 0x1C, 0xC3, 0xC0, 0x08, 0x3B, 0x07, + 0x80, 0x10, 0x7C, 0x0E, 0x00, 0x60, 0x78, 0x1C, 0x00, 0x80, 0xE0, 0x78, + 0x03, 0x01, 0x80, 0xF0, 0x07, 0x03, 0x03, 0xE0, 0x3F, 0x84, 0x1F, 0xF0, + 0x00, 0x07, 0xC0, 0x3F, 0xC0, 0x78, 0x03, 0xE0, 0x0E, 0x00, 0x70, 0x03, + 0xC0, 0x18, 0x01, 0xF0, 0x0E, 0x00, 0x6C, 0x03, 0x00, 0x1B, 0x80, 0xC0, + 0x0C, 0xE0, 0x30, 0x03, 0x18, 0x1C, 0x00, 0xC7, 0x06, 0x00, 0x30, 0xC1, + 0x80, 0x18, 0x38, 0xE0, 0x06, 0x06, 0x30, 0x01, 0x81, 0x8C, 0x00, 0xC0, + 0x73, 0x00, 0x30, 0x0D, 0xC0, 0x0C, 0x03, 0xE0, 0x03, 0x00, 0x78, 0x01, + 0x80, 0x1E, 0x00, 0x60, 0x07, 0x00, 0x38, 0x00, 0xC0, 0x0E, 0x00, 0x30, + 0x0F, 0xE0, 0x04, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0xE0, 0x07, 0xC1, + 0xE0, 0x1E, 0x01, 0xE0, 0x78, 0x01, 0xC1, 0xE0, 0x03, 0xC7, 0x80, 0x07, + 0x9F, 0x00, 0x0F, 0x3C, 0x00, 0x1E, 0xF8, 0x00, 0x3D, 0xE0, 0x00, 0xFF, + 0xC0, 0x01, 0xEF, 0x80, 0x03, 0xDE, 0x00, 0x0F, 0xBC, 0x00, 0x1E, 0x78, + 0x00, 0x7C, 0xF0, 0x00, 0xF1, 0xE0, 0x03, 0xC1, 0xC0, 0x0F, 0x03, 0xC0, + 0x3C, 0x03, 0xC1, 0xF0, 0x03, 0xFF, 0x80, 0x01, 0xFC, 0x00, 0x00, 0x07, + 0xFF, 0xC0, 0x07, 0xFF, 0xC0, 0x0E, 0x0F, 0x80, 0x78, 0x1F, 0x01, 0xC0, + 0x3C, 0x07, 0x00, 0xF0, 0x1C, 0x03, 0xC0, 0xF0, 0x0F, 0x03, 0x80, 0x78, + 0x0E, 0x01, 0xE0, 0x78, 0x1F, 0x01, 0xFF, 0xF8, 0x07, 0x7F, 0x00, 0x1C, + 0x00, 0x00, 0xF0, 0x00, 0x03, 0x80, 0x00, 0x0E, 0x00, 0x00, 0x78, 0x00, + 0x01, 0xE0, 0x00, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0xF0, 0x00, 0x0F, + 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0xFF, 0xE0, 0x03, 0xC1, 0xE0, + 0x1E, 0x01, 0xC0, 0x78, 0x03, 0xC1, 0xE0, 0x03, 0x87, 0x80, 0x07, 0x8F, + 0x00, 0x0F, 0x3C, 0x00, 0x1E, 0x78, 0x00, 0x3D, 0xE0, 0x00, 0x7B, 0xC0, + 0x01, 0xFF, 0x80, 0x03, 0xDE, 0x00, 0x07, 0xBC, 0x00, 0x1F, 0x78, 0x00, + 0x3C, 0xF0, 0x00, 0xF1, 0xE0, 0x01, 0xE3, 0xC0, 0x07, 0x83, 0x80, 0x1E, + 0x07, 0x80, 0x78, 0x07, 0x01, 0xC0, 0x03, 0xDE, 0x00, 0x01, 0xC0, 0x00, + 0x06, 0x00, 0x00, 0x18, 0x00, 0x10, 0x7F, 0xC0, 0xC3, 0xFF, 0xFF, 0x08, + 0x07, 0xF0, 0x00, 0x07, 0xFF, 0x80, 0x0F, 0xFF, 0x00, 0x78, 0x3C, 0x03, + 0xC0, 0xF0, 0x1E, 0x07, 0x80, 0xE0, 0x3C, 0x07, 0x01, 0xE0, 0x78, 0x1E, + 0x03, 0x83, 0xF0, 0x1F, 0xFE, 0x01, 0xFF, 0xC0, 0x0F, 0x38, 0x00, 0x71, + 0xE0, 0x03, 0x87, 0x00, 0x3C, 0x38, 0x01, 0xC1, 0xE0, 0x0E, 0x07, 0x00, + 0xF0, 0x3C, 0x07, 0x81, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x3C, 0x1E, 0x00, + 0xF3, 0xFC, 0x07, 0xC0, 0x00, 0xF8, 0x81, 0xFF, 0xC1, 0xE1, 0xE1, 0xE0, + 0x70, 0xF0, 0x10, 0x78, 0x08, 0x3C, 0x00, 0x1F, 0x00, 0x07, 0x80, 0x01, + 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0xF8, + 0x80, 0x3C, 0x40, 0x1E, 0x20, 0x0F, 0x38, 0x07, 0x9E, 0x07, 0x8F, 0x87, + 0x84, 0x7F, 0xC2, 0x0F, 0x80, 0x3F, 0xFF, 0xF7, 0xFF, 0xFF, 0x70, 0x78, + 0x76, 0x07, 0x02, 0xC0, 0x70, 0x28, 0x0F, 0x02, 0x00, 0xF0, 0x00, 0x0E, + 0x00, 0x01, 0xE0, 0x00, 0x1E, 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x03, + 0xC0, 0x00, 0x3C, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x07, 0x80, 0x00, + 0x70, 0x00, 0x07, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x01, 0xF0, 0x00, + 0xFF, 0xE0, 0x00, 0x7F, 0xE0, 0xFE, 0x3F, 0x00, 0x78, 0x3C, 0x00, 0x60, + 0xF0, 0x01, 0x81, 0xE0, 0x03, 0x03, 0xC0, 0x06, 0x07, 0x00, 0x08, 0x1E, + 0x00, 0x30, 0x3C, 0x00, 0x60, 0x70, 0x00, 0x81, 0xE0, 0x01, 0x03, 0xC0, + 0x06, 0x07, 0x80, 0x0C, 0x0E, 0x00, 0x10, 0x3C, 0x00, 0x60, 0x78, 0x00, + 0xC0, 0xF0, 0x01, 0x01, 0xE0, 0x06, 0x03, 0xC0, 0x08, 0x03, 0xC0, 0x30, + 0x07, 0xC1, 0xC0, 0x07, 0xFF, 0x00, 0x03, 0xF8, 0x00, 0x00, 0xFF, 0x01, + 0xFB, 0xE0, 0x07, 0x8E, 0x00, 0x18, 0x78, 0x01, 0x83, 0xC0, 0x0C, 0x1E, + 0x00, 0xC0, 0xF0, 0x06, 0x03, 0x80, 0x60, 0x1C, 0x02, 0x00, 0xE0, 0x30, + 0x07, 0x83, 0x00, 0x3C, 0x10, 0x01, 0xE1, 0x80, 0x07, 0x08, 0x00, 0x38, + 0x80, 0x01, 0xC4, 0x00, 0x0E, 0x40, 0x00, 0x7C, 0x00, 0x03, 0xE0, 0x00, + 0x0E, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0xFF, + 0x3F, 0xC3, 0xFB, 0xE0, 0x78, 0x07, 0x8E, 0x03, 0xC0, 0x18, 0x78, 0x0E, + 0x01, 0x83, 0xC0, 0x70, 0x0C, 0x1E, 0x03, 0x80, 0x40, 0xF0, 0x3C, 0x06, + 0x03, 0x81, 0xE0, 0x60, 0x1C, 0x17, 0x83, 0x00, 0xE0, 0xBC, 0x30, 0x07, + 0x09, 0xE1, 0x00, 0x38, 0x47, 0x18, 0x01, 0xE4, 0x38, 0x80, 0x0F, 0x21, + 0xCC, 0x00, 0x7A, 0x0E, 0x40, 0x01, 0xD0, 0x76, 0x00, 0x0F, 0x03, 0xA0, + 0x00, 0x78, 0x1F, 0x00, 0x03, 0x80, 0xF0, 0x00, 0x1C, 0x07, 0x00, 0x00, + 0xC0, 0x38, 0x00, 0x06, 0x00, 0x80, 0x00, 0x20, 0x04, 0x00, 0x00, 0x0F, + 0xF8, 0x7F, 0x03, 0xE0, 0x3E, 0x01, 0xC0, 0x18, 0x01, 0xE0, 0x30, 0x01, + 0xE0, 0x60, 0x00, 0xE0, 0xC0, 0x00, 0xF1, 0xC0, 0x00, 0x71, 0x80, 0x00, + 0x7B, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xCE, 0x00, 0x01, 0x8F, 0x00, 0x01, + 0x07, 0x00, 0x03, 0x07, 0x00, 0x06, 0x07, 0x80, 0x0C, 0x03, 0x80, 0x18, + 0x03, 0xC0, 0x78, 0x03, 0xE0, 0xFE, 0x1F, 0xF8, 0xFF, 0x87, 0xE7, 0xC0, + 0x38, 0x70, 0x06, 0x0E, 0x01, 0x81, 0xE0, 0x30, 0x1C, 0x0C, 0x03, 0x83, + 0x00, 0x78, 0xC0, 0x07, 0x30, 0x00, 0xE4, 0x00, 0x1D, 0x80, 0x03, 0xE0, + 0x00, 0x38, 0x00, 0x0F, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x07, 0x00, + 0x01, 0xE0, 0x00, 0x38, 0x00, 0x07, 0x00, 0x01, 0xE0, 0x00, 0x7C, 0x00, + 0x3F, 0xF0, 0x00, 0x07, 0xFF, 0xFC, 0x3F, 0xFF, 0xE0, 0xE0, 0x0F, 0x82, + 0x00, 0x3C, 0x18, 0x01, 0xE0, 0x40, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, + 0xC0, 0x00, 0x0E, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, + 0x00, 0xF0, 0x00, 0x07, 0x80, 0x00, 0x1C, 0x00, 0x00, 0xF0, 0x00, 0x07, + 0x80, 0x00, 0x3C, 0x00, 0xC1, 0xE0, 0x02, 0x0F, 0x00, 0x18, 0x38, 0x01, + 0xE1, 0xFF, 0xFF, 0x0F, 0xFF, 0xFC, 0x00, 0x01, 0xF8, 0x0C, 0x00, 0xC0, + 0x06, 0x00, 0x30, 0x01, 0x80, 0x18, 0x00, 0xC0, 0x06, 0x00, 0x30, 0x03, + 0x00, 0x18, 0x00, 0xC0, 0x06, 0x00, 0x60, 0x03, 0x00, 0x18, 0x01, 0xC0, + 0x0C, 0x00, 0x60, 0x03, 0x00, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x06, + 0x00, 0x30, 0x01, 0xF8, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x30, + 0x03, 0x80, 0x18, 0x01, 0xC0, 0x0C, 0x00, 0xC0, 0x0E, 0x00, 0x60, 0x07, + 0x00, 0x30, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x0C, 0x00, 0xC0, 0x0E, 0x00, + 0x60, 0x07, 0x00, 0x30, 0x03, 0xF0, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, + 0x0E, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x60, 0x06, 0x00, + 0x60, 0x06, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x0F, 0xC0, 0x03, 0x80, + 0x07, 0x00, 0x1F, 0x00, 0x36, 0x00, 0xCE, 0x01, 0x8C, 0x06, 0x1C, 0x0C, + 0x18, 0x38, 0x38, 0x60, 0x31, 0xC0, 0x73, 0x00, 0x6E, 0x00, 0xE0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0xE3, 0x8F, 0x0E, 0x18, 0x30, 0x01, 0xEC, 0x0E, + 0x58, 0x30, 0x70, 0xE0, 0xC3, 0x81, 0x86, 0x07, 0x1C, 0x0C, 0x38, 0x18, + 0xE0, 0x71, 0xC0, 0xE3, 0x83, 0x87, 0x0B, 0x2F, 0x36, 0xCF, 0xCF, 0x1F, + 0x1C, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x06, 0x00, + 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0x1C, 0x7C, 0x1C, 0xFE, + 0x19, 0x8F, 0x1A, 0x07, 0x3C, 0x07, 0x38, 0x07, 0x38, 0x07, 0x70, 0x0E, + 0x70, 0x0E, 0x70, 0x1C, 0x60, 0x18, 0xE0, 0x30, 0xE0, 0x60, 0xE1, 0xC0, + 0x3F, 0x00, 0x01, 0xF0, 0x38, 0xC3, 0x8E, 0x78, 0x73, 0x80, 0x3C, 0x01, + 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x47, 0x84, 0x3F, + 0xC0, 0x7C, 0x00, 0x00, 0x01, 0x80, 0x07, 0xC0, 0x00, 0xE0, 0x00, 0x60, + 0x00, 0x30, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x06, 0x00, 0xF7, + 0x01, 0xC7, 0x81, 0xC3, 0x81, 0xC1, 0xC1, 0xE0, 0xE0, 0xE0, 0x60, 0xF0, + 0x30, 0x78, 0x38, 0x78, 0x18, 0x3C, 0x0C, 0x1E, 0x0C, 0x0F, 0x0E, 0x27, + 0xCB, 0x21, 0xF9, 0xE0, 0x78, 0xE0, 0x00, 0xF0, 0x1C, 0xC3, 0x86, 0x38, + 0x33, 0xC3, 0x1C, 0x31, 0xE3, 0x1F, 0xE0, 0xF0, 0x07, 0x80, 0x3C, 0x01, + 0xE0, 0x47, 0x84, 0x3F, 0xC0, 0x7C, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x33, + 0x00, 0x06, 0x30, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0x01, 0xC0, 0x00, 0x18, + 0x00, 0x01, 0x80, 0x00, 0x38, 0x00, 0x3F, 0xF8, 0x03, 0xFF, 0x80, 0x03, + 0x00, 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x06, 0x00, 0x00, + 0x60, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x0C, 0x00, 0x00, 0xC0, 0x00, + 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x18, 0x00, 0x01, 0x80, 0x00, 0x18, 0x00, + 0x03, 0x00, 0x00, 0x30, 0x00, 0xC6, 0x00, 0x0C, 0xC0, 0x00, 0x78, 0x00, + 0x00, 0x01, 0xF8, 0x07, 0x1F, 0x0E, 0x0F, 0x0C, 0x0E, 0x18, 0x0E, 0x18, + 0x0E, 0x18, 0x1E, 0x18, 0x3C, 0x0C, 0x78, 0x07, 0xE0, 0x08, 0x00, 0x18, + 0x00, 0x1E, 0x00, 0x0F, 0xE0, 0x13, 0xF0, 0x60, 0x78, 0xC0, 0x38, 0xC0, + 0x18, 0xC0, 0x18, 0xC0, 0x30, 0x60, 0x60, 0x3F, 0x80, 0x03, 0x00, 0x1F, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x0E, + 0x00, 0x0C, 0x00, 0x1C, 0x38, 0x1C, 0x7C, 0x1C, 0xCC, 0x19, 0x0C, 0x3A, + 0x0C, 0x3C, 0x1C, 0x3C, 0x18, 0x38, 0x18, 0x70, 0x38, 0x70, 0x38, 0x70, + 0x30, 0x60, 0x72, 0xE0, 0x76, 0xE0, 0x7C, 0xC0, 0x70, 0x03, 0x03, 0xC1, + 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x7E, 0x0F, 0x03, 0x81, 0x81, + 0xC0, 0xE0, 0x70, 0x30, 0x38, 0x1C, 0x1C, 0x4C, 0x47, 0xC3, 0xC0, 0x00, + 0x0C, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x03, 0xF0, 0x00, 0xE0, 0x01, 0x80, 0x03, 0x00, + 0x0E, 0x00, 0x1C, 0x00, 0x30, 0x00, 0x60, 0x01, 0xC0, 0x03, 0x80, 0x06, + 0x00, 0x0C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xC0, 0x03, 0x80, 0x06, 0x00, + 0x0C, 0x06, 0x30, 0x0C, 0xC0, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x3E, 0x00, + 0x1C, 0x00, 0x38, 0x00, 0x60, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0C, + 0x00, 0x38, 0xFC, 0x70, 0x60, 0xE1, 0x81, 0x86, 0x07, 0x10, 0x0E, 0x40, + 0x1B, 0x80, 0x3F, 0x00, 0xE7, 0x01, 0xCE, 0x03, 0x0C, 0x06, 0x1C, 0x5C, + 0x1D, 0x38, 0x3E, 0x60, 0x38, 0x03, 0x1F, 0x07, 0x07, 0x06, 0x0E, 0x0E, + 0x0E, 0x0C, 0x1C, 0x1C, 0x18, 0x38, 0x38, 0x38, 0x30, 0x70, 0x70, 0x70, + 0x64, 0xE4, 0xE8, 0xF0, 0xE0, 0x00, 0x06, 0x18, 0x1E, 0x3E, 0x3C, 0x3F, + 0x0E, 0x4C, 0x47, 0x0C, 0x8C, 0x8E, 0x1D, 0x0D, 0x0E, 0x1E, 0x1A, 0x0E, + 0x1C, 0x1E, 0x0C, 0x3C, 0x1C, 0x1C, 0x38, 0x38, 0x1C, 0x38, 0x38, 0x1C, + 0x30, 0x38, 0x18, 0x70, 0x30, 0x39, 0x70, 0x70, 0x32, 0x60, 0x70, 0x3C, + 0x60, 0x60, 0x38, 0x06, 0x0E, 0x1F, 0x1F, 0x83, 0x99, 0xC1, 0x98, 0xC1, + 0xD8, 0xE0, 0xE8, 0x70, 0x78, 0x30, 0x38, 0x38, 0x3C, 0x1C, 0x1C, 0x0E, + 0x0E, 0x06, 0x0E, 0x03, 0x17, 0x01, 0xB3, 0x80, 0xF1, 0x80, 0x70, 0x01, + 0xF0, 0x0E, 0x38, 0x38, 0x30, 0xE0, 0x73, 0x80, 0xEE, 0x01, 0xDC, 0x03, + 0xF8, 0x0F, 0xE0, 0x1D, 0xC0, 0x3B, 0x80, 0xE7, 0x03, 0x8E, 0x06, 0x0E, + 0x38, 0x07, 0xC0, 0x00, 0x00, 0xE7, 0xC0, 0x7C, 0xFE, 0x01, 0xD1, 0xF0, + 0x1E, 0x0F, 0x01, 0xC0, 0xF0, 0x38, 0x0F, 0x03, 0x80, 0xF0, 0x38, 0x0E, + 0x03, 0x01, 0xE0, 0x70, 0x1C, 0x07, 0x03, 0xC0, 0x60, 0x78, 0x06, 0x0F, + 0x00, 0xE1, 0xC0, 0x0F, 0xF0, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xC0, + 0x00, 0x1C, 0x00, 0x01, 0x80, 0x00, 0x38, 0x00, 0x0F, 0xF0, 0x00, 0x00, + 0xF7, 0x03, 0xCE, 0x0F, 0x06, 0x1E, 0x06, 0x1C, 0x04, 0x3C, 0x04, 0x78, + 0x04, 0x78, 0x0C, 0xF0, 0x08, 0xF0, 0x18, 0xF0, 0x38, 0xF0, 0xF0, 0xF9, + 0x70, 0x7E, 0x70, 0x3C, 0x70, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x00, + 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x0F, 0xF0, 0x7C, 0x70, 0xE7, 0xC7, 0x4C, + 0x34, 0x01, 0xA0, 0x1E, 0x00, 0xF0, 0x07, 0x00, 0x78, 0x03, 0x80, 0x1C, + 0x00, 0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x00, 0x07, 0x88, 0x63, 0x86, + 0x0C, 0x30, 0x21, 0xC1, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80, 0x1C, + 0x10, 0x60, 0x83, 0x06, 0x18, 0x71, 0x82, 0x78, 0x00, 0x02, 0x03, 0x03, + 0x07, 0xF7, 0xF8, 0xE0, 0x60, 0x70, 0x38, 0x1C, 0x0C, 0x0E, 0x07, 0x03, + 0x01, 0x91, 0xC8, 0xF8, 0x78, 0x00, 0x1C, 0x0D, 0xF8, 0x38, 0x60, 0x70, + 0xC1, 0xC3, 0x83, 0x87, 0x07, 0x0C, 0x1E, 0x38, 0x78, 0x70, 0xB0, 0xE2, + 0x61, 0x8D, 0xC7, 0x33, 0x2C, 0xC6, 0x5F, 0x0F, 0x38, 0x1C, 0x00, 0x18, + 0x1B, 0xE0, 0x73, 0x81, 0xC6, 0x03, 0x18, 0x0C, 0x70, 0x21, 0xC1, 0x83, + 0x0C, 0x0C, 0x20, 0x31, 0x00, 0xC8, 0x03, 0x40, 0x0E, 0x00, 0x30, 0x00, + 0x80, 0x00, 0x18, 0x04, 0x1B, 0xE0, 0x30, 0x71, 0x80, 0xC1, 0xC6, 0x07, + 0x01, 0x1C, 0x2C, 0x08, 0x70, 0xB0, 0x20, 0xC4, 0xC1, 0x03, 0x21, 0x84, + 0x0D, 0x86, 0x20, 0x34, 0x19, 0x00, 0xE0, 0x68, 0x03, 0x81, 0xA0, 0x0C, + 0x07, 0x00, 0x30, 0x18, 0x00, 0x80, 0x40, 0x00, 0x03, 0x07, 0x0F, 0x8F, + 0x13, 0x93, 0x01, 0xB0, 0x01, 0xE0, 0x01, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x01, 0xC0, 0x03, 0xE0, 0x02, 0x60, 0x04, 0x62, 0x08, 0x64, 0xF0, 0x7C, + 0xE0, 0x30, 0x06, 0x06, 0x3F, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x81, + 0x03, 0x82, 0x01, 0x82, 0x01, 0xC4, 0x01, 0xC4, 0x01, 0xC8, 0x00, 0xC8, + 0x00, 0xD0, 0x00, 0xF0, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x80, + 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x78, 0x00, 0x70, 0x00, 0x1F, 0xFC, + 0x7F, 0xE1, 0x01, 0x08, 0x08, 0x00, 0x40, 0x02, 0x00, 0x10, 0x00, 0x80, + 0x06, 0x00, 0x10, 0x00, 0x80, 0x04, 0x00, 0x38, 0x01, 0xF0, 0x0B, 0xE0, + 0x01, 0xC6, 0x03, 0x98, 0x03, 0x80, 0x00, 0x70, 0x0C, 0x01, 0x80, 0x38, + 0x03, 0x80, 0x30, 0x07, 0x00, 0x70, 0x07, 0x00, 0x60, 0x0E, 0x00, 0xE0, + 0x0C, 0x01, 0xC0, 0x1C, 0x07, 0x80, 0x30, 0x04, 0x00, 0x20, 0x03, 0x00, + 0x30, 0x07, 0x00, 0x70, 0x06, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0C, 0x00, + 0xC0, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0xC0, 0x06, + 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x70, 0x07, 0x00, 0x70, 0x06, + 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0C, 0x00, 0x40, 0x04, 0x00, 0xC0, 0x1E, + 0x03, 0x80, 0x38, 0x03, 0x00, 0x70, 0x07, 0x00, 0x70, 0x06, 0x00, 0xE0, + 0x0E, 0x00, 0xC0, 0x1C, 0x01, 0x80, 0x70, 0x00, 0x1E, 0x00, 0x3F, 0xE1, + 0xF8, 0x7F, 0xC0, 0x07, 0x80}; + +const GFXglyph FreeSerifItalic18pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 9, 0, 1}, // 0x20 ' ' + {0, 10, 23, 12, 1, -22}, // 0x21 '!' + {29, 12, 9, 12, 4, -22}, // 0x22 '"' + {43, 19, 23, 17, 0, -22}, // 0x23 '#' + {98, 15, 29, 17, 1, -25}, // 0x24 '$' + {153, 25, 23, 29, 3, -22}, // 0x25 '%' + {225, 22, 23, 27, 3, -22}, // 0x26 '&' + {289, 5, 9, 7, 4, -22}, // 0x27 ''' + {295, 9, 29, 12, 1, -22}, // 0x28 '(' + {328, 9, 29, 12, 1, -22}, // 0x29 ')' + {361, 12, 14, 18, 5, -22}, // 0x2A '*' + {382, 16, 18, 24, 4, -17}, // 0x2B '+' + {418, 5, 8, 9, -1, -2}, // 0x2C ',' + {423, 8, 2, 12, 2, -8}, // 0x2D '-' + {425, 4, 4, 9, 1, -3}, // 0x2E '.' + {427, 16, 23, 10, 0, -22}, // 0x2F '/' + {473, 17, 24, 17, 1, -23}, // 0x30 '0' + {524, 12, 24, 17, 2, -23}, // 0x31 '1' + {560, 16, 23, 17, 1, -22}, // 0x32 '2' + {606, 17, 24, 18, 0, -23}, // 0x33 '3' + {657, 17, 24, 17, 0, -23}, // 0x34 '4' + {708, 16, 23, 18, 0, -22}, // 0x35 '5' + {754, 17, 24, 18, 1, -23}, // 0x36 '6' + {805, 16, 23, 17, 3, -22}, // 0x37 '7' + {851, 16, 24, 18, 1, -23}, // 0x38 '8' + {899, 16, 24, 17, 1, -23}, // 0x39 '9' + {947, 7, 15, 9, 2, -14}, // 0x3A ':' + {961, 9, 20, 9, 1, -14}, // 0x3B ';' + {984, 18, 18, 20, 2, -17}, // 0x3C '<' + {1025, 18, 9, 23, 3, -12}, // 0x3D '=' + {1046, 18, 18, 20, 2, -17}, // 0x3E '>' + {1087, 12, 23, 16, 4, -22}, // 0x3F '?' + {1122, 24, 23, 27, 2, -22}, // 0x40 '@' + {1191, 21, 23, 23, 0, -22}, // 0x41 'A' + {1252, 21, 23, 21, 0, -22}, // 0x42 'B' + {1313, 21, 23, 21, 2, -22}, // 0x43 'C' + {1374, 25, 23, 25, 0, -22}, // 0x44 'D' + {1446, 22, 23, 20, 0, -22}, // 0x45 'E' + {1510, 22, 23, 20, 0, -22}, // 0x46 'F' + {1574, 23, 23, 24, 2, -22}, // 0x47 'G' + {1641, 27, 23, 25, 0, -22}, // 0x48 'H' + {1719, 14, 23, 11, 0, -22}, // 0x49 'I' + {1760, 17, 23, 15, 0, -22}, // 0x4A 'J' + {1809, 25, 23, 22, 0, -22}, // 0x4B 'K' + {1881, 20, 23, 20, 0, -22}, // 0x4C 'L' + {1939, 31, 23, 29, 0, -22}, // 0x4D 'M' + {2029, 26, 23, 24, 0, -22}, // 0x4E 'N' + {2104, 23, 23, 23, 1, -22}, // 0x4F 'O' + {2171, 22, 23, 20, 0, -22}, // 0x50 'P' + {2235, 23, 29, 23, 1, -22}, // 0x51 'Q' + {2319, 21, 23, 22, 0, -22}, // 0x52 'R' + {2380, 17, 23, 16, 0, -22}, // 0x53 'S' + {2429, 20, 23, 21, 3, -22}, // 0x54 'T' + {2487, 23, 23, 25, 4, -22}, // 0x55 'U' + {2554, 21, 23, 23, 5, -22}, // 0x56 'V' + {2615, 29, 23, 31, 5, -22}, // 0x57 'W' + {2699, 24, 23, 23, 0, -22}, // 0x58 'X' + {2768, 19, 23, 21, 4, -22}, // 0x59 'Y' + {2823, 22, 23, 20, 0, -22}, // 0x5A 'Z' + {2887, 13, 28, 14, 1, -22}, // 0x5B '[' + {2933, 12, 23, 17, 4, -22}, // 0x5C '\' + {2968, 12, 28, 14, 1, -22}, // 0x5D ']' + {3010, 15, 13, 15, 0, -22}, // 0x5E '^' + {3035, 18, 2, 17, 0, 3}, // 0x5F '_' + {3040, 6, 6, 9, 5, -22}, // 0x60 '`' + {3045, 15, 15, 17, 1, -14}, // 0x61 'a' + {3074, 16, 24, 17, 1, -23}, // 0x62 'b' + {3122, 13, 15, 14, 1, -14}, // 0x63 'c' + {3147, 17, 24, 18, 1, -23}, // 0x64 'd' + {3198, 13, 15, 14, 1, -14}, // 0x65 'e' + {3223, 20, 31, 15, -3, -23}, // 0x66 'f' + {3301, 16, 22, 15, -1, -14}, // 0x67 'g' + {3345, 16, 24, 17, 1, -23}, // 0x68 'h' + {3393, 9, 23, 9, 1, -22}, // 0x69 'i' + {3419, 15, 30, 10, -3, -22}, // 0x6A 'j' + {3476, 15, 24, 16, 1, -23}, // 0x6B 'k' + {3521, 8, 25, 9, 1, -23}, // 0x6C 'l' + {3546, 24, 15, 25, 0, -14}, // 0x6D 'm' + {3591, 17, 15, 17, 0, -14}, // 0x6E 'n' + {3623, 15, 15, 17, 1, -14}, // 0x6F 'o' + {3652, 20, 22, 16, -3, -14}, // 0x70 'p' + {3707, 16, 22, 17, 1, -14}, // 0x71 'q' + {3751, 13, 15, 13, 1, -14}, // 0x72 'r' + {3776, 13, 15, 12, 0, -14}, // 0x73 's' + {3801, 9, 18, 8, 1, -17}, // 0x74 't' + {3822, 15, 15, 17, 1, -14}, // 0x75 'u' + {3851, 14, 15, 16, 2, -14}, // 0x76 'v' + {3878, 22, 15, 24, 1, -14}, // 0x77 'w' + {3920, 16, 15, 15, -1, -14}, // 0x78 'x' + {3950, 16, 22, 16, 0, -14}, // 0x79 'y' + {3994, 14, 18, 14, 0, -14}, // 0x7A 'z' + {4026, 12, 30, 14, 2, -23}, // 0x7B '{' + {4071, 2, 23, 10, 4, -22}, // 0x7C '|' + {4077, 12, 31, 14, 0, -24}, // 0x7D '}' + {4124, 17, 4, 19, 1, -10}}; // 0x7E '~' + +const GFXfont FreeSerifItalic18pt7b PROGMEM = { + (uint8_t *)FreeSerifItalic18pt7bBitmaps, + (GFXglyph *)FreeSerifItalic18pt7bGlyphs, 0x20, 0x7E, 42}; + +// Approx. 4805 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic24pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic24pt7b.h new file mode 100644 index 0000000..e14a151 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic24pt7b.h @@ -0,0 +1,736 @@ +const uint8_t FreeSerifItalic24pt7bBitmaps[] PROGMEM = { + 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x1C, + 0x01, 0xC0, 0x3C, 0x03, 0x80, 0x38, 0x03, 0x80, 0x30, 0x07, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x04, 0x00, 0x40, 0x0C, 0x00, 0x80, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0xF8, 0x0F, 0x80, 0xF8, 0x07, 0x00, + 0x38, 0x1D, 0xE0, 0x77, 0x83, 0xDC, 0x0E, 0x70, 0x39, 0xC1, 0xEE, 0x07, + 0x38, 0x1C, 0xC0, 0x63, 0x01, 0x8C, 0x06, 0x20, 0x10, 0x00, 0x06, 0x03, + 0x00, 0x07, 0x03, 0x80, 0x03, 0x81, 0xC0, 0x03, 0x81, 0xC0, 0x01, 0xC0, + 0xE0, 0x00, 0xE0, 0x70, 0x00, 0xE0, 0x70, 0x00, 0x70, 0x38, 0x00, 0x30, + 0x18, 0x00, 0x38, 0x1C, 0x03, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xF0, 0x0E, + 0x07, 0x00, 0x06, 0x03, 0x00, 0x07, 0x03, 0x80, 0x03, 0x81, 0xC0, 0x03, + 0x81, 0xC0, 0x01, 0xC0, 0xE0, 0x00, 0xE0, 0x70, 0x1F, 0xFF, 0xFF, 0x8F, + 0xFF, 0xFF, 0x80, 0x70, 0x38, 0x00, 0x38, 0x1C, 0x00, 0x1C, 0x0C, 0x00, + 0x1C, 0x0E, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x07, 0x03, 0x80, + 0x03, 0x81, 0xC0, 0x03, 0x81, 0xC0, 0x01, 0xC0, 0xE0, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x00, 0xFF, 0x80, 0x1C, 0x2F, 0x01, + 0x83, 0x3C, 0x1C, 0x18, 0xE1, 0xC0, 0xC3, 0x0E, 0x06, 0x18, 0x70, 0x60, + 0x83, 0x83, 0x04, 0x1E, 0x18, 0x00, 0xF8, 0xC0, 0x03, 0xEC, 0x00, 0x0F, + 0xE0, 0x00, 0x3F, 0x00, 0x00, 0xFC, 0x00, 0x03, 0xF0, 0x00, 0x0F, 0xC0, + 0x00, 0x7F, 0x00, 0x03, 0x7C, 0x00, 0x19, 0xE0, 0x01, 0x87, 0x80, 0x0C, + 0x3C, 0x00, 0x60, 0xE2, 0x03, 0x07, 0x10, 0x30, 0x39, 0x81, 0x81, 0xCE, + 0x0C, 0x0C, 0x70, 0x60, 0xE3, 0xC6, 0x06, 0x0F, 0x30, 0x60, 0x1F, 0x9E, + 0x00, 0x3F, 0x80, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, + 0x80, 0x00, 0x01, 0xF0, 0x00, 0xC0, 0x03, 0xFE, 0x01, 0xE0, 0x03, 0xC7, + 0x83, 0xE0, 0x03, 0xC0, 0x7F, 0x60, 0x03, 0xC0, 0x20, 0x70, 0x01, 0xC0, + 0x10, 0x30, 0x01, 0xE0, 0x08, 0x38, 0x00, 0xE0, 0x04, 0x18, 0x00, 0xF0, + 0x02, 0x1C, 0x00, 0x70, 0x02, 0x0C, 0x00, 0x38, 0x01, 0x0E, 0x00, 0x1C, + 0x01, 0x8E, 0x00, 0x0E, 0x00, 0x86, 0x00, 0x07, 0x00, 0x87, 0x03, 0xE1, + 0x80, 0xC3, 0x07, 0xFC, 0xE1, 0xC3, 0x87, 0xC6, 0x3F, 0x81, 0x87, 0x81, + 0x8F, 0x81, 0xC7, 0x80, 0x40, 0x00, 0xC3, 0xC0, 0x20, 0x00, 0xE3, 0xC0, + 0x10, 0x00, 0x61, 0xC0, 0x08, 0x00, 0x61, 0xE0, 0x04, 0x00, 0x70, 0xF0, + 0x06, 0x00, 0x30, 0x70, 0x02, 0x00, 0x38, 0x38, 0x03, 0x00, 0x18, 0x1C, + 0x01, 0x00, 0x1C, 0x0E, 0x01, 0x80, 0x0C, 0x07, 0x01, 0x80, 0x0E, 0x01, + 0xC3, 0x80, 0x06, 0x00, 0x7F, 0x80, 0x06, 0x00, 0x1F, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x71, + 0xC0, 0x00, 0x01, 0xC3, 0x80, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x38, 0x38, + 0x00, 0x01, 0xE0, 0xE0, 0x00, 0x07, 0x87, 0x00, 0x00, 0x1E, 0x18, 0x00, + 0x00, 0x78, 0xC0, 0x00, 0x01, 0xE6, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x7F, + 0xC1, 0xFE, 0x03, 0x9F, 0x03, 0xE0, 0x3C, 0x3C, 0x07, 0x01, 0xE0, 0xF8, + 0x1C, 0x0F, 0x03, 0xE0, 0xE0, 0x7C, 0x07, 0x83, 0x01, 0xE0, 0x1F, 0x1C, + 0x07, 0x80, 0x7C, 0x60, 0x3E, 0x00, 0xFB, 0x00, 0xF8, 0x03, 0xFC, 0x03, + 0xE0, 0x07, 0xE0, 0x0F, 0x80, 0x1F, 0x00, 0x3F, 0x00, 0x3E, 0x00, 0x7C, + 0x00, 0xFC, 0x01, 0xF8, 0x0F, 0xF0, 0x03, 0xF0, 0xF3, 0xF0, 0x87, 0xFF, + 0x07, 0xFC, 0x07, 0xF0, 0x07, 0xC0, 0x39, 0xDE, 0xE7, 0x3B, 0x9C, 0xC6, + 0x31, 0x00, 0x00, 0x10, 0x01, 0x00, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x1C, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x07, 0x00, 0x78, 0x03, 0x80, 0x3C, + 0x01, 0xE0, 0x0E, 0x00, 0x70, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0E, 0x00, + 0x70, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, + 0x00, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x01, 0x80, 0x0C, 0x00, 0x60, + 0x01, 0x00, 0x0C, 0x00, 0x20, 0x00, 0x00, 0x80, 0x06, 0x00, 0x10, 0x00, + 0x80, 0x06, 0x00, 0x30, 0x00, 0xC0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0C, + 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, + 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xC0, 0x1E, 0x00, 0xF0, 0x07, + 0x80, 0x38, 0x03, 0xC0, 0x1C, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, + 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0xE1, 0x07, 0xE1, 0x0F, + 0xF1, 0x1F, 0x19, 0x30, 0x07, 0xC0, 0x03, 0x80, 0x0D, 0x60, 0x79, 0x3C, + 0xF1, 0x1F, 0xE1, 0x0F, 0xE1, 0x07, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, + 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, + 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x7C, 0xF9, + 0xF1, 0xE1, 0xC3, 0x0C, 0x10, 0xC1, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, + 0x77, 0xFF, 0xF7, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x80, 0x00, 0x3C, + 0x00, 0x01, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, + 0xF0, 0x00, 0x07, 0x00, 0x00, 0x78, 0x00, 0x03, 0x80, 0x00, 0x3C, 0x00, + 0x01, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x70, + 0x00, 0x07, 0x80, 0x00, 0x38, 0x00, 0x03, 0x80, 0x00, 0x3C, 0x00, 0x01, + 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x70, 0x00, + 0x07, 0x80, 0x00, 0x38, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xE0, + 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x03, 0x86, + 0x00, 0x30, 0x18, 0x03, 0x00, 0xC0, 0x38, 0x03, 0x03, 0x80, 0x18, 0x38, + 0x00, 0xC1, 0xC0, 0x07, 0x1C, 0x00, 0x38, 0xE0, 0x01, 0xCF, 0x00, 0x0E, + 0x70, 0x00, 0x77, 0x80, 0x07, 0xBC, 0x00, 0x3D, 0xE0, 0x01, 0xEE, 0x00, + 0x0F, 0xF0, 0x00, 0x77, 0x80, 0x07, 0xBC, 0x00, 0x3D, 0xC0, 0x01, 0xCE, + 0x00, 0x1E, 0x70, 0x00, 0xF3, 0x80, 0x07, 0x1C, 0x00, 0x78, 0xE0, 0x03, + 0x83, 0x00, 0x38, 0x18, 0x03, 0x80, 0xE0, 0x18, 0x03, 0x01, 0x80, 0x0C, + 0x38, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0xC0, 0x3F, 0xE0, + 0x01, 0xF0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x1E, + 0x00, 0x0F, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, + 0xF0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x1E, 0x00, + 0x0F, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x01, 0xE0, + 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3F, 0x01, 0xFF, + 0xF0, 0x00, 0x3F, 0x00, 0x07, 0xFE, 0x00, 0x7F, 0xF8, 0x07, 0x07, 0xE0, + 0x60, 0x1F, 0x06, 0x00, 0x7C, 0x20, 0x01, 0xE0, 0x00, 0x0F, 0x00, 0x00, + 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x01, 0xE0, 0x00, 0x0E, 0x00, + 0x00, 0xF0, 0x00, 0x07, 0x00, 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x03, + 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x30, 0x01, 0x03, 0x00, 0x08, + 0x30, 0x00, 0xC3, 0xFF, 0xFC, 0x3F, 0xFF, 0xE3, 0xFF, 0xFE, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0xFF, 0xC0, 0x06, 0x0F, 0x80, 0x30, 0x1E, 0x01, 0x80, + 0x3C, 0x00, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x78, + 0x00, 0x01, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0xF0, 0x00, 0x0E, 0x00, 0x01, + 0xF8, 0x00, 0x3F, 0xF8, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xC0, 0x00, 0x0F, + 0x80, 0x00, 0x3E, 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x80, + 0x00, 0x1E, 0x00, 0x00, 0x70, 0x00, 0x01, 0xC0, 0x00, 0x07, 0x00, 0x00, + 0x38, 0x00, 0x00, 0xC0, 0x70, 0x06, 0x03, 0xF8, 0x70, 0x07, 0xFF, 0x00, + 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x00, 0x03, 0xC0, + 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x37, 0x80, 0x00, + 0xDC, 0x00, 0x06, 0x70, 0x00, 0x33, 0xC0, 0x01, 0x8F, 0x00, 0x0C, 0x38, + 0x00, 0x60, 0xE0, 0x03, 0x07, 0x80, 0x18, 0x1E, 0x00, 0xC0, 0x70, 0x06, + 0x03, 0xC0, 0x30, 0x0F, 0x01, 0x80, 0x38, 0x0C, 0x00, 0xE0, 0x70, 0x07, + 0x81, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFE, 0x00, 0x0F, 0x00, + 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x1E, 0x00, 0x00, + 0x70, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0xFF, + 0xF0, 0x07, 0xFF, 0x80, 0x10, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x70, 0x00, 0x01, 0xF8, 0x00, 0x0F, 0xF0, 0x00, 0x3F, + 0xF0, 0x00, 0x1F, 0xE0, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x78, 0x00, 0x01, 0xE0, 0x00, 0x03, 0x80, 0x00, 0x0E, 0x00, + 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x00, 0x0C, 0x00, 0x00, + 0x70, 0x00, 0x01, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x80, + 0x70, 0x0E, 0x03, 0xF0, 0xE0, 0x07, 0xFF, 0x00, 0x0F, 0xE0, 0x00, 0x00, + 0x00, 0x0E, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x03, + 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xC0, + 0x00, 0x0F, 0x80, 0x00, 0x3E, 0x00, 0x00, 0xF9, 0xF8, 0x01, 0xFF, 0xFC, + 0x07, 0xE0, 0x7C, 0x0F, 0x80, 0x7C, 0x3E, 0x00, 0x78, 0x78, 0x00, 0x78, + 0xF0, 0x00, 0xF3, 0xC0, 0x01, 0xE7, 0x80, 0x03, 0xCF, 0x00, 0x07, 0x9C, + 0x00, 0x0F, 0x38, 0x00, 0x3E, 0x70, 0x00, 0x78, 0xE0, 0x00, 0xF1, 0xC0, + 0x03, 0xC1, 0x80, 0x07, 0x83, 0x00, 0x1E, 0x03, 0x00, 0x38, 0x06, 0x01, + 0xE0, 0x03, 0x07, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0xFF, 0xF9, 0xFF, 0xFF, + 0xCF, 0xFF, 0xFC, 0xE0, 0x00, 0xCC, 0x00, 0x0E, 0x40, 0x00, 0x60, 0x00, + 0x07, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x38, 0x00, 0x01, 0x80, + 0x00, 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0xE0, 0x00, 0x07, + 0x00, 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0x38, 0x00, 0x03, 0x80, 0x00, + 0x1C, 0x00, 0x01, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, + 0x00, 0x70, 0x00, 0x07, 0x00, 0x00, 0x78, 0x00, 0x03, 0x80, 0x00, 0x38, + 0x00, 0x01, 0xC0, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x03, 0x83, + 0x80, 0x1C, 0x03, 0x00, 0xE0, 0x0E, 0x07, 0x00, 0x1C, 0x1C, 0x00, 0x70, + 0x70, 0x01, 0xC1, 0xC0, 0x07, 0x07, 0x80, 0x1C, 0x1E, 0x00, 0xE0, 0x3C, + 0x07, 0x80, 0xFC, 0x38, 0x01, 0xFB, 0xC0, 0x03, 0xF8, 0x00, 0x0F, 0xE0, + 0x00, 0x7F, 0xC0, 0x07, 0x1F, 0x80, 0x78, 0x3F, 0x03, 0x80, 0x7C, 0x1E, + 0x00, 0xF8, 0x70, 0x01, 0xE3, 0x80, 0x03, 0xCE, 0x00, 0x07, 0x38, 0x00, + 0x1C, 0xE0, 0x00, 0x73, 0x80, 0x01, 0xCE, 0x00, 0x06, 0x1C, 0x00, 0x38, + 0x70, 0x01, 0xC0, 0xE0, 0x0E, 0x01, 0xE0, 0xE0, 0x01, 0xFE, 0x00, 0x00, + 0x1F, 0x80, 0x03, 0xC3, 0x00, 0x1C, 0x02, 0x00, 0xE0, 0x0C, 0x07, 0x00, + 0x18, 0x3C, 0x00, 0x60, 0xE0, 0x01, 0xC7, 0x80, 0x07, 0x1E, 0x00, 0x1C, + 0xF0, 0x00, 0x73, 0xC0, 0x01, 0xCF, 0x00, 0x07, 0x3C, 0x00, 0x3C, 0xF0, + 0x00, 0xF3, 0xC0, 0x03, 0xCF, 0x00, 0x1E, 0x1E, 0x00, 0x78, 0x7C, 0x03, + 0xE0, 0xF8, 0x3F, 0x01, 0xFF, 0xBC, 0x03, 0xF1, 0xE0, 0x00, 0x0F, 0x80, + 0x00, 0x3C, 0x00, 0x01, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x7C, 0x00, 0x03, + 0xE0, 0x00, 0x1F, 0x00, 0x00, 0xF0, 0x00, 0x0F, 0x80, 0x00, 0x78, 0x00, + 0x0F, 0x80, 0x00, 0xE0, 0x00, 0x00, 0x07, 0x07, 0xC3, 0xE1, 0xF0, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x3E, 0x1F, 0x0F, 0x83, 0x80, 0x01, 0xC0, 0x7C, 0x0F, 0x81, + 0xF0, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x07, 0x80, 0xF8, 0x1F, 0x01, 0xE0, + 0x1C, 0x03, 0x00, 0xC0, 0x18, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0xF8, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0xC0, + 0x01, 0xFC, 0x00, 0x0F, 0xE0, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x00, 0x3F, + 0xC0, 0x01, 0xFC, 0x00, 0x07, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x1F, 0x80, + 0x00, 0x3F, 0xC0, 0x00, 0x1F, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xF8, + 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x1F, + 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x07, 0x00, 0x00, 0x02, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x0F, 0xF0, 0x00, 0x03, 0xFC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x07, 0xF0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x1F, 0xE0, + 0x00, 0x7F, 0x80, 0x01, 0xFC, 0x00, 0x07, 0xF0, 0x00, 0x3F, 0xC0, 0x00, + 0xFF, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x03, 0xF0, 0x06, 0x1C, 0x0C, 0x0E, 0x1C, 0x06, 0x1C, 0x07, 0x1C, 0x07, + 0x1C, 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x3C, + 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x03, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xF8, 0x00, + 0xF8, 0x00, 0xF8, 0x00, 0x70, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x3F, 0x01, 0xF0, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x7C, + 0x00, 0x07, 0x80, 0x7C, 0x00, 0x00, 0xE0, 0x3C, 0x00, 0x00, 0x38, 0x3C, + 0x00, 0x00, 0x0C, 0x3C, 0x00, 0x78, 0x07, 0x1E, 0x00, 0xFE, 0xE1, 0x9E, + 0x00, 0xF1, 0xF0, 0xEF, 0x00, 0xE0, 0xF0, 0x37, 0x80, 0xE0, 0x38, 0x1F, + 0x80, 0x70, 0x1C, 0x0F, 0xC0, 0x70, 0x1E, 0x07, 0xE0, 0x38, 0x0F, 0x03, + 0xF0, 0x18, 0x07, 0x01, 0xF8, 0x1C, 0x03, 0x80, 0xFC, 0x0E, 0x01, 0xC0, + 0xDE, 0x07, 0x01, 0xE0, 0x6F, 0x03, 0x80, 0xE0, 0x73, 0xC1, 0xC0, 0xF0, + 0x31, 0xE0, 0xF0, 0xF8, 0x30, 0xF0, 0x38, 0xDC, 0x30, 0x3C, 0x1F, 0xC7, + 0xF0, 0x0E, 0x07, 0x81, 0xF0, 0x07, 0x80, 0x00, 0x00, 0x01, 0xE0, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x00, 0x03, 0xF0, + 0x0F, 0x80, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x03, 0xF0, + 0x00, 0x00, 0x37, 0x80, 0x00, 0x03, 0x3C, 0x00, 0x00, 0x19, 0xE0, 0x00, + 0x01, 0x8F, 0x80, 0x00, 0x08, 0x7C, 0x00, 0x00, 0xC3, 0xE0, 0x00, 0x0C, + 0x0F, 0x00, 0x00, 0x60, 0x78, 0x00, 0x06, 0x03, 0xC0, 0x00, 0x20, 0x1F, + 0x00, 0x03, 0x00, 0xF8, 0x00, 0x3F, 0xFF, 0xC0, 0x01, 0xFF, 0xFE, 0x00, + 0x18, 0x00, 0xF0, 0x00, 0xC0, 0x07, 0x80, 0x0C, 0x00, 0x3E, 0x00, 0xE0, + 0x01, 0xF0, 0x06, 0x00, 0x0F, 0x80, 0x70, 0x00, 0x3C, 0x03, 0x00, 0x01, + 0xE0, 0x38, 0x00, 0x0F, 0x83, 0xC0, 0x00, 0x7C, 0x3E, 0x00, 0x07, 0xF3, + 0xFC, 0x01, 0xFF, 0xE0, 0x03, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xF8, 0x00, + 0x3E, 0x07, 0xC0, 0x03, 0xE0, 0x3E, 0x00, 0x3E, 0x01, 0xF0, 0x03, 0xC0, + 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x78, 0x01, 0xF0, + 0x07, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x7C, 0x00, 0xF0, + 0x3F, 0x00, 0x1F, 0xFF, 0x80, 0x01, 0xFF, 0xFC, 0x00, 0x1F, 0x07, 0xE0, + 0x01, 0xE0, 0x1F, 0x00, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x07, 0xC0, 0x3C, + 0x00, 0x7C, 0x03, 0xC0, 0x07, 0xC0, 0x7C, 0x00, 0x7C, 0x07, 0xC0, 0x07, + 0xC0, 0x78, 0x00, 0x7C, 0x0F, 0x80, 0x0F, 0x80, 0xF8, 0x00, 0xF8, 0x0F, + 0x00, 0x1F, 0x00, 0xF0, 0x03, 0xE0, 0x1F, 0x81, 0xFC, 0x03, 0xFF, 0xFF, + 0x80, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x01, 0xFE, 0x04, 0x00, 0x3F, 0xFF, + 0xE0, 0x03, 0xF0, 0x1F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0xF0, 0x00, 0x78, + 0x0F, 0x80, 0x00, 0xE0, 0x3C, 0x00, 0x03, 0x81, 0xF0, 0x00, 0x04, 0x0F, + 0x80, 0x00, 0x10, 0x7C, 0x00, 0x00, 0x41, 0xF0, 0x00, 0x00, 0x0F, 0x80, + 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x03, + 0xE0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x01, 0xF0, 0x00, 0x02, 0x07, 0xC0, 0x00, 0x18, 0x0F, 0x80, + 0x00, 0xC0, 0x3E, 0x00, 0x06, 0x00, 0x7C, 0x00, 0x70, 0x00, 0xFC, 0x07, + 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x0F, 0xC0, 0xFC, 0x00, 0x07, 0xC0, + 0x1F, 0x00, 0x03, 0xE0, 0x07, 0xC0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xF0, + 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0x0F, 0x00, 0x3C, + 0x00, 0x07, 0xC0, 0x3E, 0x00, 0x03, 0xE0, 0x1F, 0x00, 0x01, 0xF0, 0x0F, + 0x00, 0x00, 0xF8, 0x0F, 0x80, 0x00, 0x7C, 0x07, 0xC0, 0x00, 0x3E, 0x03, + 0xE0, 0x00, 0x1F, 0x01, 0xE0, 0x00, 0x1F, 0x81, 0xF0, 0x00, 0x0F, 0x80, + 0xF8, 0x00, 0x07, 0xC0, 0x78, 0x00, 0x03, 0xE0, 0x3C, 0x00, 0x03, 0xE0, + 0x3E, 0x00, 0x01, 0xF0, 0x1F, 0x00, 0x01, 0xF0, 0x0F, 0x00, 0x01, 0xF0, + 0x0F, 0x80, 0x01, 0xF8, 0x07, 0xC0, 0x01, 0xF0, 0x03, 0xE0, 0x01, 0xF0, + 0x01, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x0F, 0xE0, 0x01, 0xFF, 0xFF, 0xC0, + 0x03, 0xFF, 0xFE, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, + 0xC0, 0x0F, 0x80, 0x1E, 0x00, 0x7C, 0x00, 0x30, 0x03, 0xE0, 0x01, 0x00, + 0x1E, 0x00, 0x08, 0x01, 0xF0, 0x00, 0x40, 0x0F, 0x80, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x03, 0xC0, 0x10, 0x00, 0x3E, 0x01, 0x80, 0x01, 0xF0, 0x08, + 0x00, 0x0F, 0x01, 0xC0, 0x00, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xF0, 0x00, + 0x3E, 0x07, 0x00, 0x01, 0xE0, 0x18, 0x00, 0x1F, 0x00, 0xC0, 0x00, 0xF8, + 0x04, 0x00, 0x07, 0x80, 0x20, 0x00, 0x3C, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x08, 0x0F, 0x80, 0x00, 0xC0, + 0x7C, 0x00, 0x0E, 0x03, 0xC0, 0x00, 0xE0, 0x1E, 0x00, 0x0F, 0x01, 0xF8, + 0x03, 0xF8, 0x1F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, + 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xC0, 0x0F, 0x80, 0x1E, 0x00, 0x7C, 0x00, + 0x30, 0x03, 0xE0, 0x01, 0x00, 0x1E, 0x00, 0x08, 0x01, 0xF0, 0x00, 0x40, + 0x0F, 0x80, 0x02, 0x00, 0x7C, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x3E, + 0x00, 0x80, 0x01, 0xF0, 0x0C, 0x00, 0x0F, 0x00, 0xC0, 0x00, 0xF8, 0x0E, + 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0x00, 0x01, 0xE0, 0x18, 0x00, + 0x1F, 0x00, 0xC0, 0x00, 0xF8, 0x06, 0x00, 0x07, 0xC0, 0x20, 0x00, 0x3C, + 0x01, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0x0F, 0x80, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xFF, + 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x02, 0x00, 0x1F, 0xFF, 0x8C, 0x00, + 0xFC, 0x07, 0xF8, 0x03, 0xE0, 0x03, 0xF0, 0x0F, 0x00, 0x03, 0xC0, 0x3C, + 0x00, 0x03, 0x80, 0xF0, 0x00, 0x07, 0x03, 0xC0, 0x00, 0x0E, 0x0F, 0x80, + 0x00, 0x08, 0x3E, 0x00, 0x00, 0x10, 0x7C, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x00, 0x03, 0xE0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x3F, + 0xFF, 0xE0, 0x00, 0x0F, 0xE7, 0xC0, 0x00, 0x0F, 0x0F, 0x80, 0x00, 0x1E, + 0x1F, 0x00, 0x00, 0x7C, 0x3E, 0x00, 0x00, 0xF0, 0x7C, 0x00, 0x01, 0xE0, + 0x78, 0x00, 0x03, 0xC0, 0xF8, 0x00, 0x0F, 0x01, 0xF0, 0x00, 0x1E, 0x01, + 0xF0, 0x00, 0x3C, 0x01, 0xE0, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x01, + 0xF8, 0x0F, 0x80, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x03, + 0xFF, 0xE0, 0x7F, 0xF0, 0x07, 0xF8, 0x01, 0xFC, 0x00, 0x3E, 0x00, 0x0F, + 0x80, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, + 0x00, 0xF0, 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, + 0x78, 0x00, 0x1E, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0xF8, 0x00, 0x3E, + 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, 0x00, + 0x03, 0xC0, 0x01, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0x80, 0x01, + 0xE0, 0x00, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF8, + 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x7C, 0x00, + 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x0F, + 0x80, 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x03, 0xC0, + 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x03, 0xF8, 0x00, + 0xFE, 0x00, 0xFF, 0xE0, 0x7F, 0xFC, 0x00, 0x01, 0xFF, 0xC0, 0x1F, 0xE0, + 0x03, 0xE0, 0x00, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x07, 0xC0, 0x01, + 0xF0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF0, + 0x00, 0x7C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xE0, 0x00, 0xF8, 0x00, + 0x3E, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x01, 0xF0, 0x00, 0x7C, 0x00, 0x1E, + 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x1F, 0x00, + 0x0F, 0xE0, 0x0F, 0xFE, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x07, 0xF0, 0x00, + 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1E, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0xF8, 0x00, 0x01, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x78, 0x00, 0x01, 0xF0, 0x00, 0x03, 0xE0, + 0x00, 0x07, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x78, 0x00, + 0x00, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x0F, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xF0, 0x01, 0xC1, 0xE0, 0x07, 0xC7, + 0x80, 0x0F, 0x8F, 0x00, 0x1F, 0x3C, 0x00, 0x1F, 0xF0, 0x00, 0x0F, 0x80, + 0x00, 0x01, 0xFF, 0xE1, 0xFF, 0x80, 0x3F, 0xC0, 0x1F, 0x80, 0x0F, 0x80, + 0x0F, 0x00, 0x07, 0xC0, 0x0F, 0x00, 0x03, 0xC0, 0x0F, 0x00, 0x01, 0xE0, + 0x0E, 0x00, 0x01, 0xF0, 0x0E, 0x00, 0x00, 0xF8, 0x0E, 0x00, 0x00, 0x78, + 0x1C, 0x00, 0x00, 0x3C, 0x1C, 0x00, 0x00, 0x3E, 0x3C, 0x00, 0x00, 0x1F, + 0x38, 0x00, 0x00, 0x0F, 0x38, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x07, + 0xFE, 0x00, 0x00, 0x03, 0xDF, 0x00, 0x00, 0x01, 0xE7, 0xC0, 0x00, 0x01, + 0xF3, 0xE0, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x78, 0x3C, 0x00, 0x00, + 0x3C, 0x1F, 0x00, 0x00, 0x3E, 0x07, 0xC0, 0x00, 0x1F, 0x03, 0xE0, 0x00, + 0x0F, 0x00, 0xF8, 0x00, 0x0F, 0x80, 0x3C, 0x00, 0x07, 0xC0, 0x1F, 0x00, + 0x03, 0xC0, 0x07, 0x80, 0x01, 0xE0, 0x03, 0xE0, 0x01, 0xF0, 0x01, 0xF8, + 0x01, 0xFC, 0x01, 0xFE, 0x03, 0xFF, 0xC3, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, + 0x00, 0x0F, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, + 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x80, + 0x00, 0x01, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x01, + 0xF0, 0x00, 0x08, 0x3C, 0x00, 0x03, 0x0F, 0x80, 0x00, 0x41, 0xF0, 0x00, + 0x18, 0x3C, 0x00, 0x07, 0x07, 0x80, 0x01, 0xC1, 0xF8, 0x01, 0xF8, 0x7F, + 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0x00, 0x00, 0x3F, 0xC0, + 0x0F, 0xC0, 0x00, 0x1F, 0xC0, 0x01, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0xFC, + 0x00, 0x03, 0xF0, 0x00, 0x3F, 0x00, 0x01, 0xFC, 0x00, 0x0F, 0xC0, 0x00, + 0xFF, 0x00, 0x02, 0xF0, 0x00, 0x37, 0x80, 0x01, 0xBC, 0x00, 0x19, 0xE0, + 0x00, 0x6F, 0x80, 0x0E, 0xF8, 0x00, 0x1B, 0xE0, 0x03, 0x3E, 0x00, 0x04, + 0x78, 0x01, 0x8F, 0x00, 0x03, 0x1E, 0x00, 0xE7, 0xC0, 0x00, 0xC7, 0x80, + 0x31, 0xF0, 0x00, 0x21, 0xE0, 0x18, 0x78, 0x00, 0x18, 0x78, 0x0E, 0x1E, + 0x00, 0x06, 0x1E, 0x03, 0x0F, 0x80, 0x01, 0x87, 0x81, 0x83, 0xE0, 0x00, + 0x41, 0xF0, 0xE0, 0xF0, 0x00, 0x30, 0x7C, 0x30, 0x3C, 0x00, 0x0C, 0x0F, + 0x18, 0x1F, 0x00, 0x03, 0x03, 0xCE, 0x07, 0xC0, 0x01, 0x80, 0xF3, 0x01, + 0xE0, 0x00, 0x60, 0x3D, 0x80, 0xF8, 0x00, 0x18, 0x0F, 0xE0, 0x3E, 0x00, + 0x0C, 0x03, 0xF0, 0x0F, 0x00, 0x03, 0x00, 0xF8, 0x03, 0xC0, 0x00, 0xC0, + 0x3E, 0x01, 0xF0, 0x00, 0x70, 0x0F, 0x00, 0x7C, 0x00, 0x1C, 0x01, 0x80, + 0x3F, 0x00, 0x0F, 0x80, 0x60, 0x1F, 0xC0, 0x0F, 0xF8, 0x10, 0x1F, 0xFE, + 0x00, 0x03, 0xFC, 0x00, 0x3F, 0xE0, 0x1F, 0xC0, 0x01, 0xF8, 0x00, 0xF8, + 0x00, 0x1C, 0x00, 0x1F, 0x00, 0x03, 0x80, 0x03, 0xF0, 0x00, 0x60, 0x00, + 0x7E, 0x00, 0x0C, 0x00, 0x0B, 0xE0, 0x03, 0x80, 0x03, 0x7C, 0x00, 0x60, + 0x00, 0x67, 0x80, 0x0C, 0x00, 0x0C, 0xF8, 0x03, 0x80, 0x03, 0x0F, 0x00, + 0x70, 0x00, 0x61, 0xF0, 0x0C, 0x00, 0x0C, 0x3E, 0x01, 0x80, 0x01, 0x83, + 0xC0, 0x70, 0x00, 0x60, 0x7C, 0x0C, 0x00, 0x0C, 0x07, 0x81, 0x80, 0x01, + 0x80, 0xF8, 0x30, 0x00, 0x60, 0x0F, 0x0E, 0x00, 0x0C, 0x01, 0xE1, 0x80, + 0x01, 0x80, 0x3E, 0x30, 0x00, 0x30, 0x03, 0xCE, 0x00, 0x0C, 0x00, 0x7D, + 0x80, 0x01, 0x80, 0x07, 0xB0, 0x00, 0x30, 0x00, 0xF6, 0x00, 0x0E, 0x00, + 0x1F, 0xC0, 0x01, 0x80, 0x01, 0xF0, 0x00, 0x30, 0x00, 0x3E, 0x00, 0x0E, + 0x00, 0x03, 0xC0, 0x01, 0xC0, 0x00, 0x70, 0x00, 0x7C, 0x00, 0x06, 0x00, + 0x3F, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x01, 0xF0, 0x7C, 0x00, 0x0F, 0x00, 0x78, + 0x00, 0x78, 0x00, 0xF0, 0x07, 0xC0, 0x03, 0xE0, 0x3E, 0x00, 0x07, 0x81, + 0xF0, 0x00, 0x1E, 0x07, 0xC0, 0x00, 0x7C, 0x3E, 0x00, 0x01, 0xF1, 0xF0, + 0x00, 0x07, 0xC7, 0xC0, 0x00, 0x1F, 0x3F, 0x00, 0x00, 0x7C, 0xF8, 0x00, + 0x01, 0xF7, 0xE0, 0x00, 0x0F, 0xDF, 0x00, 0x00, 0x3F, 0x7C, 0x00, 0x00, + 0xFB, 0xF0, 0x00, 0x07, 0xEF, 0xC0, 0x00, 0x1F, 0xBE, 0x00, 0x00, 0x7C, + 0xF8, 0x00, 0x03, 0xF3, 0xE0, 0x00, 0x0F, 0x8F, 0x80, 0x00, 0x3E, 0x3E, + 0x00, 0x01, 0xF0, 0xF8, 0x00, 0x0F, 0x81, 0xE0, 0x00, 0x3E, 0x07, 0x80, + 0x01, 0xF0, 0x1F, 0x00, 0x0F, 0x80, 0x3C, 0x00, 0x7C, 0x00, 0x78, 0x03, + 0xC0, 0x00, 0xF8, 0x3E, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x01, 0xFC, 0x00, + 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x0F, 0x81, 0xF8, + 0x00, 0x7C, 0x03, 0xE0, 0x03, 0xE0, 0x1F, 0x00, 0x1E, 0x00, 0x7C, 0x01, + 0xF0, 0x03, 0xE0, 0x0F, 0x80, 0x1F, 0x00, 0x78, 0x00, 0xF8, 0x03, 0xC0, + 0x07, 0xC0, 0x3E, 0x00, 0x3C, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0x00, 0x3E, + 0x00, 0xF8, 0x03, 0xF0, 0x07, 0xC0, 0x7E, 0x00, 0x3F, 0xFF, 0xE0, 0x01, + 0xEF, 0xF8, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x07, 0x80, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1F, + 0xFE, 0x00, 0x00, 0xF0, 0x7C, 0x00, 0x0F, 0x00, 0x78, 0x00, 0x78, 0x00, + 0xF0, 0x03, 0xC0, 0x03, 0xE0, 0x1E, 0x00, 0x07, 0x80, 0xF0, 0x00, 0x1E, + 0x07, 0xC0, 0x00, 0x7C, 0x3E, 0x00, 0x01, 0xF1, 0xF8, 0x00, 0x07, 0xC7, + 0xC0, 0x00, 0x1F, 0x3F, 0x00, 0x00, 0x7C, 0xF8, 0x00, 0x01, 0xF7, 0xE0, + 0x00, 0x0F, 0xDF, 0x80, 0x00, 0x3F, 0x7C, 0x00, 0x00, 0xFB, 0xF0, 0x00, + 0x03, 0xEF, 0xC0, 0x00, 0x1F, 0xBE, 0x00, 0x00, 0x7C, 0xF8, 0x00, 0x01, + 0xF3, 0xE0, 0x00, 0x0F, 0x8F, 0x80, 0x00, 0x3E, 0x3E, 0x00, 0x01, 0xF0, + 0xF8, 0x00, 0x07, 0xC3, 0xE0, 0x00, 0x3E, 0x07, 0x80, 0x01, 0xF0, 0x1F, + 0x00, 0x07, 0x80, 0x3C, 0x00, 0x3C, 0x00, 0xF8, 0x01, 0xE0, 0x01, 0xE0, + 0x1E, 0x00, 0x01, 0xF3, 0xE0, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x04, 0x0F, 0xF0, 0x00, + 0x60, 0x7F, 0xFC, 0x07, 0x03, 0xFF, 0xFF, 0xF8, 0x38, 0x1F, 0xFF, 0x80, + 0x00, 0x07, 0xF8, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xF8, 0x00, + 0x3E, 0x0F, 0xC0, 0x03, 0xE0, 0x3E, 0x00, 0x3E, 0x01, 0xF0, 0x03, 0xC0, + 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x78, 0x01, 0xF0, + 0x07, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x7C, 0x00, 0xF0, + 0x1F, 0x80, 0x1F, 0xFF, 0xE0, 0x01, 0xFF, 0xF0, 0x00, 0x1E, 0x1E, 0x00, + 0x01, 0xE1, 0xE0, 0x00, 0x3E, 0x1F, 0x00, 0x03, 0xE0, 0xF0, 0x00, 0x3C, + 0x0F, 0x00, 0x03, 0xC0, 0xF8, 0x00, 0x7C, 0x07, 0x80, 0x07, 0xC0, 0x7C, + 0x00, 0x78, 0x03, 0xC0, 0x0F, 0x80, 0x3C, 0x00, 0xF8, 0x03, 0xE0, 0x0F, + 0x00, 0x1E, 0x00, 0xF0, 0x01, 0xE0, 0x1F, 0x00, 0x1F, 0x03, 0xF8, 0x00, + 0xF8, 0xFF, 0xE0, 0x0F, 0xE0, 0x00, 0x3F, 0x06, 0x01, 0xFF, 0xDC, 0x07, + 0xC1, 0xF0, 0x1E, 0x01, 0xE0, 0x3C, 0x01, 0xC0, 0xF0, 0x03, 0x81, 0xE0, + 0x03, 0x03, 0xC0, 0x04, 0x07, 0x80, 0x08, 0x0F, 0x80, 0x00, 0x1F, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7C, 0x08, 0x00, 0x78, 0x10, + 0x00, 0xF0, 0x20, 0x01, 0xE0, 0xC0, 0x03, 0xC1, 0x80, 0x07, 0x83, 0x80, + 0x1E, 0x07, 0x00, 0x3C, 0x0F, 0x00, 0xF0, 0x1F, 0x87, 0xC0, 0x23, 0xFF, + 0x00, 0x81, 0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xF0, + 0x3E, 0x07, 0xB8, 0x07, 0xC0, 0x76, 0x00, 0xF8, 0x04, 0x80, 0x3E, 0x00, + 0xB0, 0x07, 0xC0, 0x14, 0x00, 0xF8, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xC0, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0F, 0x00, + 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, + 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xFF, 0x00, + 0x00, 0x7F, 0xFE, 0x03, 0xFE, 0x1F, 0xE0, 0x01, 0xF8, 0x1F, 0x80, 0x01, + 0xC0, 0x3E, 0x00, 0x03, 0x80, 0x7C, 0x00, 0x07, 0x00, 0xF8, 0x00, 0x0C, + 0x03, 0xE0, 0x00, 0x18, 0x07, 0xC0, 0x00, 0x70, 0x0F, 0x80, 0x00, 0xC0, + 0x1F, 0x00, 0x01, 0x80, 0x7C, 0x00, 0x03, 0x00, 0xF8, 0x00, 0x0E, 0x01, + 0xF0, 0x00, 0x18, 0x07, 0xC0, 0x00, 0x30, 0x0F, 0x80, 0x00, 0x60, 0x1F, + 0x00, 0x01, 0x80, 0x3E, 0x00, 0x03, 0x00, 0xF8, 0x00, 0x06, 0x01, 0xF0, + 0x00, 0x18, 0x03, 0xE0, 0x00, 0x30, 0x07, 0xC0, 0x00, 0x60, 0x1F, 0x00, + 0x00, 0xC0, 0x3E, 0x00, 0x03, 0x00, 0x7C, 0x00, 0x06, 0x00, 0xF8, 0x00, + 0x18, 0x01, 0xF0, 0x00, 0x30, 0x03, 0xE0, 0x00, 0xC0, 0x03, 0xE0, 0x03, + 0x80, 0x03, 0xE0, 0x0E, 0x00, 0x03, 0xF0, 0x78, 0x00, 0x03, 0xFF, 0xC0, + 0x00, 0x01, 0xFE, 0x00, 0x00, 0xFF, 0xE0, 0x0F, 0xF9, 0xFC, 0x00, 0x1F, + 0x07, 0xC0, 0x00, 0x78, 0x3E, 0x00, 0x03, 0x81, 0xF0, 0x00, 0x18, 0x0F, + 0x80, 0x01, 0xC0, 0x7C, 0x00, 0x0C, 0x01, 0xE0, 0x00, 0xC0, 0x0F, 0x80, + 0x06, 0x00, 0x7C, 0x00, 0x60, 0x03, 0xE0, 0x07, 0x00, 0x1F, 0x00, 0x30, + 0x00, 0xF8, 0x03, 0x00, 0x03, 0xC0, 0x18, 0x00, 0x1E, 0x01, 0x80, 0x00, + 0xF8, 0x1C, 0x00, 0x07, 0xC0, 0xC0, 0x00, 0x3E, 0x0C, 0x00, 0x01, 0xF0, + 0x60, 0x00, 0x07, 0x86, 0x00, 0x00, 0x3C, 0x30, 0x00, 0x01, 0xE3, 0x00, + 0x00, 0x0F, 0xB0, 0x00, 0x00, 0x7D, 0x80, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0xFF, 0xE3, 0xFF, 0x81, 0xFE, 0x7F, 0x01, 0xFC, 0x00, 0xF8, 0x7C, + 0x01, 0xF0, 0x00, 0xE0, 0xF8, 0x03, 0xE0, 0x01, 0x81, 0xF0, 0x03, 0xC0, + 0x07, 0x03, 0xE0, 0x07, 0x80, 0x0C, 0x03, 0xC0, 0x0F, 0x00, 0x18, 0x07, + 0x80, 0x1E, 0x00, 0x60, 0x0F, 0x00, 0x7E, 0x00, 0xC0, 0x1F, 0x00, 0xFC, + 0x03, 0x00, 0x3E, 0x03, 0xF8, 0x06, 0x00, 0x7C, 0x05, 0xF0, 0x18, 0x00, + 0xF8, 0x1B, 0xE0, 0x30, 0x01, 0xF0, 0x33, 0xC0, 0xC0, 0x01, 0xE0, 0xC7, + 0x83, 0x80, 0x03, 0xC1, 0x8F, 0x06, 0x00, 0x07, 0x86, 0x1E, 0x1C, 0x00, + 0x0F, 0x0C, 0x3C, 0x30, 0x00, 0x1F, 0x30, 0x7C, 0xE0, 0x00, 0x3E, 0x60, + 0xF9, 0x80, 0x00, 0x7D, 0x81, 0xF7, 0x00, 0x00, 0xFB, 0x03, 0xEC, 0x00, + 0x01, 0xFC, 0x03, 0xF8, 0x00, 0x01, 0xF8, 0x07, 0xE0, 0x00, 0x03, 0xE0, + 0x0F, 0x80, 0x00, 0x07, 0xC0, 0x1F, 0x00, 0x00, 0x0F, 0x00, 0x3C, 0x00, + 0x00, 0x1E, 0x00, 0x78, 0x00, 0x00, 0x38, 0x00, 0xE0, 0x00, 0x00, 0x70, + 0x01, 0xC0, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x80, 0x06, 0x00, + 0x00, 0x07, 0xFF, 0x83, 0xFF, 0x01, 0xFE, 0x00, 0xFE, 0x00, 0x7C, 0x00, + 0x78, 0x00, 0x7C, 0x00, 0x70, 0x00, 0x3C, 0x00, 0xE0, 0x00, 0x3E, 0x01, + 0xC0, 0x00, 0x3E, 0x01, 0x80, 0x00, 0x1F, 0x03, 0x00, 0x00, 0x1F, 0x07, + 0x00, 0x00, 0x0F, 0x0E, 0x00, 0x00, 0x0F, 0x9C, 0x00, 0x00, 0x0F, 0xB8, + 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x03, 0xC0, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x07, 0xF0, + 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x1C, 0xF0, 0x00, 0x00, 0x38, 0xF8, + 0x00, 0x00, 0x30, 0xF8, 0x00, 0x00, 0x60, 0x7C, 0x00, 0x00, 0xC0, 0x7C, + 0x00, 0x01, 0xC0, 0x3C, 0x00, 0x03, 0x80, 0x3E, 0x00, 0x07, 0x00, 0x3E, + 0x00, 0x0E, 0x00, 0x1F, 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x7F, 0x00, 0x3F, + 0xC0, 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xC7, 0xF0, 0x03, 0xE0, + 0xF8, 0x00, 0x70, 0x3E, 0x00, 0x38, 0x07, 0x80, 0x0C, 0x01, 0xE0, 0x07, + 0x00, 0x7C, 0x03, 0x80, 0x1F, 0x00, 0xC0, 0x03, 0xC0, 0x60, 0x00, 0xF0, + 0x30, 0x00, 0x3E, 0x1C, 0x00, 0x07, 0x8E, 0x00, 0x01, 0xE3, 0x00, 0x00, + 0x7D, 0x80, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xF0, 0x00, 0x00, 0xF8, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xFF, 0xC0, 0x00, 0x03, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xF0, 0x00, 0xFC, 0x0E, 0x00, 0x0F, + 0xC0, 0x60, 0x00, 0xFC, 0x06, 0x00, 0x0F, 0xC0, 0x20, 0x00, 0x7C, 0x00, + 0x00, 0x07, 0xE0, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x03, 0xF0, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xFC, 0x00, + 0x08, 0x0F, 0xC0, 0x00, 0x80, 0xFC, 0x00, 0x0C, 0x07, 0xC0, 0x00, 0x60, + 0x7E, 0x00, 0x07, 0x07, 0xE0, 0x01, 0xF0, 0x7F, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0x80, 0x3C, 0x00, 0x1C, 0x00, 0x0E, 0x00, + 0x07, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0xE0, + 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, + 0x00, 0x03, 0x80, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, + 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x07, 0x00, + 0x03, 0x80, 0x01, 0xC0, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, + 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x07, 0x80, 0x03, + 0xFC, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, 0x80, + 0x01, 0xE0, 0x00, 0x70, 0x00, 0x3C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, + 0xC0, 0x00, 0xE0, 0x00, 0x78, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x03, 0x80, + 0x01, 0xC0, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, + 0x80, 0x01, 0xE0, 0x00, 0x70, 0x00, 0x3C, 0x00, 0x0E, 0x00, 0x07, 0x00, + 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x78, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, + 0x80, 0x00, 0xFF, 0x80, 0x07, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0xF0, + 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x07, + 0x00, 0x03, 0x80, 0x01, 0xC0, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, + 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x78, 0x00, 0x38, + 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, + 0xC0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0x70, 0x00, 0x38, 0x03, 0xFC, 0x00, + 0x00, 0xF0, 0x00, 0x0F, 0x00, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x03, 0xBC, + 0x00, 0x39, 0xC0, 0x07, 0x1E, 0x00, 0x70, 0xE0, 0x0E, 0x0F, 0x00, 0xE0, + 0x70, 0x1E, 0x07, 0x81, 0xC0, 0x38, 0x3C, 0x03, 0xC3, 0x80, 0x1C, 0x78, + 0x01, 0xE7, 0x00, 0x0E, 0xF0, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x60, 0xF0, 0xF8, 0x78, 0x3C, 0x1E, 0x0E, 0x07, 0x00, 0x1E, 0x70, + 0x03, 0x0B, 0x80, 0x70, 0x3C, 0x07, 0x01, 0xE0, 0x70, 0x0E, 0x07, 0x00, + 0x70, 0x78, 0x03, 0x83, 0x80, 0x38, 0x3C, 0x01, 0xC1, 0xC0, 0x0E, 0x1E, + 0x00, 0xF0, 0xF0, 0x07, 0x0F, 0x00, 0x78, 0x78, 0x03, 0xC3, 0xC0, 0x3E, + 0x1E, 0x01, 0x70, 0xF0, 0x17, 0x0F, 0x81, 0x38, 0xBE, 0x11, 0xC8, 0xFF, + 0x0F, 0x83, 0xF0, 0x70, 0x00, 0x00, 0xF0, 0x00, 0x7F, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, + 0x78, 0x00, 0x03, 0x80, 0x00, 0x1C, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x0F, + 0x80, 0x71, 0xFE, 0x03, 0x98, 0xF8, 0x3D, 0x03, 0xE1, 0xE8, 0x0F, 0x0E, + 0x80, 0x78, 0x78, 0x03, 0xC7, 0xC0, 0x1E, 0x3C, 0x00, 0xF1, 0xE0, 0x0F, + 0x1E, 0x00, 0x78, 0xF0, 0x03, 0xC7, 0x80, 0x3C, 0x38, 0x01, 0xE3, 0xC0, + 0x1E, 0x1E, 0x00, 0xE0, 0xE0, 0x0E, 0x07, 0x00, 0xF0, 0x78, 0x07, 0x03, + 0xC0, 0xE0, 0x0F, 0x0E, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x38, + 0x60, 0x38, 0x1C, 0x1C, 0x0F, 0x0E, 0x03, 0x87, 0x80, 0x03, 0xC0, 0x00, + 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, 0xC0, 0x00, 0xF0, + 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x02, 0x3E, 0x01, + 0x87, 0x80, 0xC1, 0xF0, 0x60, 0x3F, 0xF0, 0x03, 0xF0, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x78, 0x00, 0x1E, 0x78, 0x00, 0x71, + 0x70, 0x00, 0xC1, 0x70, 0x03, 0x80, 0xF0, 0x07, 0x80, 0xE0, 0x07, 0x01, + 0xE0, 0x0E, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x3C, 0x01, 0xC0, 0x3C, 0x01, + 0xC0, 0x78, 0x03, 0xC0, 0x78, 0x03, 0xC0, 0x78, 0x03, 0x80, 0xF0, 0x07, + 0x80, 0xF0, 0x07, 0x80, 0xF0, 0x0F, 0x80, 0xF0, 0x0F, 0x00, 0xF0, 0x17, + 0x08, 0xF0, 0x27, 0x10, 0x78, 0x47, 0x20, 0x7F, 0x87, 0xC0, 0x1E, 0x07, + 0x00, 0x00, 0x1F, 0x00, 0x1C, 0xF0, 0x1C, 0x1C, 0x0E, 0x07, 0x07, 0x01, + 0xC3, 0xC0, 0xF1, 0xE0, 0x38, 0x70, 0x1C, 0x3C, 0x0E, 0x1F, 0x0F, 0x07, + 0x8F, 0x01, 0xFE, 0x00, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0xC0, + 0x00, 0xF0, 0x01, 0x3C, 0x00, 0xC7, 0x80, 0x61, 0xF0, 0x60, 0x3F, 0xF0, + 0x03, 0xE0, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x03, 0x1C, 0x00, 0x00, + 0xC3, 0x80, 0x00, 0x38, 0x70, 0x00, 0x06, 0x00, 0x00, 0x01, 0xC0, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x07, 0xFF, 0xC0, 0x00, + 0xFF, 0xF8, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x01, 0xE0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x70, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x03, 0xC0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x01, 0xC0, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x01, 0x80, 0x00, 0x38, + 0x60, 0x00, 0x07, 0x0C, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x00, 0x00, 0x3F, 0x00, 0x07, 0x0E, 0x00, 0x70, 0x3E, 0x07, 0x01, 0xF0, + 0x70, 0x0E, 0x07, 0x80, 0x70, 0x3C, 0x03, 0x81, 0xC0, 0x1C, 0x0E, 0x01, + 0xE0, 0x70, 0x0E, 0x03, 0x80, 0xF0, 0x0E, 0x0F, 0x00, 0x30, 0xE0, 0x00, + 0xFE, 0x00, 0x0C, 0x00, 0x00, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0x7E, 0x00, + 0x03, 0xFE, 0x00, 0x0F, 0xFC, 0x00, 0x8F, 0xF0, 0x18, 0x0F, 0xC1, 0x80, + 0x1F, 0x18, 0x00, 0x78, 0xC0, 0x01, 0xC6, 0x00, 0x0E, 0x30, 0x00, 0x61, + 0xC0, 0x07, 0x06, 0x00, 0x70, 0x1C, 0x0E, 0x00, 0x3F, 0xC0, 0x00, 0x00, + 0xF0, 0x00, 0x7F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, + 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1C, + 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x03, 0x80, 0x78, 0x7E, 0x03, 0x86, 0x70, + 0x3C, 0x43, 0x81, 0xE4, 0x1C, 0x0E, 0x40, 0xE0, 0x74, 0x0E, 0x07, 0xA0, + 0x70, 0x3E, 0x03, 0x81, 0xE0, 0x1C, 0x0F, 0x00, 0xE0, 0xF0, 0x0E, 0x07, + 0x80, 0x70, 0x38, 0x03, 0x81, 0xC0, 0x1C, 0x1E, 0x00, 0xC2, 0xF0, 0x0E, + 0x27, 0x00, 0x73, 0x38, 0x03, 0x93, 0xC0, 0x1F, 0x1E, 0x00, 0xE0, 0x03, + 0x81, 0xF0, 0x7C, 0x1F, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x71, 0xFC, 0x1F, 0x07, 0x81, 0xE0, 0x78, 0x1C, 0x07, 0x03, 0xC0, 0xF0, + 0x38, 0x0E, 0x07, 0x81, 0xE0, 0x70, 0x1C, 0x0F, 0x03, 0x84, 0xE2, 0x39, + 0x0F, 0x81, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, + 0xF0, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x3F, 0xC0, 0x00, 0xF0, 0x00, 0x1E, + 0x00, 0x03, 0xC0, 0x00, 0x78, 0x00, 0x0E, 0x00, 0x03, 0xC0, 0x00, 0x78, + 0x00, 0x0F, 0x00, 0x01, 0xC0, 0x00, 0x38, 0x00, 0x0F, 0x00, 0x01, 0xE0, + 0x00, 0x38, 0x00, 0x07, 0x00, 0x01, 0xE0, 0x00, 0x38, 0x00, 0x07, 0x00, + 0x00, 0xE0, 0x00, 0x3C, 0x00, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x00, + 0x07, 0x00, 0x00, 0xE0, 0x00, 0x1C, 0x01, 0xC7, 0x00, 0x38, 0xC0, 0x07, + 0x30, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x7F, 0x00, 0x00, 0x78, + 0x00, 0x03, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00, 0x00, + 0x78, 0x00, 0x03, 0xC0, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, + 0x00, 0x70, 0xFF, 0x83, 0x80, 0xF0, 0x3C, 0x06, 0x01, 0xE0, 0x60, 0x0E, + 0x06, 0x00, 0x70, 0xE0, 0x07, 0x8C, 0x00, 0x3C, 0xC0, 0x01, 0xCC, 0x00, + 0x0F, 0xF0, 0x00, 0xFF, 0x80, 0x07, 0x9E, 0x00, 0x38, 0xF0, 0x01, 0xC3, + 0x80, 0x1E, 0x1E, 0x00, 0xF0, 0x70, 0x07, 0x03, 0xC2, 0x78, 0x0E, 0x13, + 0xC0, 0x79, 0x1E, 0x01, 0xF0, 0x00, 0x07, 0x00, 0x00, 0xE1, 0xFC, 0x0F, + 0x80, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1C, 0x07, 0x80, 0xF0, 0x1E, 0x03, + 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x70, 0x1E, 0x03, 0xC0, 0x78, 0x0E, 0x03, + 0xC0, 0x78, 0x0E, 0x01, 0xC0, 0x78, 0x0F, 0x01, 0xC0, 0x38, 0x4F, 0x11, + 0xE4, 0x39, 0x07, 0xC0, 0x70, 0x00, 0x07, 0x81, 0xC0, 0x78, 0xFE, 0x0F, + 0xC1, 0xF8, 0x3C, 0x33, 0x84, 0x70, 0x78, 0x87, 0x10, 0xE0, 0xF2, 0x0E, + 0x41, 0xC1, 0xC8, 0x39, 0x07, 0x87, 0xA0, 0x74, 0x0F, 0x0F, 0x40, 0xE8, + 0x1E, 0x1F, 0x01, 0xE0, 0x38, 0x3C, 0x07, 0xC0, 0xF0, 0xF8, 0x0F, 0x01, + 0xE1, 0xE0, 0x1E, 0x03, 0xC3, 0xC0, 0x38, 0x07, 0x07, 0x00, 0xF0, 0x1E, + 0x1E, 0x01, 0xE0, 0x3C, 0x3C, 0x03, 0x80, 0x79, 0x70, 0x07, 0x00, 0xE2, + 0xE0, 0x1E, 0x03, 0x8B, 0xC0, 0x3C, 0x07, 0x27, 0x80, 0x70, 0x0F, 0x8E, + 0x00, 0xE0, 0x1E, 0x00, 0x07, 0x81, 0xE3, 0xFC, 0x3F, 0x83, 0xC2, 0x3C, + 0x1E, 0x21, 0xE0, 0xF2, 0x0F, 0x07, 0x20, 0x70, 0x39, 0x07, 0x83, 0xD0, + 0x3C, 0x1F, 0x01, 0xE0, 0xE8, 0x0E, 0x0F, 0x80, 0xF0, 0x78, 0x07, 0x83, + 0xC0, 0x38, 0x1C, 0x01, 0xC1, 0xE0, 0x1E, 0x0F, 0x00, 0xF1, 0x70, 0x07, + 0x0B, 0x80, 0x38, 0xBC, 0x01, 0xC9, 0xE0, 0x0F, 0x8E, 0x00, 0x38, 0x00, + 0x00, 0x1F, 0x80, 0x07, 0x8F, 0x00, 0x70, 0x3C, 0x07, 0x00, 0xE0, 0x70, + 0x07, 0x87, 0x80, 0x3C, 0x78, 0x01, 0xE7, 0x80, 0x0F, 0x3C, 0x00, 0x7B, + 0xC0, 0x03, 0xDE, 0x00, 0x3D, 0xF0, 0x01, 0xEF, 0x80, 0x0F, 0x78, 0x00, + 0xF3, 0xC0, 0x07, 0x9E, 0x00, 0x78, 0xF0, 0x03, 0x87, 0x80, 0x38, 0x1C, + 0x03, 0x80, 0xF0, 0x38, 0x03, 0xC3, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x3C, + 0x3F, 0x00, 0x7F, 0x8F, 0xF0, 0x01, 0xF7, 0x3F, 0x00, 0x1D, 0x83, 0xF0, + 0x07, 0xA0, 0x3E, 0x00, 0xF8, 0x07, 0xC0, 0x1E, 0x00, 0xF8, 0x03, 0xC0, + 0x1F, 0x00, 0xF0, 0x03, 0xE0, 0x1E, 0x00, 0x7C, 0x03, 0xC0, 0x1F, 0x00, + 0x70, 0x03, 0xE0, 0x1E, 0x00, 0x78, 0x03, 0xC0, 0x1F, 0x00, 0x70, 0x03, + 0xC0, 0x0E, 0x00, 0xF8, 0x03, 0xC0, 0x1E, 0x00, 0x78, 0x07, 0x80, 0x0F, + 0x01, 0xE0, 0x01, 0xE0, 0x70, 0x00, 0x7C, 0x3C, 0x00, 0x0F, 0x7C, 0x00, + 0x01, 0xC0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0xE0, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x03, 0x8D, + 0xC0, 0x38, 0x2E, 0x07, 0x80, 0xF0, 0x78, 0x07, 0x03, 0x80, 0x38, 0x38, + 0x03, 0xC3, 0xC0, 0x1E, 0x3C, 0x00, 0xE1, 0xE0, 0x07, 0x1E, 0x00, 0x78, + 0xF0, 0x03, 0x87, 0x80, 0x3C, 0x78, 0x01, 0xE3, 0xC0, 0x1F, 0x1E, 0x01, + 0x70, 0xF0, 0x17, 0x87, 0x80, 0xBC, 0x3C, 0x09, 0xC0, 0xF1, 0x8E, 0x07, + 0xF8, 0xF0, 0x1F, 0x07, 0x80, 0x00, 0x38, 0x00, 0x03, 0xC0, 0x00, 0x1E, + 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x03, 0xC0, 0x00, + 0x3E, 0x00, 0x0F, 0xFE, 0x00, 0x07, 0x87, 0x3F, 0x87, 0xC3, 0xC7, 0xE1, + 0xE6, 0xF0, 0xF6, 0x00, 0x72, 0x00, 0x3A, 0x00, 0x1D, 0x00, 0x1F, 0x00, + 0x0E, 0x80, 0x07, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, + 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x0F, + 0x00, 0x00, 0x01, 0xF8, 0x81, 0x87, 0xC1, 0x80, 0xE1, 0xC0, 0x60, 0xE0, + 0x10, 0x70, 0x08, 0x3C, 0x04, 0x1F, 0x00, 0x07, 0xC0, 0x03, 0xE0, 0x00, + 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x00, 0x03, 0xC1, 0x01, 0xE0, 0x80, 0x70, + 0x40, 0x38, 0x30, 0x1C, 0x38, 0x0C, 0x1C, 0x0E, 0x0F, 0x0E, 0x04, 0x7C, + 0x00, 0x00, 0xC0, 0x18, 0x03, 0x80, 0x78, 0x1F, 0x03, 0xFF, 0x7F, 0xF0, + 0xF0, 0x0E, 0x00, 0xE0, 0x1E, 0x01, 0xE0, 0x1C, 0x01, 0xC0, 0x3C, 0x03, + 0xC0, 0x38, 0x03, 0x80, 0x78, 0x07, 0x80, 0x70, 0x8F, 0x10, 0xF1, 0x0F, + 0x20, 0xFC, 0x07, 0x80, 0x00, 0x00, 0x00, 0xF0, 0x0E, 0x7F, 0x00, 0xE0, + 0xF0, 0x1E, 0x0E, 0x01, 0xE1, 0xE0, 0x3C, 0x1E, 0x03, 0xC1, 0xE0, 0x3C, + 0x1C, 0x07, 0xC3, 0xC0, 0x78, 0x3C, 0x0F, 0x83, 0xC0, 0xB8, 0x38, 0x1F, + 0x87, 0x83, 0x70, 0x78, 0x27, 0x07, 0x86, 0x70, 0x70, 0xC7, 0x1F, 0x08, + 0xE1, 0xE1, 0x0E, 0x2E, 0x60, 0xE4, 0xFC, 0x0F, 0x87, 0x00, 0x70, 0x1C, + 0x03, 0xBF, 0x00, 0xF1, 0xE0, 0x3C, 0x78, 0x07, 0x1E, 0x00, 0xC3, 0x80, + 0x30, 0xE0, 0x08, 0x38, 0x06, 0x0E, 0x01, 0x03, 0x80, 0xC0, 0xF0, 0x20, + 0x3C, 0x10, 0x07, 0x04, 0x01, 0xC2, 0x00, 0x71, 0x00, 0x1C, 0xC0, 0x07, + 0x60, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x1C, 0x00, 0x06, 0x00, 0x01, 0x00, + 0x00, 0x0C, 0x00, 0x40, 0x3B, 0xF8, 0x01, 0x00, 0xF1, 0xE0, 0x0C, 0x03, + 0xC3, 0x80, 0x78, 0x07, 0x0E, 0x01, 0xE0, 0x0C, 0x38, 0x0F, 0x80, 0x20, + 0xE0, 0x6E, 0x00, 0x83, 0x81, 0x38, 0x04, 0x0F, 0x0C, 0xE0, 0x10, 0x1C, + 0x23, 0x80, 0x80, 0x71, 0x8E, 0x06, 0x01, 0xCC, 0x38, 0x10, 0x07, 0x20, + 0xE0, 0x80, 0x1D, 0x83, 0x86, 0x00, 0x7C, 0x07, 0x30, 0x01, 0xF0, 0x1C, + 0x80, 0x07, 0x80, 0x74, 0x00, 0x1E, 0x01, 0xF0, 0x00, 0x70, 0x07, 0x80, + 0x01, 0xC0, 0x1C, 0x00, 0x06, 0x00, 0x60, 0x00, 0x10, 0x01, 0x00, 0x00, + 0x00, 0xE0, 0x38, 0x1F, 0x81, 0xF0, 0x8F, 0x09, 0x80, 0x3C, 0x40, 0x00, + 0x72, 0x00, 0x01, 0xD0, 0x00, 0x07, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0x38, + 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x7C, 0x00, + 0x01, 0x70, 0x00, 0x09, 0xC0, 0x00, 0x67, 0x00, 0x01, 0x1E, 0x10, 0x08, + 0x38, 0x40, 0x40, 0xE2, 0x39, 0x03, 0xD0, 0xF8, 0x0F, 0x83, 0xC0, 0x1C, + 0x00, 0x07, 0x80, 0x33, 0xFC, 0x03, 0xC1, 0xE0, 0x1E, 0x07, 0x80, 0x70, + 0x3C, 0x01, 0x80, 0xE0, 0x0C, 0x07, 0x80, 0x40, 0x3C, 0x02, 0x00, 0xE0, + 0x20, 0x07, 0x81, 0x00, 0x3C, 0x18, 0x01, 0xE0, 0x80, 0x07, 0x0C, 0x00, + 0x38, 0x40, 0x01, 0xE4, 0x00, 0x0F, 0x60, 0x00, 0x3A, 0x00, 0x01, 0xF0, + 0x00, 0x0F, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x18, 0x00, 0x00, + 0x80, 0x00, 0x0C, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, + 0x04, 0x00, 0x0E, 0x40, 0x00, 0x7C, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, + 0xFF, 0x87, 0xFF, 0x82, 0x00, 0x83, 0x00, 0xC1, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x20, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x18, 0x00, 0x1E, + 0x00, 0x1F, 0xC0, 0x1F, 0xF0, 0xE8, 0xFC, 0x70, 0x1E, 0x38, 0x03, 0x88, + 0x00, 0x78, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x0F, + 0x00, 0x07, 0x80, 0x03, 0x80, 0x01, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, + 0x70, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x07, 0x80, 0x07, 0x80, 0x03, 0xC0, 0x07, 0xC0, 0x07, 0xC0, 0x00, 0x80, + 0x00, 0x60, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x07, + 0x80, 0x03, 0x80, 0x01, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x01, 0x80, + 0x00, 0x70, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x18, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x07, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0E, + 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x3C, + 0x00, 0x3C, 0x00, 0x38, 0x00, 0x38, 0x00, 0x18, 0x00, 0x08, 0x00, 0x1C, + 0x00, 0x7E, 0x00, 0x78, 0x00, 0xF0, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xE0, + 0x01, 0xC0, 0x01, 0xC0, 0x03, 0xC0, 0x03, 0x80, 0x03, 0x80, 0x07, 0x80, + 0x07, 0x80, 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x1C, 0x00, + 0xF8, 0x00, 0x1F, 0x80, 0x00, 0xFF, 0x80, 0xC7, 0xFF, 0x87, 0xBC, 0x3F, + 0xFE, 0x60, 0x3F, 0xF0, 0x00, 0x1F, 0x00}; + +const GFXglyph FreeSerifItalic24pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 12, 0, 1}, // 0x20 ' ' + {0, 12, 32, 16, 2, -30}, // 0x21 '!' + {48, 14, 12, 16, 6, -31}, // 0x22 '"' + {69, 25, 31, 23, 0, -30}, // 0x23 '#' + {166, 21, 38, 24, 2, -33}, // 0x24 '$' + {266, 33, 32, 39, 4, -30}, // 0x25 '%' + {398, 30, 33, 37, 4, -31}, // 0x26 '&' + {522, 5, 12, 9, 6, -31}, // 0x27 ''' + {530, 13, 39, 16, 2, -30}, // 0x28 '(' + {594, 13, 39, 16, 0, -30}, // 0x29 ')' + {658, 16, 20, 23, 7, -31}, // 0x2A '*' + {698, 23, 23, 32, 4, -22}, // 0x2B '+' + {765, 7, 11, 12, -1, -4}, // 0x2C ',' + {775, 11, 3, 16, 2, -11}, // 0x2D '-' + {780, 5, 5, 12, 1, -3}, // 0x2E '.' + {784, 21, 33, 14, 0, -31}, // 0x2F '/' + {871, 21, 31, 23, 2, -30}, // 0x30 '0' + {953, 17, 32, 23, 2, -31}, // 0x31 '1' + {1021, 21, 31, 24, 0, -30}, // 0x32 '2' + {1103, 22, 32, 23, 0, -31}, // 0x33 '3' + {1191, 22, 32, 23, 0, -31}, // 0x34 '4' + {1279, 22, 32, 24, 0, -31}, // 0x35 '5' + {1367, 23, 32, 23, 1, -31}, // 0x36 '6' + {1459, 21, 32, 23, 4, -31}, // 0x37 '7' + {1543, 22, 32, 23, 1, -31}, // 0x38 '8' + {1631, 22, 33, 23, 1, -31}, // 0x39 '9' + {1722, 9, 22, 12, 2, -20}, // 0x3A ':' + {1747, 11, 27, 12, 1, -20}, // 0x3B ';' + {1785, 23, 25, 27, 3, -24}, // 0x3C '<' + {1857, 24, 12, 31, 4, -17}, // 0x3D '=' + {1893, 24, 25, 27, 3, -24}, // 0x3E '>' + {1968, 16, 33, 21, 6, -31}, // 0x3F '?' + {2034, 33, 33, 37, 3, -31}, // 0x40 '@' + {2171, 29, 31, 31, 0, -30}, // 0x41 'A' + {2284, 28, 31, 28, 0, -30}, // 0x42 'B' + {2393, 30, 33, 29, 2, -31}, // 0x43 'C' + {2517, 33, 31, 33, 0, -30}, // 0x44 'D' + {2645, 29, 31, 27, 0, -30}, // 0x45 'E' + {2758, 29, 31, 27, 0, -30}, // 0x46 'F' + {2871, 31, 33, 32, 2, -31}, // 0x47 'G' + {2999, 36, 31, 33, 0, -30}, // 0x48 'H' + {3139, 18, 31, 15, 0, -30}, // 0x49 'I' + {3209, 23, 32, 20, 0, -30}, // 0x4A 'J' + {3301, 33, 31, 30, 0, -30}, // 0x4B 'K' + {3429, 27, 31, 27, 0, -30}, // 0x4C 'L' + {3534, 42, 31, 39, 0, -30}, // 0x4D 'M' + {3697, 35, 32, 32, 0, -30}, // 0x4E 'N' + {3837, 30, 33, 31, 2, -31}, // 0x4F 'O' + {3961, 29, 31, 27, 0, -30}, // 0x50 'P' + {4074, 30, 41, 31, 2, -31}, // 0x51 'Q' + {4228, 28, 31, 29, 0, -30}, // 0x52 'R' + {4337, 23, 33, 21, 0, -31}, // 0x53 'S' + {4432, 27, 31, 28, 4, -30}, // 0x54 'T' + {4537, 31, 32, 33, 5, -30}, // 0x55 'U' + {4661, 29, 32, 31, 6, -30}, // 0x56 'V' + {4777, 39, 32, 42, 6, -30}, // 0x57 'W' + {4933, 32, 31, 31, 0, -30}, // 0x58 'X' + {5057, 26, 31, 28, 5, -30}, // 0x59 'Y' + {5158, 29, 31, 26, 0, -30}, // 0x5A 'Z' + {5271, 17, 39, 18, 1, -31}, // 0x5B '[' + {5354, 17, 33, 23, 5, -31}, // 0x5C '\' + {5425, 17, 39, 18, 1, -31}, // 0x5D ']' + {5508, 20, 17, 20, 0, -31}, // 0x5E '^' + {5551, 24, 2, 23, 0, 5}, // 0x5F '_' + {5557, 8, 8, 12, 6, -31}, // 0x60 '`' + {5565, 21, 21, 23, 1, -20}, // 0x61 'a' + {5621, 21, 33, 22, 1, -31}, // 0x62 'b' + {5708, 18, 22, 19, 1, -20}, // 0x63 'c' + {5758, 24, 33, 23, 1, -31}, // 0x64 'd' + {5857, 18, 22, 19, 1, -20}, // 0x65 'e' + {5907, 27, 42, 20, -4, -31}, // 0x66 'f' + {6049, 21, 31, 21, -1, -20}, // 0x67 'g' + {6131, 21, 32, 23, 1, -31}, // 0x68 'h' + {6215, 10, 32, 12, 2, -30}, // 0x69 'i' + {6255, 19, 41, 13, -3, -30}, // 0x6A 'j' + {6353, 21, 33, 21, 1, -31}, // 0x6B 'k' + {6440, 11, 33, 12, 2, -31}, // 0x6C 'l' + {6486, 31, 21, 34, 1, -20}, // 0x6D 'm' + {6568, 21, 21, 23, 1, -20}, // 0x6E 'n' + {6624, 21, 22, 22, 1, -20}, // 0x6F 'o' + {6682, 27, 31, 22, -4, -20}, // 0x70 'p' + {6787, 21, 31, 23, 1, -20}, // 0x71 'q' + {6869, 17, 21, 17, 1, -20}, // 0x72 'r' + {6914, 17, 22, 16, 0, -20}, // 0x73 's' + {6961, 12, 26, 11, 1, -24}, // 0x74 't' + {7000, 20, 22, 23, 1, -20}, // 0x75 'u' + {7055, 18, 22, 21, 3, -20}, // 0x76 'v' + {7105, 30, 22, 32, 2, -20}, // 0x77 'w' + {7188, 22, 22, 20, -1, -20}, // 0x78 'x' + {7249, 21, 31, 22, 1, -20}, // 0x79 'y' + {7331, 17, 24, 18, 0, -19}, // 0x7A 'z' + {7382, 17, 40, 19, 2, -31}, // 0x7B '{' + {7467, 3, 33, 13, 5, -31}, // 0x7C '|' + {7480, 16, 41, 19, 0, -32}, // 0x7D '}' + {7562, 22, 6, 25, 2, -14}}; // 0x7E '~' + +const GFXfont FreeSerifItalic24pt7b PROGMEM = { + (uint8_t *)FreeSerifItalic24pt7bBitmaps, + (GFXglyph *)FreeSerifItalic24pt7bGlyphs, 0x20, 0x7E, 56}; + +// Approx. 8251 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic9pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic9pt7b.h new file mode 100644 index 0000000..9f3d5df --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/FreeSerifItalic9pt7b.h @@ -0,0 +1,201 @@ +const uint8_t FreeSerifItalic9pt7bBitmaps[] PROGMEM = { + 0x11, 0x12, 0x22, 0x24, 0x40, 0x0C, 0xDE, 0xE5, 0x40, 0x04, 0x82, 0x20, + 0x98, 0x24, 0x7F, 0xC4, 0x82, 0x23, 0xFC, 0x24, 0x11, 0x04, 0x83, 0x20, + 0x1C, 0x1B, 0x99, 0x4D, 0x26, 0x81, 0xC0, 0x70, 0x1C, 0x13, 0x49, 0xA4, + 0xDA, 0xC7, 0xC1, 0x00, 0x80, 0x1C, 0x61, 0xCF, 0x0E, 0x28, 0x30, 0xA0, + 0xC5, 0x03, 0x34, 0xE7, 0xAE, 0x40, 0xB1, 0x05, 0x84, 0x26, 0x20, 0x99, + 0x84, 0x3C, 0x03, 0x80, 0x6C, 0x06, 0xC0, 0x78, 0x06, 0x01, 0xEF, 0x66, + 0x24, 0x24, 0xC3, 0x8C, 0x10, 0xE3, 0x87, 0xCE, 0xFA, 0x08, 0x21, 0x08, + 0x61, 0x8C, 0x30, 0xC3, 0x0C, 0x30, 0x41, 0x02, 0x00, 0x10, 0x40, 0x82, + 0x0C, 0x30, 0xC3, 0x0C, 0x61, 0x84, 0x21, 0x08, 0x00, 0x30, 0xCA, 0x5E, + 0x6A, 0x93, 0x08, 0x08, 0x04, 0x02, 0x01, 0x0F, 0xF8, 0x40, 0x20, 0x10, + 0x08, 0x00, 0x56, 0xF0, 0xF0, 0x03, 0x02, 0x06, 0x04, 0x08, 0x08, 0x10, + 0x30, 0x20, 0x60, 0x40, 0xC0, 0x0E, 0x0C, 0x8C, 0x6C, 0x36, 0x1F, 0x0F, + 0x07, 0x87, 0xC3, 0x61, 0xB1, 0x88, 0x83, 0x80, 0x04, 0x70, 0xC3, 0x08, + 0x21, 0x86, 0x10, 0x43, 0x08, 0xF8, 0x1C, 0x67, 0x83, 0x03, 0x02, 0x06, + 0x0C, 0x08, 0x10, 0x20, 0x42, 0xFC, 0x0F, 0x08, 0xC0, 0x60, 0xC1, 0xE0, + 0x38, 0x0C, 0x06, 0x03, 0x01, 0x01, 0x1F, 0x00, 0x01, 0x01, 0x81, 0x41, + 0x61, 0x21, 0x11, 0x18, 0x88, 0xFF, 0x02, 0x03, 0x01, 0x00, 0x0F, 0x84, + 0x04, 0x03, 0x80, 0x60, 0x18, 0x0C, 0x06, 0x03, 0x03, 0x03, 0x1E, 0x00, + 0x01, 0x83, 0x87, 0x07, 0x03, 0x03, 0x73, 0xCD, 0x86, 0xC3, 0x61, 0xB1, + 0x88, 0xC3, 0xC0, 0x7F, 0x40, 0x80, 0x80, 0x40, 0x40, 0x60, 0x20, 0x20, + 0x10, 0x10, 0x18, 0x08, 0x00, 0x1E, 0x19, 0xCC, 0x66, 0x33, 0xB0, 0xE0, + 0x50, 0xCC, 0xC3, 0x61, 0xB0, 0xCC, 0xC3, 0xC0, 0x0E, 0x19, 0x8C, 0x6C, + 0x36, 0x1B, 0x0D, 0x86, 0xE6, 0x3F, 0x03, 0x03, 0x06, 0x0C, 0x00, 0x33, + 0x00, 0x00, 0xCC, 0x33, 0x00, 0x00, 0x44, 0x48, 0x01, 0x83, 0x86, 0x1C, + 0x0C, 0x03, 0x80, 0x30, 0x07, 0x00, 0x80, 0xFF, 0x80, 0x00, 0x00, 0x0F, + 0xF8, 0xC0, 0x1C, 0x03, 0x80, 0x70, 0x18, 0x38, 0x70, 0xC0, 0x80, 0x00, + 0x3C, 0x8C, 0x18, 0x30, 0xC3, 0x0C, 0x20, 0x40, 0x80, 0x06, 0x00, 0x0F, + 0xC0, 0xC3, 0x0C, 0x04, 0xC7, 0xBC, 0x64, 0xE2, 0x27, 0x31, 0x39, 0x91, + 0xCC, 0x93, 0x3B, 0x0E, 0x00, 0x1F, 0x80, 0x01, 0x00, 0x60, 0x14, 0x04, + 0xC0, 0x98, 0x23, 0x07, 0xE1, 0x04, 0x20, 0x88, 0x1B, 0x8F, 0x80, 0x3F, + 0xC1, 0x8C, 0x21, 0x8C, 0x31, 0x8C, 0x3E, 0x04, 0x61, 0x86, 0x30, 0xC4, + 0x19, 0x86, 0x7F, 0x80, 0x07, 0x91, 0x86, 0x30, 0x26, 0x02, 0x60, 0x0C, + 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0x61, 0x83, 0xE0, 0x3F, 0xC0, + 0x63, 0x82, 0x0C, 0x30, 0x31, 0x81, 0x8C, 0x0C, 0x40, 0x66, 0x07, 0x30, + 0x31, 0x03, 0x18, 0x71, 0xFE, 0x00, 0x3F, 0xF0, 0xC2, 0x08, 0x21, 0x80, + 0x19, 0x81, 0xF8, 0x11, 0x03, 0x10, 0x30, 0x02, 0x04, 0x60, 0x8F, 0xF8, + 0x3F, 0xF0, 0xC2, 0x08, 0x21, 0x80, 0x19, 0x81, 0xF8, 0x11, 0x03, 0x10, + 0x30, 0x02, 0x00, 0x60, 0x0F, 0x80, 0x07, 0x91, 0x87, 0x30, 0x26, 0x02, + 0x60, 0x0C, 0x00, 0xC1, 0xFC, 0x0C, 0xC0, 0xCC, 0x0C, 0x60, 0x83, 0xF0, + 0x3E, 0x3C, 0x30, 0x60, 0x81, 0x06, 0x0C, 0x18, 0x30, 0x7F, 0x81, 0x06, + 0x0C, 0x18, 0x30, 0x60, 0x81, 0x06, 0x0C, 0x3C, 0x78, 0x1E, 0x18, 0x20, + 0xC1, 0x83, 0x04, 0x18, 0x30, 0x41, 0x87, 0x80, 0x0F, 0x81, 0x80, 0x80, + 0xC0, 0x60, 0x20, 0x30, 0x18, 0x0C, 0x04, 0x36, 0x1E, 0x00, 0x3E, 0x78, + 0x61, 0x82, 0x10, 0x31, 0x01, 0xB0, 0x0E, 0x00, 0x58, 0x06, 0x60, 0x33, + 0x01, 0x0C, 0x18, 0x61, 0xE7, 0xC0, 0x3E, 0x01, 0x80, 0x20, 0x0C, 0x01, + 0x80, 0x30, 0x04, 0x01, 0x80, 0x30, 0x04, 0x0D, 0x83, 0x7F, 0xE0, 0x1C, + 0x07, 0x0C, 0x0E, 0x0C, 0x14, 0x14, 0x1C, 0x14, 0x2C, 0x16, 0x4C, 0x26, + 0x48, 0x26, 0x98, 0x27, 0x18, 0x27, 0x10, 0x42, 0x30, 0xF4, 0x7C, 0x38, + 0x78, 0x60, 0x83, 0x04, 0x2C, 0x41, 0x22, 0x09, 0x10, 0x4D, 0x84, 0x28, + 0x21, 0x41, 0x06, 0x10, 0x21, 0xE1, 0x00, 0x07, 0x83, 0x18, 0xC1, 0xB0, + 0x36, 0x07, 0xC0, 0xF0, 0x3E, 0x06, 0xC0, 0xD8, 0x31, 0x8C, 0x1E, 0x00, + 0x3F, 0xC1, 0x9C, 0x21, 0x8C, 0x31, 0x86, 0x31, 0x87, 0xE1, 0x80, 0x30, + 0x04, 0x01, 0x80, 0x78, 0x00, 0x07, 0x83, 0x18, 0xC1, 0x98, 0x36, 0x07, + 0xC0, 0xF0, 0x1E, 0x06, 0xC0, 0xD8, 0x31, 0x04, 0x13, 0x01, 0x80, 0x70, + 0xB7, 0xE0, 0x3F, 0xC1, 0x8C, 0x21, 0x8C, 0x31, 0x8C, 0x3F, 0x04, 0xC1, + 0x98, 0x31, 0x84, 0x31, 0x86, 0x78, 0x70, 0x1E, 0x4C, 0x63, 0x08, 0xC0, + 0x38, 0x07, 0x00, 0x60, 0x0C, 0x43, 0x10, 0xC6, 0x62, 0x70, 0x7F, 0xE9, + 0x8E, 0x31, 0x04, 0x01, 0x80, 0x30, 0x06, 0x00, 0x80, 0x30, 0x06, 0x00, + 0x80, 0x7E, 0x00, 0x7C, 0xF3, 0x02, 0x30, 0x46, 0x04, 0x60, 0x46, 0x04, + 0x40, 0x8C, 0x08, 0xC0, 0x8C, 0x10, 0xE3, 0x03, 0xC0, 0xF8, 0xEC, 0x0C, + 0x81, 0x18, 0x43, 0x08, 0x62, 0x0C, 0x81, 0x90, 0x14, 0x03, 0x00, 0x60, + 0x08, 0x00, 0xFB, 0xCE, 0x43, 0x0C, 0x86, 0x11, 0x8C, 0x43, 0x38, 0x86, + 0xB2, 0x0D, 0x24, 0x1C, 0x50, 0x38, 0xA0, 0x21, 0x80, 0x42, 0x01, 0x04, + 0x00, 0x3E, 0x71, 0x82, 0x0C, 0x40, 0xC8, 0x07, 0x00, 0x60, 0x06, 0x00, + 0xB0, 0x13, 0x02, 0x18, 0x61, 0x8F, 0x3E, 0xF9, 0xC8, 0x23, 0x10, 0xC8, + 0x34, 0x05, 0x01, 0x80, 0x40, 0x30, 0x0C, 0x03, 0x03, 0xE0, 0x3F, 0xE4, + 0x19, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0x40, 0x18, 0x06, 0x05, + 0x81, 0x7F, 0xE0, 0x0E, 0x10, 0x20, 0x81, 0x02, 0x04, 0x10, 0x20, 0x40, + 0x82, 0x04, 0x08, 0x1C, 0x00, 0x81, 0x04, 0x18, 0x20, 0xC1, 0x04, 0x08, + 0x20, 0x41, 0x38, 0x20, 0x82, 0x08, 0x41, 0x04, 0x10, 0xC2, 0x08, 0x20, + 0x8C, 0x00, 0x18, 0x18, 0x2C, 0x24, 0x46, 0x42, 0x83, 0xFF, 0x80, 0xD8, + 0x80, 0x1F, 0x98, 0x98, 0x4C, 0x2C, 0x36, 0x33, 0x3A, 0xEE, 0x38, 0x08, + 0x04, 0x02, 0x03, 0x71, 0xCC, 0xC6, 0xC3, 0x63, 0x21, 0x93, 0x8F, 0x00, + 0x1F, 0x33, 0x60, 0xC0, 0xC0, 0xC0, 0xC4, 0x78, 0x01, 0x80, 0x40, 0x60, + 0x20, 0xF1, 0x89, 0x8C, 0xC4, 0xC2, 0x63, 0x33, 0xAE, 0xE0, 0x0E, 0x65, + 0x8B, 0x2F, 0x98, 0x31, 0x3C, 0x01, 0xE0, 0x40, 0x08, 0x02, 0x00, 0x40, + 0x3E, 0x03, 0x00, 0x40, 0x08, 0x01, 0x00, 0x60, 0x0C, 0x01, 0x00, 0x20, + 0x04, 0x01, 0x00, 0xC0, 0x00, 0x1E, 0x19, 0xD8, 0xCC, 0xE1, 0xC3, 0x01, + 0xE0, 0xBC, 0x82, 0x41, 0x31, 0x0F, 0x00, 0x38, 0x08, 0x04, 0x02, 0x03, + 0x39, 0x6C, 0xC6, 0x46, 0x63, 0x21, 0x11, 0xB8, 0xE0, 0x30, 0x00, 0xE2, + 0x44, 0xC8, 0xCE, 0x06, 0x00, 0x00, 0x00, 0xC0, 0x83, 0x04, 0x08, 0x10, + 0x60, 0x81, 0x02, 0x04, 0x70, 0x38, 0x10, 0x10, 0x10, 0x37, 0x22, 0x24, + 0x38, 0x78, 0x48, 0x4D, 0xC6, 0x73, 0x32, 0x26, 0x64, 0x4C, 0xDE, 0x77, + 0x39, 0x5E, 0xCC, 0xCC, 0xCE, 0x66, 0x62, 0x22, 0x11, 0x11, 0xB9, 0x8E, + 0x77, 0x3B, 0x33, 0x62, 0x62, 0x42, 0x4D, 0xCE, 0x0F, 0x18, 0xD8, 0x7C, + 0x3C, 0x3E, 0x1B, 0x18, 0xF0, 0x3B, 0x87, 0x31, 0x8C, 0x43, 0x31, 0x88, + 0x62, 0x30, 0xF0, 0x60, 0x10, 0x04, 0x03, 0x80, 0x0F, 0x18, 0x98, 0x4C, + 0x2C, 0x26, 0x33, 0x38, 0xEC, 0x04, 0x02, 0x03, 0x03, 0xC0, 0x76, 0x50, + 0xC1, 0x06, 0x08, 0x10, 0x60, 0x1A, 0x6C, 0xC8, 0xC0, 0xD1, 0xB3, 0x5C, + 0x23, 0xC8, 0xC4, 0x21, 0x18, 0xE0, 0xC3, 0x42, 0x42, 0xC6, 0x86, 0x8C, + 0x9D, 0xEE, 0x62, 0xC4, 0x89, 0xA3, 0x47, 0x0C, 0x10, 0xE2, 0x2C, 0x44, + 0xD8, 0x9D, 0x23, 0xA4, 0x65, 0x0C, 0xC1, 0x10, 0x19, 0x95, 0x43, 0x01, + 0x80, 0xC0, 0xA0, 0x91, 0x8E, 0x70, 0x88, 0x46, 0x23, 0x20, 0x90, 0x50, + 0x28, 0x18, 0x08, 0x08, 0x08, 0x18, 0x00, 0x3F, 0x42, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x72, 0x0E, 0x08, 0x61, 0x04, 0x30, 0x86, 0x08, 0x61, 0x04, + 0x30, 0xC3, 0x8F, 0x00, 0xFF, 0xF0, 0x1E, 0x0C, 0x10, 0x20, 0xC1, 0x82, + 0x04, 0x1C, 0x30, 0x40, 0x83, 0x04, 0x08, 0x20, 0x60, 0x99, 0x8E}; + +const GFXglyph FreeSerifItalic9pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 5, 0, 1}, // 0x20 ' ' + {0, 4, 12, 6, 1, -11}, // 0x21 '!' + {6, 5, 4, 6, 3, -11}, // 0x22 '"' + {9, 10, 12, 9, 0, -11}, // 0x23 '#' + {24, 9, 15, 9, 1, -12}, // 0x24 '$' + {41, 14, 12, 15, 1, -11}, // 0x25 '%' + {62, 12, 12, 14, 1, -11}, // 0x26 '&' + {80, 2, 4, 4, 3, -11}, // 0x27 ''' + {81, 6, 15, 6, 1, -11}, // 0x28 '(' + {93, 6, 15, 6, 0, -11}, // 0x29 ')' + {105, 6, 8, 9, 3, -11}, // 0x2A '*' + {111, 9, 9, 12, 1, -8}, // 0x2B '+' + {122, 2, 4, 5, 0, -1}, // 0x2C ',' + {123, 4, 1, 6, 1, -3}, // 0x2D '-' + {124, 2, 2, 5, 0, -1}, // 0x2E '.' + {125, 8, 12, 5, 0, -11}, // 0x2F '/' + {137, 9, 13, 9, 1, -12}, // 0x30 '0' + {152, 6, 13, 9, 1, -12}, // 0x31 '1' + {162, 8, 12, 9, 1, -11}, // 0x32 '2' + {174, 9, 12, 9, 0, -11}, // 0x33 '3' + {188, 9, 12, 9, 0, -11}, // 0x34 '4' + {202, 9, 12, 9, 0, -11}, // 0x35 '5' + {216, 9, 13, 9, 1, -12}, // 0x36 '6' + {231, 9, 12, 9, 1, -11}, // 0x37 '7' + {245, 9, 13, 9, 1, -12}, // 0x38 '8' + {260, 9, 13, 9, 0, -12}, // 0x39 '9' + {275, 4, 8, 4, 1, -7}, // 0x3A ':' + {279, 4, 10, 4, 1, -7}, // 0x3B ';' + {284, 9, 9, 10, 1, -8}, // 0x3C '<' + {295, 9, 5, 12, 2, -6}, // 0x3D '=' + {301, 9, 9, 10, 1, -8}, // 0x3E '>' + {312, 7, 12, 8, 2, -11}, // 0x3F '?' + {323, 13, 12, 14, 1, -11}, // 0x40 '@' + {343, 11, 11, 12, 0, -10}, // 0x41 'A' + {359, 11, 12, 11, 0, -11}, // 0x42 'B' + {376, 12, 12, 11, 1, -11}, // 0x43 'C' + {394, 13, 12, 13, 0, -11}, // 0x44 'D' + {414, 12, 12, 10, 0, -11}, // 0x45 'E' + {432, 12, 12, 10, 0, -11}, // 0x46 'F' + {450, 12, 12, 12, 1, -11}, // 0x47 'G' + {468, 14, 12, 13, 0, -11}, // 0x48 'H' + {489, 7, 12, 6, 0, -11}, // 0x49 'I' + {500, 9, 12, 8, 0, -11}, // 0x4A 'J' + {514, 13, 12, 12, 0, -11}, // 0x4B 'K' + {534, 11, 12, 10, 0, -11}, // 0x4C 'L' + {551, 16, 12, 15, 0, -11}, // 0x4D 'M' + {575, 13, 12, 12, 0, -11}, // 0x4E 'N' + {595, 11, 12, 12, 1, -11}, // 0x4F 'O' + {612, 11, 12, 10, 0, -11}, // 0x50 'P' + {629, 11, 15, 12, 1, -11}, // 0x51 'Q' + {650, 11, 12, 11, 0, -11}, // 0x52 'R' + {667, 10, 12, 8, 0, -11}, // 0x53 'S' + {682, 11, 12, 11, 2, -11}, // 0x54 'T' + {699, 12, 12, 13, 2, -11}, // 0x55 'U' + {717, 11, 12, 12, 2, -11}, // 0x56 'V' + {734, 15, 12, 16, 2, -11}, // 0x57 'W' + {757, 12, 12, 12, 0, -11}, // 0x58 'X' + {775, 10, 12, 11, 2, -11}, // 0x59 'Y' + {790, 11, 12, 10, 0, -11}, // 0x5A 'Z' + {807, 7, 15, 7, 0, -11}, // 0x5B '[' + {821, 6, 12, 9, 2, -11}, // 0x5C '\' + {830, 6, 15, 7, 1, -11}, // 0x5D ']' + {842, 8, 7, 8, 0, -11}, // 0x5E '^' + {849, 9, 1, 9, 0, 2}, // 0x5F '_' + {851, 3, 3, 5, 2, -11}, // 0x60 '`' + {853, 9, 8, 9, 0, -7}, // 0x61 'a' + {862, 9, 12, 9, 0, -11}, // 0x62 'b' + {876, 8, 8, 7, 0, -7}, // 0x63 'c' + {884, 9, 12, 9, 0, -11}, // 0x64 'd' + {898, 7, 8, 7, 0, -7}, // 0x65 'e' + {905, 11, 17, 8, -1, -12}, // 0x66 'f' + {929, 9, 12, 8, 0, -7}, // 0x67 'g' + {943, 9, 12, 9, 0, -11}, // 0x68 'h' + {957, 4, 12, 4, 1, -11}, // 0x69 'i' + {963, 7, 16, 5, -1, -11}, // 0x6A 'j' + {977, 8, 12, 8, 0, -11}, // 0x6B 'k' + {989, 4, 12, 5, 1, -11}, // 0x6C 'l' + {995, 13, 8, 13, 0, -7}, // 0x6D 'm' + {1008, 8, 8, 9, 0, -7}, // 0x6E 'n' + {1016, 9, 8, 9, 0, -7}, // 0x6F 'o' + {1025, 10, 12, 8, -1, -7}, // 0x70 'p' + {1040, 9, 12, 9, 0, -7}, // 0x71 'q' + {1054, 7, 8, 7, 0, -7}, // 0x72 'r' + {1061, 7, 8, 6, 0, -7}, // 0x73 's' + {1068, 5, 9, 4, 0, -8}, // 0x74 't' + {1074, 8, 8, 9, 1, -7}, // 0x75 'u' + {1082, 7, 8, 8, 1, -7}, // 0x76 'v' + {1089, 11, 8, 12, 1, -7}, // 0x77 'w' + {1100, 9, 8, 8, -1, -7}, // 0x78 'x' + {1109, 9, 12, 9, 0, -7}, // 0x79 'y' + {1123, 8, 9, 7, 0, -7}, // 0x7A 'z' + {1132, 6, 15, 7, 1, -11}, // 0x7B '{' + {1144, 1, 12, 5, 2, -11}, // 0x7C '|' + {1146, 7, 16, 7, 0, -12}, // 0x7D '}' + {1160, 8, 3, 10, 1, -5}}; // 0x7E '~' + +const GFXfont FreeSerifItalic9pt7b PROGMEM = { + (uint8_t *)FreeSerifItalic9pt7bBitmaps, + (GFXglyph *)FreeSerifItalic9pt7bGlyphs, 0x20, 0x7E, 22}; + +// Approx. 1835 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/Org_01.h b/libraries/Adafruit_GFX_Library/Fonts/Org_01.h new file mode 100644 index 0000000..eabbd92 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/Org_01.h @@ -0,0 +1,128 @@ +// Org_v01 by Orgdot (www.orgdot.com/aliasfonts). A tiny, +// stylized font with all characters within a 6 pixel height. + +const uint8_t Org_01Bitmaps[] PROGMEM = { + 0xE8, 0xA0, 0x57, 0xD5, 0xF5, 0x00, 0xFD, 0x3E, 0x5F, 0x80, 0x88, 0x88, + 0x88, 0x80, 0xF4, 0xBF, 0x2E, 0x80, 0x80, 0x6A, 0x40, 0x95, 0x80, 0xAA, + 0x80, 0x5D, 0x00, 0xC0, 0xF0, 0x80, 0x08, 0x88, 0x88, 0x00, 0xFC, 0x63, + 0x1F, 0x80, 0xF8, 0xF8, 0x7F, 0x0F, 0x80, 0xF8, 0x7E, 0x1F, 0x80, 0x8C, + 0x7E, 0x10, 0x80, 0xFC, 0x3E, 0x1F, 0x80, 0xFC, 0x3F, 0x1F, 0x80, 0xF8, + 0x42, 0x10, 0x80, 0xFC, 0x7F, 0x1F, 0x80, 0xFC, 0x7E, 0x1F, 0x80, 0x90, + 0xB0, 0x2A, 0x22, 0xF0, 0xF0, 0x88, 0xA8, 0xF8, 0x4E, 0x02, 0x00, 0xFD, + 0x6F, 0x0F, 0x80, 0xFC, 0x7F, 0x18, 0x80, 0xF4, 0x7D, 0x1F, 0x00, 0xFC, + 0x21, 0x0F, 0x80, 0xF4, 0x63, 0x1F, 0x00, 0xFC, 0x3F, 0x0F, 0x80, 0xFC, + 0x3F, 0x08, 0x00, 0xFC, 0x2F, 0x1F, 0x80, 0x8C, 0x7F, 0x18, 0x80, 0xF9, + 0x08, 0x4F, 0x80, 0x78, 0x85, 0x2F, 0x80, 0x8D, 0xB1, 0x68, 0x80, 0x84, + 0x21, 0x0F, 0x80, 0xFD, 0x6B, 0x5A, 0x80, 0xFC, 0x63, 0x18, 0x80, 0xFC, + 0x63, 0x1F, 0x80, 0xFC, 0x7F, 0x08, 0x00, 0xFC, 0x63, 0x3F, 0x80, 0xFC, + 0x7F, 0x29, 0x00, 0xFC, 0x3E, 0x1F, 0x80, 0xF9, 0x08, 0x42, 0x00, 0x8C, + 0x63, 0x1F, 0x80, 0x8C, 0x62, 0xA2, 0x00, 0xAD, 0x6B, 0x5F, 0x80, 0x8A, + 0x88, 0xA8, 0x80, 0x8C, 0x54, 0x42, 0x00, 0xF8, 0x7F, 0x0F, 0x80, 0xEA, + 0xC0, 0x82, 0x08, 0x20, 0x80, 0xD5, 0xC0, 0x54, 0xF8, 0x80, 0xF1, 0xFF, + 0x8F, 0x99, 0xF0, 0xF8, 0x8F, 0x1F, 0x99, 0xF0, 0xFF, 0x8F, 0x6B, 0xA4, + 0xF9, 0x9F, 0x10, 0x8F, 0x99, 0x90, 0xF0, 0x55, 0xC0, 0x8A, 0xF9, 0x90, + 0xF8, 0xFD, 0x63, 0x10, 0xF9, 0x99, 0xF9, 0x9F, 0xF9, 0x9F, 0x80, 0xF9, + 0x9F, 0x20, 0xF8, 0x88, 0x47, 0x1F, 0x27, 0xC8, 0x42, 0x00, 0x99, 0x9F, + 0x99, 0x97, 0x8C, 0x6B, 0xF0, 0x96, 0x69, 0x99, 0x9F, 0x10, 0x2E, 0x8F, + 0x2B, 0x22, 0xF8, 0x89, 0xA8, 0x0F, 0xE0}; + +const GFXglyph Org_01Glyphs[] PROGMEM = {{0, 0, 0, 6, 0, 1}, // 0x20 ' ' + {0, 1, 5, 2, 0, -4}, // 0x21 '!' + {1, 3, 1, 4, 0, -4}, // 0x22 '"' + {2, 5, 5, 6, 0, -4}, // 0x23 '#' + {6, 5, 5, 6, 0, -4}, // 0x24 '$' + {10, 5, 5, 6, 0, -4}, // 0x25 '%' + {14, 5, 5, 6, 0, -4}, // 0x26 '&' + {18, 1, 1, 2, 0, -4}, // 0x27 ''' + {19, 2, 5, 3, 0, -4}, // 0x28 '(' + {21, 2, 5, 3, 0, -4}, // 0x29 ')' + {23, 3, 3, 4, 0, -3}, // 0x2A '*' + {25, 3, 3, 4, 0, -3}, // 0x2B '+' + {27, 1, 2, 2, 0, 0}, // 0x2C ',' + {28, 4, 1, 5, 0, -2}, // 0x2D '-' + {29, 1, 1, 2, 0, 0}, // 0x2E '.' + {30, 5, 5, 6, 0, -4}, // 0x2F '/' + {34, 5, 5, 6, 0, -4}, // 0x30 '0' + {38, 1, 5, 2, 0, -4}, // 0x31 '1' + {39, 5, 5, 6, 0, -4}, // 0x32 '2' + {43, 5, 5, 6, 0, -4}, // 0x33 '3' + {47, 5, 5, 6, 0, -4}, // 0x34 '4' + {51, 5, 5, 6, 0, -4}, // 0x35 '5' + {55, 5, 5, 6, 0, -4}, // 0x36 '6' + {59, 5, 5, 6, 0, -4}, // 0x37 '7' + {63, 5, 5, 6, 0, -4}, // 0x38 '8' + {67, 5, 5, 6, 0, -4}, // 0x39 '9' + {71, 1, 4, 2, 0, -3}, // 0x3A ':' + {72, 1, 4, 2, 0, -3}, // 0x3B ';' + {73, 3, 5, 4, 0, -4}, // 0x3C '<' + {75, 4, 3, 5, 0, -3}, // 0x3D '=' + {77, 3, 5, 4, 0, -4}, // 0x3E '>' + {79, 5, 5, 6, 0, -4}, // 0x3F '?' + {83, 5, 5, 6, 0, -4}, // 0x40 '@' + {87, 5, 5, 6, 0, -4}, // 0x41 'A' + {91, 5, 5, 6, 0, -4}, // 0x42 'B' + {95, 5, 5, 6, 0, -4}, // 0x43 'C' + {99, 5, 5, 6, 0, -4}, // 0x44 'D' + {103, 5, 5, 6, 0, -4}, // 0x45 'E' + {107, 5, 5, 6, 0, -4}, // 0x46 'F' + {111, 5, 5, 6, 0, -4}, // 0x47 'G' + {115, 5, 5, 6, 0, -4}, // 0x48 'H' + {119, 5, 5, 6, 0, -4}, // 0x49 'I' + {123, 5, 5, 6, 0, -4}, // 0x4A 'J' + {127, 5, 5, 6, 0, -4}, // 0x4B 'K' + {131, 5, 5, 6, 0, -4}, // 0x4C 'L' + {135, 5, 5, 6, 0, -4}, // 0x4D 'M' + {139, 5, 5, 6, 0, -4}, // 0x4E 'N' + {143, 5, 5, 6, 0, -4}, // 0x4F 'O' + {147, 5, 5, 6, 0, -4}, // 0x50 'P' + {151, 5, 5, 6, 0, -4}, // 0x51 'Q' + {155, 5, 5, 6, 0, -4}, // 0x52 'R' + {159, 5, 5, 6, 0, -4}, // 0x53 'S' + {163, 5, 5, 6, 0, -4}, // 0x54 'T' + {167, 5, 5, 6, 0, -4}, // 0x55 'U' + {171, 5, 5, 6, 0, -4}, // 0x56 'V' + {175, 5, 5, 6, 0, -4}, // 0x57 'W' + {179, 5, 5, 6, 0, -4}, // 0x58 'X' + {183, 5, 5, 6, 0, -4}, // 0x59 'Y' + {187, 5, 5, 6, 0, -4}, // 0x5A 'Z' + {191, 2, 5, 3, 0, -4}, // 0x5B '[' + {193, 5, 5, 6, 0, -4}, // 0x5C '\' + {197, 2, 5, 3, 0, -4}, // 0x5D ']' + {199, 3, 2, 4, 0, -4}, // 0x5E '^' + {200, 5, 1, 6, 0, 1}, // 0x5F '_' + {201, 1, 1, 2, 0, -4}, // 0x60 '`' + {202, 4, 4, 5, 0, -3}, // 0x61 'a' + {204, 4, 5, 5, 0, -4}, // 0x62 'b' + {207, 4, 4, 5, 0, -3}, // 0x63 'c' + {209, 4, 5, 5, 0, -4}, // 0x64 'd' + {212, 4, 4, 5, 0, -3}, // 0x65 'e' + {214, 3, 5, 4, 0, -4}, // 0x66 'f' + {216, 4, 5, 5, 0, -3}, // 0x67 'g' + {219, 4, 5, 5, 0, -4}, // 0x68 'h' + {222, 1, 4, 2, 0, -3}, // 0x69 'i' + {223, 2, 5, 3, 0, -3}, // 0x6A 'j' + {225, 4, 5, 5, 0, -4}, // 0x6B 'k' + {228, 1, 5, 2, 0, -4}, // 0x6C 'l' + {229, 5, 4, 6, 0, -3}, // 0x6D 'm' + {232, 4, 4, 5, 0, -3}, // 0x6E 'n' + {234, 4, 4, 5, 0, -3}, // 0x6F 'o' + {236, 4, 5, 5, 0, -3}, // 0x70 'p' + {239, 4, 5, 5, 0, -3}, // 0x71 'q' + {242, 4, 4, 5, 0, -3}, // 0x72 'r' + {244, 4, 4, 5, 0, -3}, // 0x73 's' + {246, 5, 5, 6, 0, -4}, // 0x74 't' + {250, 4, 4, 5, 0, -3}, // 0x75 'u' + {252, 4, 4, 5, 0, -3}, // 0x76 'v' + {254, 5, 4, 6, 0, -3}, // 0x77 'w' + {257, 4, 4, 5, 0, -3}, // 0x78 'x' + {259, 4, 5, 5, 0, -3}, // 0x79 'y' + {262, 4, 4, 5, 0, -3}, // 0x7A 'z' + {264, 3, 5, 4, 0, -4}, // 0x7B '{' + {266, 1, 5, 2, 0, -4}, // 0x7C '|' + {267, 3, 5, 4, 0, -4}, // 0x7D '}' + {269, 5, 3, 6, 0, -3}}; // 0x7E '~' + +const GFXfont Org_01 PROGMEM = {(uint8_t *)Org_01Bitmaps, + (GFXglyph *)Org_01Glyphs, 0x20, 0x7E, 7}; + +// Approx. 943 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/Picopixel.h b/libraries/Adafruit_GFX_Library/Fonts/Picopixel.h new file mode 100644 index 0000000..c1a0092 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/Picopixel.h @@ -0,0 +1,120 @@ +// Picopixel by Sebastian Weber. A tiny font +// with all characters within a 6 pixel height. + +const uint8_t PicopixelBitmaps[] PROGMEM = { + 0xE8, 0xB4, 0x57, 0xD5, 0xF5, 0x00, 0x4E, 0x3E, 0x80, 0xA5, 0x4A, 0x4A, + 0x5A, 0x50, 0xC0, 0x6A, 0x40, 0x95, 0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60, + 0xE0, 0x80, 0x25, 0x48, 0x56, 0xD4, 0x75, 0x40, 0xC5, 0x4E, 0xC5, 0x1C, + 0x97, 0x92, 0xF3, 0x1C, 0x53, 0x54, 0xE5, 0x48, 0x55, 0x54, 0x55, 0x94, + 0xA0, 0x46, 0x64, 0xE3, 0x80, 0x98, 0xC5, 0x04, 0x56, 0xC6, 0x57, 0xDA, + 0xD7, 0x5C, 0x72, 0x46, 0xD6, 0xDC, 0xF3, 0xCE, 0xF3, 0x48, 0x72, 0xD4, + 0xB7, 0xDA, 0xF8, 0x24, 0xD4, 0xBB, 0x5A, 0x92, 0x4E, 0x8E, 0xEB, 0x58, + 0x80, 0x9D, 0xB9, 0x90, 0x56, 0xD4, 0xD7, 0x48, 0x56, 0xD4, 0x40, 0xD7, + 0x5A, 0x71, 0x1C, 0xE9, 0x24, 0xB6, 0xD4, 0xB6, 0xA4, 0x8C, 0x6B, 0x55, + 0x00, 0xB5, 0x5A, 0xB5, 0x24, 0xE5, 0x4E, 0xEA, 0xC0, 0x91, 0x12, 0xD5, + 0xC0, 0x54, 0xF0, 0x90, 0xC7, 0xF0, 0x93, 0x5E, 0x71, 0x80, 0x25, 0xDE, + 0x5E, 0x30, 0x6E, 0x80, 0x77, 0x9C, 0x93, 0x5A, 0xB8, 0x45, 0x60, 0x92, + 0xEA, 0xAA, 0x40, 0xD5, 0x6A, 0xD6, 0x80, 0x55, 0x00, 0xD7, 0x40, 0x75, + 0x90, 0xE8, 0x71, 0xE0, 0xBA, 0x40, 0xB5, 0x80, 0xB5, 0x00, 0x8D, 0x54, + 0xAA, 0x80, 0xAC, 0xE0, 0xE5, 0x70, 0x6A, 0x26, 0xFC, 0xC8, 0xAC, 0x5A}; + +const GFXglyph PicopixelGlyphs[] PROGMEM = {{0, 0, 0, 2, 0, 1}, // 0x20 ' ' + {0, 1, 5, 2, 0, -4}, // 0x21 '!' + {1, 3, 2, 4, 0, -4}, // 0x22 '"' + {2, 5, 5, 6, 0, -4}, // 0x23 '#' + {6, 3, 6, 4, 0, -4}, // 0x24 '$' + {9, 3, 5, 4, 0, -4}, // 0x25 '%' + {11, 4, 5, 5, 0, -4}, // 0x26 '&' + {14, 1, 2, 2, 0, -4}, // 0x27 ''' + {15, 2, 5, 3, 0, -4}, // 0x28 '(' + {17, 2, 5, 3, 0, -4}, // 0x29 ')' + {19, 3, 3, 4, 0, -3}, // 0x2A '*' + {21, 3, 3, 4, 0, -3}, // 0x2B '+' + {23, 2, 2, 3, 0, 0}, // 0x2C ',' + {24, 3, 1, 4, 0, -2}, // 0x2D '-' + {25, 1, 1, 2, 0, 0}, // 0x2E '.' + {26, 3, 5, 4, 0, -4}, // 0x2F '/' + {28, 3, 5, 4, 0, -4}, // 0x30 '0' + {30, 2, 5, 3, 0, -4}, // 0x31 '1' + {32, 3, 5, 4, 0, -4}, // 0x32 '2' + {34, 3, 5, 4, 0, -4}, // 0x33 '3' + {36, 3, 5, 4, 0, -4}, // 0x34 '4' + {38, 3, 5, 4, 0, -4}, // 0x35 '5' + {40, 3, 5, 4, 0, -4}, // 0x36 '6' + {42, 3, 5, 4, 0, -4}, // 0x37 '7' + {44, 3, 5, 4, 0, -4}, // 0x38 '8' + {46, 3, 5, 4, 0, -4}, // 0x39 '9' + {48, 1, 3, 2, 0, -3}, // 0x3A ':' + {49, 2, 4, 3, 0, -3}, // 0x3B ';' + {50, 2, 3, 3, 0, -3}, // 0x3C '<' + {51, 3, 3, 4, 0, -3}, // 0x3D '=' + {53, 2, 3, 3, 0, -3}, // 0x3E '>' + {54, 3, 5, 4, 0, -4}, // 0x3F '?' + {56, 3, 5, 4, 0, -4}, // 0x40 '@' + {58, 3, 5, 4, 0, -4}, // 0x41 'A' + {60, 3, 5, 4, 0, -4}, // 0x42 'B' + {62, 3, 5, 4, 0, -4}, // 0x43 'C' + {64, 3, 5, 4, 0, -4}, // 0x44 'D' + {66, 3, 5, 4, 0, -4}, // 0x45 'E' + {68, 3, 5, 4, 0, -4}, // 0x46 'F' + {70, 3, 5, 4, 0, -4}, // 0x47 'G' + {72, 3, 5, 4, 0, -4}, // 0x48 'H' + {74, 1, 5, 2, 0, -4}, // 0x49 'I' + {75, 3, 5, 4, 0, -4}, // 0x4A 'J' + {77, 3, 5, 4, 0, -4}, // 0x4B 'K' + {79, 3, 5, 4, 0, -4}, // 0x4C 'L' + {81, 5, 5, 6, 0, -4}, // 0x4D 'M' + {85, 4, 5, 5, 0, -4}, // 0x4E 'N' + {88, 3, 5, 4, 0, -4}, // 0x4F 'O' + {90, 3, 5, 4, 0, -4}, // 0x50 'P' + {92, 3, 6, 4, 0, -4}, // 0x51 'Q' + {95, 3, 5, 4, 0, -4}, // 0x52 'R' + {97, 3, 5, 4, 0, -4}, // 0x53 'S' + {99, 3, 5, 4, 0, -4}, // 0x54 'T' + {101, 3, 5, 4, 0, -4}, // 0x55 'U' + {103, 3, 5, 4, 0, -4}, // 0x56 'V' + {105, 5, 5, 6, 0, -4}, // 0x57 'W' + {109, 3, 5, 4, 0, -4}, // 0x58 'X' + {111, 3, 5, 4, 0, -4}, // 0x59 'Y' + {113, 3, 5, 4, 0, -4}, // 0x5A 'Z' + {115, 2, 5, 3, 0, -4}, // 0x5B '[' + {117, 3, 5, 4, 0, -4}, // 0x5C '\' + {119, 2, 5, 3, 0, -4}, // 0x5D ']' + {121, 3, 2, 4, 0, -4}, // 0x5E '^' + {122, 4, 1, 4, 0, 1}, // 0x5F '_' + {123, 2, 2, 3, 0, -4}, // 0x60 '`' + {124, 3, 4, 4, 0, -3}, // 0x61 'a' + {126, 3, 5, 4, 0, -4}, // 0x62 'b' + {128, 3, 3, 4, 0, -2}, // 0x63 'c' + {130, 3, 5, 4, 0, -4}, // 0x64 'd' + {132, 3, 4, 4, 0, -3}, // 0x65 'e' + {134, 2, 5, 3, 0, -4}, // 0x66 'f' + {136, 3, 5, 4, 0, -3}, // 0x67 'g' + {138, 3, 5, 4, 0, -4}, // 0x68 'h' + {140, 1, 5, 2, 0, -4}, // 0x69 'i' + {141, 2, 6, 3, 0, -4}, // 0x6A 'j' + {143, 3, 5, 4, 0, -4}, // 0x6B 'k' + {145, 2, 5, 3, 0, -4}, // 0x6C 'l' + {147, 5, 3, 6, 0, -2}, // 0x6D 'm' + {149, 3, 3, 4, 0, -2}, // 0x6E 'n' + {151, 3, 3, 4, 0, -2}, // 0x6F 'o' + {153, 3, 4, 4, 0, -2}, // 0x70 'p' + {155, 3, 4, 4, 0, -2}, // 0x71 'q' + {157, 2, 3, 3, 0, -2}, // 0x72 'r' + {158, 3, 4, 4, 0, -3}, // 0x73 's' + {160, 2, 5, 3, 0, -4}, // 0x74 't' + {162, 3, 3, 4, 0, -2}, // 0x75 'u' + {164, 3, 3, 4, 0, -2}, // 0x76 'v' + {166, 5, 3, 6, 0, -2}, // 0x77 'w' + {168, 3, 3, 4, 0, -2}, // 0x78 'x' + {170, 3, 4, 4, 0, -2}, // 0x79 'y' + {172, 3, 4, 4, 0, -3}, // 0x7A 'z' + {174, 3, 5, 4, 0, -4}, // 0x7B '{' + {176, 1, 6, 2, 0, -4}, // 0x7C '|' + {177, 3, 5, 4, 0, -4}, // 0x7D '}' + {179, 4, 2, 5, 0, -3}}; // 0x7E '~' + +const GFXfont Picopixel PROGMEM = {(uint8_t *)PicopixelBitmaps, + (GFXglyph *)PicopixelGlyphs, 0x20, 0x7E, 7}; + +// Approx. 852 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/Tiny3x3a2pt7b.h b/libraries/Adafruit_GFX_Library/Fonts/Tiny3x3a2pt7b.h new file mode 100644 index 0000000..c3071d7 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/Tiny3x3a2pt7b.h @@ -0,0 +1,130 @@ +/** +** The FontStruction “Tiny3x3a” +** (https://fontstruct.com/fontstructions/show/670512) by “Michaelangel007” is +** licensed under a Creative Commons Attribution Non-commercial Share Alike +*license +** (http://creativecommons.org/licenses/by-nc-sa/3.0/). +** “Tiny3x3a” was originally cloned (copied) from the FontStruction +** “CHECKER” (https://fontstruct.com/fontstructions/show/2391) by Wolf grant +** Grant, which is licensed under a Creative Commons Attribution Non-commercial +** Share Alike license (http://creativecommons.org/licenses/by-nc-sa/3.0/). +* +* Converted by eadmaster with fontconvert +**/ + +const uint8_t Tiny3x3a2pt7bBitmaps[] PROGMEM = { + 0xC0, 0xB4, 0xBF, 0x80, 0x6B, 0x00, 0xDD, 0x80, 0x59, 0x80, 0x80, 0x64, + 0x98, 0xF0, 0x5D, 0x00, 0xC0, 0xE0, 0x80, 0x2A, 0x00, 0x55, 0x00, 0x94, + 0xC9, 0x80, 0xEF, 0x80, 0xBC, 0x80, 0x6B, 0x00, 0x9F, 0x80, 0xE4, 0x80, + 0x7F, 0x00, 0xFC, 0x80, 0xA0, 0x58, 0x64, 0xE3, 0x80, 0x98, 0xD8, 0xD8, + 0x80, 0x5E, 0x80, 0xDF, 0x80, 0x71, 0x80, 0xD7, 0x00, 0xFB, 0x80, 0xFA, + 0x00, 0xD7, 0x80, 0xBE, 0x80, 0xE0, 0x27, 0x00, 0xBA, 0x80, 0x93, 0x80, + 0xFE, 0x80, 0xF6, 0x80, 0xF7, 0x80, 0xFE, 0x00, 0xF7, 0x00, 0xDE, 0x80, + 0x6B, 0x00, 0xE9, 0x00, 0xB7, 0x80, 0xB5, 0x00, 0xBF, 0x80, 0xAA, 0x80, + 0xA9, 0x00, 0xEB, 0x80, 0xEC, 0x88, 0x80, 0xDC, 0x54, 0xE0, 0x90, 0x70, + 0xBC, 0xF0, 0x7C, 0xB0, 0x68, 0xFC, 0xBC, 0xC0, 0x58, 0x9A, 0x80, 0xA4, + 0xDC, 0xD4, 0xF0, 0xF8, 0xF4, 0xE0, 0x60, 0x59, 0x80, 0xBC, 0xA8, 0xEC, + 0xF0, 0xAC, 0x80, 0x90, 0x79, 0x80, 0xF0, 0xCF, 0x00, 0x78}; + +const GFXglyph Tiny3x3a2pt7bGlyphs[] PROGMEM = { + {0, 0, 0, 4, 0, 1}, // 0x20 ' ' + {0, 1, 2, 3, 1, -2}, // 0x21 '!' + {1, 3, 2, 4, 0, -2}, // 0x22 '"' + {2, 3, 3, 4, 0, -2}, // 0x23 '#' + {4, 3, 3, 4, 0, -2}, // 0x24 '$' + {6, 3, 3, 4, 0, -2}, // 0x25 '%' + {8, 3, 3, 4, 0, -2}, // 0x26 '&' + {10, 1, 1, 3, 1, -2}, // 0x27 ''' + {11, 2, 3, 3, 0, -2}, // 0x28 '(' + {12, 2, 3, 4, 1, -2}, // 0x29 ')' + {13, 2, 2, 4, 1, -2}, // 0x2A '*' + {14, 3, 3, 4, 0, -2}, // 0x2B '+' + {16, 1, 2, 2, 0, 0}, // 0x2C ',' + {17, 3, 1, 4, 0, -1}, // 0x2D '-' + {18, 1, 1, 2, 0, 0}, // 0x2E '.' + {19, 3, 3, 4, 0, -2}, // 0x2F '/' + {21, 3, 3, 4, 0, -2}, // 0x30 '0' + {23, 2, 3, 3, 0, -2}, // 0x31 '1' + {24, 3, 3, 4, 0, -2}, // 0x32 '2' + {26, 3, 3, 4, 0, -2}, // 0x33 '3' + {28, 3, 3, 4, 0, -2}, // 0x34 '4' + {30, 3, 3, 4, 0, -2}, // 0x35 '5' + {32, 3, 3, 4, 0, -2}, // 0x36 '6' + {34, 3, 3, 4, 0, -2}, // 0x37 '7' + {36, 3, 3, 4, 0, -2}, // 0x38 '8' + {38, 3, 3, 4, 0, -2}, // 0x39 '9' + {40, 1, 3, 3, 1, -2}, // 0x3A ':' + {41, 2, 3, 3, 0, -1}, // 0x3B ';' + {42, 2, 3, 3, 0, -2}, // 0x3C '<' + {43, 3, 3, 4, 0, -2}, // 0x3D '=' + {45, 2, 3, 4, 1, -2}, // 0x3E '>' + {46, 2, 3, 4, 1, -2}, // 0x3F '?' + {47, 3, 3, 4, 0, -2}, // 0x40 '@' + {49, 3, 3, 4, 0, -2}, // 0x41 'A' + {51, 3, 3, 4, 0, -2}, // 0x42 'B' + {53, 3, 3, 4, 0, -2}, // 0x43 'C' + {55, 3, 3, 4, 0, -2}, // 0x44 'D' + {57, 3, 3, 4, 0, -2}, // 0x45 'E' + {59, 3, 3, 4, 0, -2}, // 0x46 'F' + {61, 3, 3, 4, 0, -2}, // 0x47 'G' + {63, 3, 3, 4, 0, -2}, // 0x48 'H' + {65, 1, 3, 3, 1, -2}, // 0x49 'I' + {66, 3, 3, 4, 0, -2}, // 0x4A 'J' + {68, 3, 3, 4, 0, -2}, // 0x4B 'K' + {70, 3, 3, 4, 0, -2}, // 0x4C 'L' + {72, 3, 3, 4, 0, -2}, // 0x4D 'M' + {74, 3, 3, 4, 0, -2}, // 0x4E 'N' + {76, 3, 3, 4, 0, -2}, // 0x4F 'O' + {78, 3, 3, 4, 0, -2}, // 0x50 'P' + {80, 3, 3, 4, 0, -2}, // 0x51 'Q' + {82, 3, 3, 4, 0, -2}, // 0x52 'R' + {84, 3, 3, 4, 0, -2}, // 0x53 'S' + {86, 3, 3, 4, 0, -2}, // 0x54 'T' + {88, 3, 3, 4, 0, -2}, // 0x55 'U' + {90, 3, 3, 4, 0, -2}, // 0x56 'V' + {92, 3, 3, 4, 0, -2}, // 0x57 'W' + {94, 3, 3, 4, 0, -2}, // 0x58 'X' + {96, 3, 3, 4, 0, -2}, // 0x59 'Y' + {98, 3, 3, 4, 0, -2}, // 0x5A 'Z' + {100, 2, 3, 3, 0, -2}, // 0x5B '[' + {101, 3, 3, 4, 0, -2}, // 0x5C '\' + {103, 2, 3, 4, 1, -2}, // 0x5D ']' + {104, 3, 2, 4, 0, -2}, // 0x5E '^' + {105, 3, 1, 4, 0, 0}, // 0x5F '_' + {106, 2, 2, 3, 0, -2}, // 0x60 '`' + {107, 2, 2, 3, 0, -1}, // 0x61 'a' + {108, 2, 3, 3, 0, -2}, // 0x62 'b' + {109, 2, 2, 3, 0, -1}, // 0x63 'c' + {110, 2, 3, 3, 0, -2}, // 0x64 'd' + {111, 2, 2, 3, 0, -1}, // 0x65 'e' + {112, 2, 3, 3, 0, -2}, // 0x66 'f' + {113, 2, 3, 3, 0, -1}, // 0x67 'g' + {114, 2, 3, 3, 0, -2}, // 0x68 'h' + {115, 1, 2, 2, 0, -1}, // 0x69 'i' + {116, 2, 3, 3, 0, -1}, // 0x6A 'j' + {117, 3, 3, 4, 0, -2}, // 0x6B 'k' + {119, 2, 3, 3, 0, -2}, // 0x6C 'l' + {120, 3, 2, 4, 0, -1}, // 0x6D 'm' + {121, 3, 2, 4, 0, -1}, // 0x6E 'n' + {122, 2, 2, 3, 0, -1}, // 0x6F 'o' + {123, 2, 3, 3, 0, -1}, // 0x70 'p' + {124, 2, 3, 3, 0, -1}, // 0x71 'q' + {125, 2, 2, 3, 0, -1}, // 0x72 'r' + {126, 2, 2, 3, 0, -1}, // 0x73 's' + {127, 3, 3, 4, 0, -2}, // 0x74 't' + {129, 3, 2, 4, 0, -1}, // 0x75 'u' + {130, 3, 2, 4, 0, -1}, // 0x76 'v' + {131, 3, 2, 4, 0, -1}, // 0x77 'w' + {132, 2, 2, 3, 0, -1}, // 0x78 'x' + {133, 3, 3, 4, 0, -1}, // 0x79 'y' + {135, 2, 2, 3, 0, -1}, // 0x7A 'z' + {136, 3, 3, 4, 0, -2}, // 0x7B '{' + {138, 1, 4, 3, 1, -2}, // 0x7C '|' + {139, 3, 3, 4, 0, -2}, // 0x7D '}' + {141, 3, 2, 4, 0, -2}}; // 0x7E '~' + +const GFXfont Tiny3x3a2pt7b PROGMEM = {(uint8_t *)Tiny3x3a2pt7bBitmaps, + (GFXglyph *)Tiny3x3a2pt7bGlyphs, 0x20, + 0x7E, 4}; + +// Approx. 814 bytes diff --git a/libraries/Adafruit_GFX_Library/Fonts/TomThumb.h b/libraries/Adafruit_GFX_Library/Fonts/TomThumb.h new file mode 100644 index 0000000..8bc4339 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/Fonts/TomThumb.h @@ -0,0 +1,471 @@ +/** +** The original 3x5 font is licensed under the 3-clause BSD license: +** +** Copyright 1999 Brian J. Swetland +** Copyright 1999 Vassilii Khachaturov +** Portions (of vt100.c/vt100.h) copyright Dan Marks +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions, and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions, and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the authors may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +** Modifications to Tom Thumb for improved readability are from Robey Pointer, +** see: +** http://robey.lag.net/2010/01/23/tiny-monospace-font.html +** +** The original author does not have any objection to relicensing of Robey +** Pointer's modifications (in this file) in a more permissive license. See +** the discussion at the above blog, and also here: +** http://opengameart.org/forumtopic/how-to-submit-art-using-the-3-clause-bsd-license +** +** Feb 21, 2016: Conversion from Linux BDF --> Adafruit GFX font, +** with the help of this Python script: +** https://gist.github.com/skelliam/322d421f028545f16f6d +** William Skellenger (williamj@skellenger.net) +** Twitter: @skelliam +** +*/ + +#define TOMTHUMB_USE_EXTENDED 0 + +const uint8_t TomThumbBitmaps[] PROGMEM = { + 0x00, /* 0x20 space */ + 0x80, 0x80, 0x80, 0x00, 0x80, /* 0x21 exclam */ + 0xA0, 0xA0, /* 0x22 quotedbl */ + 0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /* 0x23 numbersign */ + 0x60, 0xC0, 0x60, 0xC0, 0x40, /* 0x24 dollar */ + 0x80, 0x20, 0x40, 0x80, 0x20, /* 0x25 percent */ + 0xC0, 0xC0, 0xE0, 0xA0, 0x60, /* 0x26 ampersand */ + 0x80, 0x80, /* 0x27 quotesingle */ + 0x40, 0x80, 0x80, 0x80, 0x40, /* 0x28 parenleft */ + 0x80, 0x40, 0x40, 0x40, 0x80, /* 0x29 parenright */ + 0xA0, 0x40, 0xA0, /* 0x2A asterisk */ + 0x40, 0xE0, 0x40, /* 0x2B plus */ + 0x40, 0x80, /* 0x2C comma */ + 0xE0, /* 0x2D hyphen */ + 0x80, /* 0x2E period */ + 0x20, 0x20, 0x40, 0x80, 0x80, /* 0x2F slash */ + 0x60, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x30 zero */ + 0x40, 0xC0, 0x40, 0x40, 0x40, /* 0x31 one */ + 0xC0, 0x20, 0x40, 0x80, 0xE0, /* 0x32 two */ + 0xC0, 0x20, 0x40, 0x20, 0xC0, /* 0x33 three */ + 0xA0, 0xA0, 0xE0, 0x20, 0x20, /* 0x34 four */ + 0xE0, 0x80, 0xC0, 0x20, 0xC0, /* 0x35 five */ + 0x60, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */ + 0xE0, 0x20, 0x40, 0x80, 0x80, /* 0x37 seven */ + 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /* 0x38 eight */ + 0xE0, 0xA0, 0xE0, 0x20, 0xC0, /* 0x39 nine */ + 0x80, 0x00, 0x80, /* 0x3A colon */ + 0x40, 0x00, 0x40, 0x80, /* 0x3B semicolon */ + 0x20, 0x40, 0x80, 0x40, 0x20, /* 0x3C less */ + 0xE0, 0x00, 0xE0, /* 0x3D equal */ + 0x80, 0x40, 0x20, 0x40, 0x80, /* 0x3E greater */ + 0xE0, 0x20, 0x40, 0x00, 0x40, /* 0x3F question */ + 0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */ + 0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */ + 0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */ + 0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */ + 0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */ + 0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */ + 0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */ + 0x60, 0x80, 0xE0, 0xA0, 0x60, /* 0x47 G */ + 0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x48 H */ + 0xE0, 0x40, 0x40, 0x40, 0xE0, /* 0x49 I */ + 0x20, 0x20, 0x20, 0xA0, 0x40, /* 0x4A J */ + 0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /* 0x4B K */ + 0x80, 0x80, 0x80, 0x80, 0xE0, /* 0x4C L */ + 0xA0, 0xE0, 0xE0, 0xA0, 0xA0, /* 0x4D M */ + 0xA0, 0xE0, 0xE0, 0xE0, 0xA0, /* 0x4E N */ + 0x40, 0xA0, 0xA0, 0xA0, 0x40, /* 0x4F O */ + 0xC0, 0xA0, 0xC0, 0x80, 0x80, /* 0x50 P */ + 0x40, 0xA0, 0xA0, 0xE0, 0x60, /* 0x51 Q */ + 0xC0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */ + 0x60, 0x80, 0x40, 0x20, 0xC0, /* 0x53 S */ + 0xE0, 0x40, 0x40, 0x40, 0x40, /* 0x54 T */ + 0xA0, 0xA0, 0xA0, 0xA0, 0x60, /* 0x55 U */ + 0xA0, 0xA0, 0xA0, 0x40, 0x40, /* 0x56 V */ + 0xA0, 0xA0, 0xE0, 0xE0, 0xA0, /* 0x57 W */ + 0xA0, 0xA0, 0x40, 0xA0, 0xA0, /* 0x58 X */ + 0xA0, 0xA0, 0x40, 0x40, 0x40, /* 0x59 Y */ + 0xE0, 0x20, 0x40, 0x80, 0xE0, /* 0x5A Z */ + 0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x5B bracketleft */ + 0x80, 0x40, 0x20, /* 0x5C backslash */ + 0xE0, 0x20, 0x20, 0x20, 0xE0, /* 0x5D bracketright */ + 0x40, 0xA0, /* 0x5E asciicircum */ + 0xE0, /* 0x5F underscore */ + 0x80, 0x40, /* 0x60 grave */ + 0xC0, 0x60, 0xA0, 0xE0, /* 0x61 a */ + 0x80, 0xC0, 0xA0, 0xA0, 0xC0, /* 0x62 b */ + 0x60, 0x80, 0x80, 0x60, /* 0x63 c */ + 0x20, 0x60, 0xA0, 0xA0, 0x60, /* 0x64 d */ + 0x60, 0xA0, 0xC0, 0x60, /* 0x65 e */ + 0x20, 0x40, 0xE0, 0x40, 0x40, /* 0x66 f */ + 0x60, 0xA0, 0xE0, 0x20, 0x40, /* 0x67 g */ + 0x80, 0xC0, 0xA0, 0xA0, 0xA0, /* 0x68 h */ + 0x80, 0x00, 0x80, 0x80, 0x80, /* 0x69 i */ + 0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /* 0x6A j */ + 0x80, 0xA0, 0xC0, 0xC0, 0xA0, /* 0x6B k */ + 0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x6C l */ + 0xE0, 0xE0, 0xE0, 0xA0, /* 0x6D m */ + 0xC0, 0xA0, 0xA0, 0xA0, /* 0x6E n */ + 0x40, 0xA0, 0xA0, 0x40, /* 0x6F o */ + 0xC0, 0xA0, 0xA0, 0xC0, 0x80, /* 0x70 p */ + 0x60, 0xA0, 0xA0, 0x60, 0x20, /* 0x71 q */ + 0x60, 0x80, 0x80, 0x80, /* 0x72 r */ + 0x60, 0xC0, 0x60, 0xC0, /* 0x73 s */ + 0x40, 0xE0, 0x40, 0x40, 0x60, /* 0x74 t */ + 0xA0, 0xA0, 0xA0, 0x60, /* 0x75 u */ + 0xA0, 0xA0, 0xE0, 0x40, /* 0x76 v */ + 0xA0, 0xE0, 0xE0, 0xE0, /* 0x77 w */ + 0xA0, 0x40, 0x40, 0xA0, /* 0x78 x */ + 0xA0, 0xA0, 0x60, 0x20, 0x40, /* 0x79 y */ + 0xE0, 0x60, 0xC0, 0xE0, /* 0x7A z */ + 0x60, 0x40, 0x80, 0x40, 0x60, /* 0x7B braceleft */ + 0x80, 0x80, 0x00, 0x80, 0x80, /* 0x7C bar */ + 0xC0, 0x40, 0x20, 0x40, 0xC0, /* 0x7D braceright */ + 0x60, 0xC0, /* 0x7E asciitilde */ +#if (TOMTHUMB_USE_EXTENDED) + 0x80, 0x00, 0x80, 0x80, 0x80, /* 0xA1 exclamdown */ + 0x40, 0xE0, 0x80, 0xE0, 0x40, /* 0xA2 cent */ + 0x60, 0x40, 0xE0, 0x40, 0xE0, /* 0xA3 sterling */ + 0xA0, 0x40, 0xE0, 0x40, 0xA0, /* 0xA4 currency */ + 0xA0, 0xA0, 0x40, 0xE0, 0x40, /* 0xA5 yen */ + 0x80, 0x80, 0x00, 0x80, 0x80, /* 0xA6 brokenbar */ + 0x60, 0x40, 0xA0, 0x40, 0xC0, /* 0xA7 section */ + 0xA0, /* 0xA8 dieresis */ + 0x60, 0x80, 0x60, /* 0xA9 copyright */ + 0x60, 0xA0, 0xE0, 0x00, 0xE0, /* 0xAA ordfeminine */ + 0x40, 0x80, 0x40, /* 0xAB guillemotleft */ + 0xE0, 0x20, /* 0xAC logicalnot */ + 0xC0, /* 0xAD softhyphen */ + 0xC0, 0xC0, 0xA0, /* 0xAE registered */ + 0xE0, /* 0xAF macron */ + 0x40, 0xA0, 0x40, /* 0xB0 degree */ + 0x40, 0xE0, 0x40, 0x00, 0xE0, /* 0xB1 plusminus */ + 0xC0, 0x40, 0x60, /* 0xB2 twosuperior */ + 0xE0, 0x60, 0xE0, /* 0xB3 threesuperior */ + 0x40, 0x80, /* 0xB4 acute */ + 0xA0, 0xA0, 0xA0, 0xC0, 0x80, /* 0xB5 mu */ + 0x60, 0xA0, 0x60, 0x60, 0x60, /* 0xB6 paragraph */ + 0xE0, 0xE0, 0xE0, /* 0xB7 periodcentered */ + 0x40, 0x20, 0xC0, /* 0xB8 cedilla */ + 0x80, 0x80, 0x80, /* 0xB9 onesuperior */ + 0x40, 0xA0, 0x40, 0x00, 0xE0, /* 0xBA ordmasculine */ + 0x80, 0x40, 0x80, /* 0xBB guillemotright */ + 0x80, 0x80, 0x00, 0x60, 0x20, /* 0xBC onequarter */ + 0x80, 0x80, 0x00, 0xC0, 0x60, /* 0xBD onehalf */ + 0xC0, 0xC0, 0x00, 0x60, 0x20, /* 0xBE threequarters */ + 0x40, 0x00, 0x40, 0x80, 0xE0, /* 0xBF questiondown */ + 0x40, 0x20, 0x40, 0xE0, 0xA0, /* 0xC0 Agrave */ + 0x40, 0x80, 0x40, 0xE0, 0xA0, /* 0xC1 Aacute */ + 0xE0, 0x00, 0x40, 0xE0, 0xA0, /* 0xC2 Acircumflex */ + 0x60, 0xC0, 0x40, 0xE0, 0xA0, /* 0xC3 Atilde */ + 0xA0, 0x40, 0xA0, 0xE0, 0xA0, /* 0xC4 Adieresis */ + 0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /* 0xC5 Aring */ + 0x60, 0xC0, 0xE0, 0xC0, 0xE0, /* 0xC6 AE */ + 0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /* 0xC7 Ccedilla */ + 0x40, 0x20, 0xE0, 0xC0, 0xE0, /* 0xC8 Egrave */ + 0x40, 0x80, 0xE0, 0xC0, 0xE0, /* 0xC9 Eacute */ + 0xE0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCA Ecircumflex */ + 0xA0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCB Edieresis */ + 0x40, 0x20, 0xE0, 0x40, 0xE0, /* 0xCC Igrave */ + 0x40, 0x80, 0xE0, 0x40, 0xE0, /* 0xCD Iacute */ + 0xE0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCE Icircumflex */ + 0xA0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCF Idieresis */ + 0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD0 Eth */ + 0xC0, 0x60, 0xA0, 0xE0, 0xA0, /* 0xD1 Ntilde */ + 0x40, 0x20, 0xE0, 0xA0, 0xE0, /* 0xD2 Ograve */ + 0x40, 0x80, 0xE0, 0xA0, 0xE0, /* 0xD3 Oacute */ + 0xE0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD4 Ocircumflex */ + 0xC0, 0x60, 0xE0, 0xA0, 0xE0, /* 0xD5 Otilde */ + 0xA0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD6 Odieresis */ + 0xA0, 0x40, 0xA0, /* 0xD7 multiply */ + 0x60, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD8 Oslash */ + 0x80, 0x40, 0xA0, 0xA0, 0xE0, /* 0xD9 Ugrave */ + 0x20, 0x40, 0xA0, 0xA0, 0xE0, /* 0xDA Uacute */ + 0xE0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDB Ucircumflex */ + 0xA0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDC Udieresis */ + 0x20, 0x40, 0xA0, 0xE0, 0x40, /* 0xDD Yacute */ + 0x80, 0xE0, 0xA0, 0xE0, 0x80, /* 0xDE Thorn */ + 0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /* 0xDF germandbls */ + 0x40, 0x20, 0x60, 0xA0, 0xE0, /* 0xE0 agrave */ + 0x40, 0x80, 0x60, 0xA0, 0xE0, /* 0xE1 aacute */ + 0xE0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE2 acircumflex */ + 0x60, 0xC0, 0x60, 0xA0, 0xE0, /* 0xE3 atilde */ + 0xA0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE4 adieresis */ + 0x60, 0x60, 0x60, 0xA0, 0xE0, /* 0xE5 aring */ + 0x60, 0xE0, 0xE0, 0xC0, /* 0xE6 ae */ + 0x60, 0x80, 0x60, 0x20, 0x40, /* 0xE7 ccedilla */ + 0x40, 0x20, 0x60, 0xE0, 0x60, /* 0xE8 egrave */ + 0x40, 0x80, 0x60, 0xE0, 0x60, /* 0xE9 eacute */ + 0xE0, 0x00, 0x60, 0xE0, 0x60, /* 0xEA ecircumflex */ + 0xA0, 0x00, 0x60, 0xE0, 0x60, /* 0xEB edieresis */ + 0x80, 0x40, 0x80, 0x80, 0x80, /* 0xEC igrave */ + 0x40, 0x80, 0x40, 0x40, 0x40, /* 0xED iacute */ + 0xE0, 0x00, 0x40, 0x40, 0x40, /* 0xEE icircumflex */ + 0xA0, 0x00, 0x40, 0x40, 0x40, /* 0xEF idieresis */ + 0x60, 0xC0, 0x60, 0xA0, 0x60, /* 0xF0 eth */ + 0xC0, 0x60, 0xC0, 0xA0, 0xA0, /* 0xF1 ntilde */ + 0x40, 0x20, 0x40, 0xA0, 0x40, /* 0xF2 ograve */ + 0x40, 0x80, 0x40, 0xA0, 0x40, /* 0xF3 oacute */ + 0xE0, 0x00, 0x40, 0xA0, 0x40, /* 0xF4 ocircumflex */ + 0xC0, 0x60, 0x40, 0xA0, 0x40, /* 0xF5 otilde */ + 0xA0, 0x00, 0x40, 0xA0, 0x40, /* 0xF6 odieresis */ + 0x40, 0x00, 0xE0, 0x00, 0x40, /* 0xF7 divide */ + 0x60, 0xE0, 0xA0, 0xC0, /* 0xF8 oslash */ + 0x80, 0x40, 0xA0, 0xA0, 0x60, /* 0xF9 ugrave */ + 0x20, 0x40, 0xA0, 0xA0, 0x60, /* 0xFA uacute */ + 0xE0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFB ucircumflex */ + 0xA0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFC udieresis */ + 0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /* 0xFD yacute */ + 0x80, 0xC0, 0xA0, 0xC0, 0x80, /* 0xFE thorn */ + 0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /* 0xFF ydieresis */ + 0x00, /* 0x11D gcircumflex */ + 0x60, 0xC0, 0xE0, 0xC0, 0x60, /* 0x152 OE */ + 0x60, 0xE0, 0xC0, 0xE0, /* 0x153 oe */ + 0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x160 Scaron */ + 0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x161 scaron */ + 0xA0, 0x00, 0xA0, 0x40, 0x40, /* 0x178 Ydieresis */ + 0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17D Zcaron */ + 0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17E zcaron */ + 0x00, /* 0xEA4 uni0EA4 */ + 0x00, /* 0x13A0 uni13A0 */ + 0x80, /* 0x2022 bullet */ + 0xA0, /* 0x2026 ellipsis */ + 0x60, 0xE0, 0xE0, 0xC0, 0x60, /* 0x20AC Euro */ + 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0xFFFD uniFFFD */ +#endif /* (TOMTHUMB_USE_EXTENDED) */ +}; + +/* {offset, width, height, advance cursor, x offset, y offset} */ +const GFXglyph TomThumbGlyphs[] PROGMEM = { + {0, 8, 1, 2, 0, -5}, /* 0x20 space */ + {1, 8, 5, 2, 0, -5}, /* 0x21 exclam */ + {6, 8, 2, 4, 0, -5}, /* 0x22 quotedbl */ + {8, 8, 5, 4, 0, -5}, /* 0x23 numbersign */ + {13, 8, 5, 4, 0, -5}, /* 0x24 dollar */ + {18, 8, 5, 4, 0, -5}, /* 0x25 percent */ + {23, 8, 5, 4, 0, -5}, /* 0x26 ampersand */ + {28, 8, 2, 2, 0, -5}, /* 0x27 quotesingle */ + {30, 8, 5, 3, 0, -5}, /* 0x28 parenleft */ + {35, 8, 5, 3, 0, -5}, /* 0x29 parenright */ + {40, 8, 3, 4, 0, -5}, /* 0x2A asterisk */ + {43, 8, 3, 4, 0, -4}, /* 0x2B plus */ + {46, 8, 2, 3, 0, -2}, /* 0x2C comma */ + {48, 8, 1, 4, 0, -3}, /* 0x2D hyphen */ + {49, 8, 1, 2, 0, -1}, /* 0x2E period */ + {50, 8, 5, 4, 0, -5}, /* 0x2F slash */ + {55, 8, 5, 4, 0, -5}, /* 0x30 zero */ + {60, 8, 5, 3, 0, -5}, /* 0x31 one */ + {65, 8, 5, 4, 0, -5}, /* 0x32 two */ + {70, 8, 5, 4, 0, -5}, /* 0x33 three */ + {75, 8, 5, 4, 0, -5}, /* 0x34 four */ + {80, 8, 5, 4, 0, -5}, /* 0x35 five */ + {85, 8, 5, 4, 0, -5}, /* 0x36 six */ + {90, 8, 5, 4, 0, -5}, /* 0x37 seven */ + {95, 8, 5, 4, 0, -5}, /* 0x38 eight */ + {100, 8, 5, 4, 0, -5}, /* 0x39 nine */ + {105, 8, 3, 2, 0, -4}, /* 0x3A colon */ + {108, 8, 4, 3, 0, -4}, /* 0x3B semicolon */ + {112, 8, 5, 4, 0, -5}, /* 0x3C less */ + {117, 8, 3, 4, 0, -4}, /* 0x3D equal */ + {120, 8, 5, 4, 0, -5}, /* 0x3E greater */ + {125, 8, 5, 4, 0, -5}, /* 0x3F question */ + {130, 8, 5, 4, 0, -5}, /* 0x40 at */ + {135, 8, 5, 4, 0, -5}, /* 0x41 A */ + {140, 8, 5, 4, 0, -5}, /* 0x42 B */ + {145, 8, 5, 4, 0, -5}, /* 0x43 C */ + {150, 8, 5, 4, 0, -5}, /* 0x44 D */ + {155, 8, 5, 4, 0, -5}, /* 0x45 E */ + {160, 8, 5, 4, 0, -5}, /* 0x46 F */ + {165, 8, 5, 4, 0, -5}, /* 0x47 G */ + {170, 8, 5, 4, 0, -5}, /* 0x48 H */ + {175, 8, 5, 4, 0, -5}, /* 0x49 I */ + {180, 8, 5, 4, 0, -5}, /* 0x4A J */ + {185, 8, 5, 4, 0, -5}, /* 0x4B K */ + {190, 8, 5, 4, 0, -5}, /* 0x4C L */ + {195, 8, 5, 4, 0, -5}, /* 0x4D M */ + {200, 8, 5, 4, 0, -5}, /* 0x4E N */ + {205, 8, 5, 4, 0, -5}, /* 0x4F O */ + {210, 8, 5, 4, 0, -5}, /* 0x50 P */ + {215, 8, 5, 4, 0, -5}, /* 0x51 Q */ + {220, 8, 5, 4, 0, -5}, /* 0x52 R */ + {225, 8, 5, 4, 0, -5}, /* 0x53 S */ + {230, 8, 5, 4, 0, -5}, /* 0x54 T */ + {235, 8, 5, 4, 0, -5}, /* 0x55 U */ + {240, 8, 5, 4, 0, -5}, /* 0x56 V */ + {245, 8, 5, 4, 0, -5}, /* 0x57 W */ + {250, 8, 5, 4, 0, -5}, /* 0x58 X */ + {255, 8, 5, 4, 0, -5}, /* 0x59 Y */ + {260, 8, 5, 4, 0, -5}, /* 0x5A Z */ + {265, 8, 5, 4, 0, -5}, /* 0x5B bracketleft */ + {270, 8, 3, 4, 0, -4}, /* 0x5C backslash */ + {273, 8, 5, 4, 0, -5}, /* 0x5D bracketright */ + {278, 8, 2, 4, 0, -5}, /* 0x5E asciicircum */ + {280, 8, 1, 4, 0, -1}, /* 0x5F underscore */ + {281, 8, 2, 3, 0, -5}, /* 0x60 grave */ + {283, 8, 4, 4, 0, -4}, /* 0x61 a */ + {287, 8, 5, 4, 0, -5}, /* 0x62 b */ + {292, 8, 4, 4, 0, -4}, /* 0x63 c */ + {296, 8, 5, 4, 0, -5}, /* 0x64 d */ + {301, 8, 4, 4, 0, -4}, /* 0x65 e */ + {305, 8, 5, 4, 0, -5}, /* 0x66 f */ + {310, 8, 5, 4, 0, -4}, /* 0x67 g */ + {315, 8, 5, 4, 0, -5}, /* 0x68 h */ + {320, 8, 5, 2, 0, -5}, /* 0x69 i */ + {325, 8, 6, 4, 0, -5}, /* 0x6A j */ + {331, 8, 5, 4, 0, -5}, /* 0x6B k */ + {336, 8, 5, 4, 0, -5}, /* 0x6C l */ + {341, 8, 4, 4, 0, -4}, /* 0x6D m */ + {345, 8, 4, 4, 0, -4}, /* 0x6E n */ + {349, 8, 4, 4, 0, -4}, /* 0x6F o */ + {353, 8, 5, 4, 0, -4}, /* 0x70 p */ + {358, 8, 5, 4, 0, -4}, /* 0x71 q */ + {363, 8, 4, 4, 0, -4}, /* 0x72 r */ + {367, 8, 4, 4, 0, -4}, /* 0x73 s */ + {371, 8, 5, 4, 0, -5}, /* 0x74 t */ + {376, 8, 4, 4, 0, -4}, /* 0x75 u */ + {380, 8, 4, 4, 0, -4}, /* 0x76 v */ + {384, 8, 4, 4, 0, -4}, /* 0x77 w */ + {388, 8, 4, 4, 0, -4}, /* 0x78 x */ + {392, 8, 5, 4, 0, -4}, /* 0x79 y */ + {397, 8, 4, 4, 0, -4}, /* 0x7A z */ + {401, 8, 5, 4, 0, -5}, /* 0x7B braceleft */ + {406, 8, 5, 2, 0, -5}, /* 0x7C bar */ + {411, 8, 5, 4, 0, -5}, /* 0x7D braceright */ + {416, 8, 2, 4, 0, -5}, /* 0x7E asciitilde */ +#if (TOMTHUMB_USE_EXTENDED) + {418, 8, 5, 2, 0, -5}, /* 0xA1 exclamdown */ + {423, 8, 5, 4, 0, -5}, /* 0xA2 cent */ + {428, 8, 5, 4, 0, -5}, /* 0xA3 sterling */ + {433, 8, 5, 4, 0, -5}, /* 0xA4 currency */ + {438, 8, 5, 4, 0, -5}, /* 0xA5 yen */ + {443, 8, 5, 2, 0, -5}, /* 0xA6 brokenbar */ + {448, 8, 5, 4, 0, -5}, /* 0xA7 section */ + {453, 8, 1, 4, 0, -5}, /* 0xA8 dieresis */ + {454, 8, 3, 4, 0, -5}, /* 0xA9 copyright */ + {457, 8, 5, 4, 0, -5}, /* 0xAA ordfeminine */ + {462, 8, 3, 3, 0, -5}, /* 0xAB guillemotleft */ + {465, 8, 2, 4, 0, -4}, /* 0xAC logicalnot */ + {467, 8, 1, 3, 0, -3}, /* 0xAD softhyphen */ + {468, 8, 3, 4, 0, -5}, /* 0xAE registered */ + {471, 8, 1, 4, 0, -5}, /* 0xAF macron */ + {472, 8, 3, 4, 0, -5}, /* 0xB0 degree */ + {475, 8, 5, 4, 0, -5}, /* 0xB1 plusminus */ + {480, 8, 3, 4, 0, -5}, /* 0xB2 twosuperior */ + {483, 8, 3, 4, 0, -5}, /* 0xB3 threesuperior */ + {486, 8, 2, 3, 0, -5}, /* 0xB4 acute */ + {488, 8, 5, 4, 0, -5}, /* 0xB5 mu */ + {493, 8, 5, 4, 0, -5}, /* 0xB6 paragraph */ + {498, 8, 3, 4, 0, -4}, /* 0xB7 periodcentered */ + {501, 8, 3, 4, 0, -3}, /* 0xB8 cedilla */ + {504, 8, 3, 2, 0, -5}, /* 0xB9 onesuperior */ + {507, 8, 5, 4, 0, -5}, /* 0xBA ordmasculine */ + {512, 8, 3, 3, 0, -5}, /* 0xBB guillemotright */ + {515, 8, 5, 4, 0, -5}, /* 0xBC onequarter */ + {520, 8, 5, 4, 0, -5}, /* 0xBD onehalf */ + {525, 8, 5, 4, 0, -5}, /* 0xBE threequarters */ + {530, 8, 5, 4, 0, -5}, /* 0xBF questiondown */ + {535, 8, 5, 4, 0, -5}, /* 0xC0 Agrave */ + {540, 8, 5, 4, 0, -5}, /* 0xC1 Aacute */ + {545, 8, 5, 4, 0, -5}, /* 0xC2 Acircumflex */ + {550, 8, 5, 4, 0, -5}, /* 0xC3 Atilde */ + {555, 8, 5, 4, 0, -5}, /* 0xC4 Adieresis */ + {560, 8, 5, 4, 0, -5}, /* 0xC5 Aring */ + {565, 8, 5, 4, 0, -5}, /* 0xC6 AE */ + {570, 8, 6, 4, 0, -5}, /* 0xC7 Ccedilla */ + {576, 8, 5, 4, 0, -5}, /* 0xC8 Egrave */ + {581, 8, 5, 4, 0, -5}, /* 0xC9 Eacute */ + {586, 8, 5, 4, 0, -5}, /* 0xCA Ecircumflex */ + {591, 8, 5, 4, 0, -5}, /* 0xCB Edieresis */ + {596, 8, 5, 4, 0, -5}, /* 0xCC Igrave */ + {601, 8, 5, 4, 0, -5}, /* 0xCD Iacute */ + {606, 8, 5, 4, 0, -5}, /* 0xCE Icircumflex */ + {611, 8, 5, 4, 0, -5}, /* 0xCF Idieresis */ + {616, 8, 5, 4, 0, -5}, /* 0xD0 Eth */ + {621, 8, 5, 4, 0, -5}, /* 0xD1 Ntilde */ + {626, 8, 5, 4, 0, -5}, /* 0xD2 Ograve */ + {631, 8, 5, 4, 0, -5}, /* 0xD3 Oacute */ + {636, 8, 5, 4, 0, -5}, /* 0xD4 Ocircumflex */ + {641, 8, 5, 4, 0, -5}, /* 0xD5 Otilde */ + {646, 8, 5, 4, 0, -5}, /* 0xD6 Odieresis */ + {651, 8, 3, 4, 0, -4}, /* 0xD7 multiply */ + {654, 8, 5, 4, 0, -5}, /* 0xD8 Oslash */ + {659, 8, 5, 4, 0, -5}, /* 0xD9 Ugrave */ + {664, 8, 5, 4, 0, -5}, /* 0xDA Uacute */ + {669, 8, 5, 4, 0, -5}, /* 0xDB Ucircumflex */ + {674, 8, 5, 4, 0, -5}, /* 0xDC Udieresis */ + {679, 8, 5, 4, 0, -5}, /* 0xDD Yacute */ + {684, 8, 5, 4, 0, -5}, /* 0xDE Thorn */ + {689, 8, 6, 4, 0, -5}, /* 0xDF germandbls */ + {695, 8, 5, 4, 0, -5}, /* 0xE0 agrave */ + {700, 8, 5, 4, 0, -5}, /* 0xE1 aacute */ + {705, 8, 5, 4, 0, -5}, /* 0xE2 acircumflex */ + {710, 8, 5, 4, 0, -5}, /* 0xE3 atilde */ + {715, 8, 5, 4, 0, -5}, /* 0xE4 adieresis */ + {720, 8, 5, 4, 0, -5}, /* 0xE5 aring */ + {725, 8, 4, 4, 0, -4}, /* 0xE6 ae */ + {729, 8, 5, 4, 0, -4}, /* 0xE7 ccedilla */ + {734, 8, 5, 4, 0, -5}, /* 0xE8 egrave */ + {739, 8, 5, 4, 0, -5}, /* 0xE9 eacute */ + {744, 8, 5, 4, 0, -5}, /* 0xEA ecircumflex */ + {749, 8, 5, 4, 0, -5}, /* 0xEB edieresis */ + {754, 8, 5, 3, 0, -5}, /* 0xEC igrave */ + {759, 8, 5, 3, 0, -5}, /* 0xED iacute */ + {764, 8, 5, 4, 0, -5}, /* 0xEE icircumflex */ + {769, 8, 5, 4, 0, -5}, /* 0xEF idieresis */ + {774, 8, 5, 4, 0, -5}, /* 0xF0 eth */ + {779, 8, 5, 4, 0, -5}, /* 0xF1 ntilde */ + {784, 8, 5, 4, 0, -5}, /* 0xF2 ograve */ + {789, 8, 5, 4, 0, -5}, /* 0xF3 oacute */ + {794, 8, 5, 4, 0, -5}, /* 0xF4 ocircumflex */ + {799, 8, 5, 4, 0, -5}, /* 0xF5 otilde */ + {804, 8, 5, 4, 0, -5}, /* 0xF6 odieresis */ + {809, 8, 5, 4, 0, -5}, /* 0xF7 divide */ + {814, 8, 4, 4, 0, -4}, /* 0xF8 oslash */ + {818, 8, 5, 4, 0, -5}, /* 0xF9 ugrave */ + {823, 8, 5, 4, 0, -5}, /* 0xFA uacute */ + {828, 8, 5, 4, 0, -5}, /* 0xFB ucircumflex */ + {833, 8, 5, 4, 0, -5}, /* 0xFC udieresis */ + {838, 8, 6, 4, 0, -5}, /* 0xFD yacute */ + {844, 8, 5, 4, 0, -4}, /* 0xFE thorn */ + {849, 8, 6, 4, 0, -5}, /* 0xFF ydieresis */ + {855, 8, 1, 2, 0, -1}, /* 0x11D gcircumflex */ + {856, 8, 5, 4, 0, -5}, /* 0x152 OE */ + {861, 8, 4, 4, 0, -4}, /* 0x153 oe */ + {865, 8, 5, 4, 0, -5}, /* 0x160 Scaron */ + {870, 8, 5, 4, 0, -5}, /* 0x161 scaron */ + {875, 8, 5, 4, 0, -5}, /* 0x178 Ydieresis */ + {880, 8, 5, 4, 0, -5}, /* 0x17D Zcaron */ + {885, 8, 5, 4, 0, -5}, /* 0x17E zcaron */ + {890, 8, 1, 2, 0, -1}, /* 0xEA4 uni0EA4 */ + {891, 8, 1, 2, 0, -1}, /* 0x13A0 uni13A0 */ + {892, 8, 1, 2, 0, -3}, /* 0x2022 bullet */ + {893, 8, 1, 4, 0, -1}, /* 0x2026 ellipsis */ + {894, 8, 5, 4, 0, -5}, /* 0x20AC Euro */ + {899, 8, 5, 4, 0, -5}, /* 0xFFFD uniFFFD */ +#endif /* (TOMTHUMB_USE_EXTENDED) */ +}; + +const GFXfont TomThumb PROGMEM = {(uint8_t *)TomThumbBitmaps, + (GFXglyph *)TomThumbGlyphs, 0x20, 0x7E, 6}; diff --git a/libraries/Adafruit_GFX_Library/README.md b/libraries/Adafruit_GFX_Library/README.md new file mode 100644 index 0000000..5707c05 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/README.md @@ -0,0 +1,33 @@ +# Adafruit GFX Library ![Build Status](https://github.com/adafruit/Adafruit-GFX-Library/workflows/Arduino%20Library%20CI/badge.svg) + +This is the core graphics library for all our displays, providing a common set of graphics primitives (points, lines, circles, etc.). It needs to be paired with a hardware-specific library for each display device we carry (to handle the lower-level functions). + +Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information. +All text above must be included in any redistribution. + +Recent Arduino IDE releases include the Library Manager for easy installation. Otherwise, to download, click the DOWNLOAD ZIP button, uncompress and rename the uncompressed folder Adafruit_GFX. Confirm that the Adafruit_GFX folder contains Adafruit_GFX.cpp and Adafruit_GFX.h. Place the Adafruit_GFX library folder your ArduinoSketchFolder/Libraries/ folder. You may need to create the Libraries subfolder if its your first library. Restart the IDE. + +**You will also need to install the latest Adafruit BusIO library.** Search for "Adafruit BusIO" in the library manager, or install by hand from https://github.com/adafruit/Adafruit_BusIO + +# Useful Resources + +- Image2Code: This is a handy Java GUI utility to convert a BMP file into the array code necessary to display the image with the drawBitmap function. Check out the code at ehubin's GitHub repository: https://github.com/ehubin/Adafruit-GFX-Library/tree/master/Img2Code + +- drawXBitmap function: You can use the GIMP photo editor to save a .xbm file and use the array saved in the file to draw a bitmap with the drawXBitmap function. See the pull request here for more details: https://github.com/adafruit/Adafruit-GFX-Library/pull/31 + +- 'Fonts' folder contains bitmap fonts for use with recent (1.1 and later) Adafruit_GFX. To use a font in your Arduino sketch, \#include the corresponding .h file and pass address of GFXfont struct to setFont(). Pass NULL to revert to 'classic' fixed-space bitmap font. + +- 'fontconvert' folder contains a command-line tool for converting TTF fonts to Adafruit_GFX header format. + +--- + +### Roadmap + +The PRIME DIRECTIVE is to maintain backward compatibility with existing Arduino sketches -- many are hosted elsewhere and don't track changes here, some are in print and can never be changed! This "little" library has grown organically over time and sometimes we paint ourselves into a design corner and just have to live with it or add ungainly workarounds. + +Highly unlikely to merge any changes for additional or incompatible font formats (see Prime Directive above). There are already two formats and the code is quite bloaty there as it is (this also creates liabilities for tools and documentation). If you *must* have a more sophisticated font format, consider creating a fork with the features required for your project. For similar reasons, also unlikely to add any more bitmap formats, it's getting messy. + +Please don't reformat code for the sake of reformatting code. The resulting large "visual diff" makes it impossible to untangle actual bug fixes from merely rearranged lines. diff --git a/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvas.ino b/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvas.ino new file mode 100644 index 0000000..d4e3370 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvas.ino @@ -0,0 +1,113 @@ +/*** +This example is intended to demonstrate the use of getPixel() versus +getRawPixel() and the fast horizontal and vertical drawing routines +in the GFXcanvas family of classes, + +When using the GFXcanvas* classes as the image buffer for a hardware driver, +there is a need to get individual pixel color values at given physical +coordinates. Rather than subclasses or client classes call getBuffer() and +reinterpret the byte layout of the buffer, two methods are added to each of the +GFXcanvas* classes that allow fetching of specific pixel values. + + * getPixel(x, y) : Gets the pixel color value at the rotated coordinates in +the image. + * getRawPixel(x,y) : Gets the pixel color value at the unrotated coordinates +in the image. This is useful for getting the pixel value to map to a hardware +pixel location. This method was made protected as only the hardware driver +should be accessing it. + +The GFXcanvas*SerialDemo classes in this example will print to Serial the +contents of the underlying GFXcanvas buffer in both the current rotated layout +and the underlying physical layout. +***/ + +#include "GFXcanvasSerialDemo.h" +#include + +void setup() { + Serial.begin(115200); + + // first create a rectangular GFXcanvas8SerialDemo object and draw to it + GFXcanvas8SerialDemo demo8(21, 11); + + demo8.fillScreen(0x00); + demo8.setRotation(1); // now canvas is 11x21 + demo8.fillCircle(5, 10, 5, 0xAA); + demo8.writeFastHLine(0, 0, 11, 0x11); + demo8.writeFastHLine(10, 10, -11, 0x22); + demo8.writeFastHLine(0, 20, 11, 0x33); + demo8.writeFastVLine(0, 0, 21, 0x44); + demo8.writeFastVLine(10, 20, -21, 0x55); + + Serial.println("Demonstrating GFXcanvas rotated and raw pixels.\n"); + + // print it out rotated + + Serial.println("The canvas's content in the rotation of '0':\n"); + demo8.setRotation(0); + demo8.print(true); + Serial.println("\n"); + + Serial.println("The canvas's content in the rotation of '1' (which is what " + "it was drawn in):\n"); + demo8.setRotation(1); + demo8.print(true); + Serial.println("\n"); + + Serial.println("The canvas's content in the rotation of '2':\n"); + demo8.setRotation(2); + demo8.print(true); + Serial.println("\n"); + + Serial.println("The canvas's content in the rotation of '3':\n"); + demo8.setRotation(3); + demo8.print(true); + Serial.println("\n"); + + // print it out unrotated + Serial.println("The canvas's content in it's raw, physical layout:\n"); + demo8.print(false); + Serial.println("\n"); + + // Demonstrate GFXcanvas1SerialDemo + + GFXcanvas1SerialDemo demo1(21, 11); + demo1.fillScreen(0); + demo1.setRotation(0); + demo1.writeFastHLine(0, 0, 9, 1); + demo1.setRotation(1); + demo1.writeFastHLine(0, 0, 9, 1); + demo1.setRotation(2); + demo1.writeFastHLine(0, 0, 9, 1); + demo1.setRotation(3); + demo1.writeFastHLine(0, 0, 9, 1); + demo1.setRotation(1); + demo1.fillRect(3, 8, 5, 5, 1); + + Serial.println("\nThe GFXcanvas1 raw content after drawing a fast horizontal " + "line in each rotation:\n"); + demo1.print(false); + Serial.println("\n"); + + // Demonstrate GFXcanvas16SerialDemo + + GFXcanvas16SerialDemo demo16(21, 11); + demo16.fillScreen(0); + demo16.setRotation(0); + demo16.writeFastHLine(0, 0, 9, 0x1111); + demo16.setRotation(1); + demo16.writeFastHLine(0, 0, 9, 0x2222); + demo16.setRotation(2); + demo16.writeFastHLine(0, 0, 9, 0x3333); + demo16.setRotation(3); + demo16.writeFastHLine(0, 0, 9, 0x4444); + demo16.setRotation(1); + demo16.fillRect(3, 8, 5, 5, 0x8888); + + Serial.println("\nThe GFXcanvas16 raw content after drawing a fast " + "horizontal line in each rotation:\n"); + demo16.print(false); + Serial.println("\n"); +} + +void loop() {} diff --git a/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvasSerialDemo.cpp b/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvasSerialDemo.cpp new file mode 100644 index 0000000..ed83a96 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvasSerialDemo.cpp @@ -0,0 +1,92 @@ +#include "GFXcanvasSerialDemo.h" +#include + +GFXcanvas1SerialDemo::GFXcanvas1SerialDemo(uint16_t w, uint16_t h) + : GFXcanvas1(w, h) {} + +void GFXcanvas1SerialDemo::print(bool rotated) { + char pixel_buffer[8]; + uint16_t width, height; + + if (rotated) { + width = this->width(); + height = this->height(); + } else { + width = this->WIDTH; + height = this->HEIGHT; + } + + for (uint16_t y = 0; y < height; y++) { + for (uint16_t x = 0; x < width; x++) { + bool pixel; + if (rotated) { + pixel = this->getPixel(x, y); + } else { + pixel = this->getRawPixel(x, y); + } + sprintf(pixel_buffer, " %d", pixel); + Serial.print(pixel_buffer); + } + Serial.print("\n"); + } +} + +GFXcanvas8SerialDemo::GFXcanvas8SerialDemo(uint16_t w, uint16_t h) + : GFXcanvas8(w, h) {} + +void GFXcanvas8SerialDemo::print(bool rotated) { + char pixel_buffer[8]; + uint16_t width, height; + + if (rotated) { + width = this->width(); + height = this->height(); + } else { + width = this->WIDTH; + height = this->HEIGHT; + } + + for (uint16_t y = 0; y < height; y++) { + for (uint16_t x = 0; x < width; x++) { + uint8_t pixel; + if (rotated) { + pixel = this->getPixel(x, y); + } else { + pixel = this->getRawPixel(x, y); + } + sprintf(pixel_buffer, " %02x", pixel); + Serial.print(pixel_buffer); + } + Serial.print("\n"); + } +} + +GFXcanvas16SerialDemo::GFXcanvas16SerialDemo(uint16_t w, uint16_t h) + : GFXcanvas16(w, h) {} + +void GFXcanvas16SerialDemo::print(bool rotated) { + char pixel_buffer[8]; + uint16_t width, height; + + if (rotated) { + width = this->width(); + height = this->height(); + } else { + width = this->WIDTH; + height = this->HEIGHT; + } + + for (uint16_t y = 0; y < height; y++) { + for (uint16_t x = 0; x < width; x++) { + uint16_t pixel; + if (rotated) { + pixel = this->getPixel(x, y); + } else { + pixel = this->getRawPixel(x, y); + } + sprintf(pixel_buffer, " %04x", pixel); + Serial.print(pixel_buffer); + } + Serial.print("\n"); + } +} diff --git a/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvasSerialDemo.h b/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvasSerialDemo.h new file mode 100644 index 0000000..80589d9 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/examples/GFXcanvas/GFXcanvasSerialDemo.h @@ -0,0 +1,65 @@ +#ifndef __GFXcanvasSerialDemo__ +#define __GFXcanvasSerialDemo__ +#include + +/**********************************************************************/ +/*! + @brief Demonstrates using the GFXconvas classes as the backing store + for a device driver. +*/ +/**********************************************************************/ +class GFXcanvas1SerialDemo : public GFXcanvas1 { +public: + GFXcanvas1SerialDemo(uint16_t w, uint16_t h); + + /**********************************************************************/ + /*! + @brief Prints the current contents of the canvas to Serial + @param rotated true to print according to the current GFX rotation, + false to print to the native rotation of the canvas (or unrotated). + */ + /**********************************************************************/ + void print(bool rotated); +}; + +/**********************************************************************/ +/*! + @brief Demonstrates using the GFXconvas classes as the backing store + for a device driver. +*/ +/**********************************************************************/ +class GFXcanvas8SerialDemo : public GFXcanvas8 { +public: + GFXcanvas8SerialDemo(uint16_t w, uint16_t h); + + /**********************************************************************/ + /*! + @brief Prints the current contents of the canvas to Serial + @param rotated true to print according to the current GFX rotation, + false to print to the native rotation of the canvas (or unrotated). + */ + /**********************************************************************/ + void print(bool rotated); +}; + +/**********************************************************************/ +/*! + @brief Demonstrates using the GFXconvas classes as the backing store + for a device driver. +*/ +/**********************************************************************/ +class GFXcanvas16SerialDemo : public GFXcanvas16 { +public: + GFXcanvas16SerialDemo(uint16_t w, uint16_t h); + + /**********************************************************************/ + /*! + @brief Prints the current contents of the canvas to Serial + @param rotated true to print according to the current GFX rotation, + false to print to the native rotation of the canvas (or unrotated). + */ + /**********************************************************************/ + void print(bool rotated); +}; + +#endif // __GFXcanvasSerialDemo__ diff --git a/libraries/Adafruit_GFX_Library/examples/mock_ili9341/mock_ili9341.ino b/libraries/Adafruit_GFX_Library/examples/mock_ili9341/mock_ili9341.ino new file mode 100644 index 0000000..d141839 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/examples/mock_ili9341/mock_ili9341.ino @@ -0,0 +1,365 @@ +/*************************************************** + This is our GFX example for the Adafruit ILI9341 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ + + +#include "SPI.h" +#include "Adafruit_GFX.h" +#include "Adafruit_ILI9341.h" + +// For the Adafruit shield, these are the default. +#define TFT_DC 9 +#define TFT_CS 10 + +// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); +// If using the breakout, change pins as desired +//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO); + +void setup() { + Serial.begin(9600); + Serial.println("ILI9341 Test!"); + + tft.begin(); + + // read diagnostics (optional but can help debug problems) + uint8_t x = tft.readcommand8(ILI9341_RDMODE); + Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDMADCTL); + Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDPIXFMT); + Serial.print("Pixel Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDIMGFMT); + Serial.print("Image Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDSELFDIAG); + Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); + + Serial.println(F("Benchmark Time (microseconds)")); + delay(10); + Serial.print(F("Screen fill ")); + Serial.println(testFillScreen()); + delay(500); + + Serial.print(F("Text ")); + Serial.println(testText()); + delay(3000); + + Serial.print(F("Lines ")); + Serial.println(testLines(ILI9341_CYAN)); + delay(500); + + Serial.print(F("Horiz/Vert Lines ")); + Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE)); + delay(500); + + Serial.print(F("Rectangles (outline) ")); + Serial.println(testRects(ILI9341_GREEN)); + delay(500); + + Serial.print(F("Rectangles (filled) ")); + Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA)); + delay(500); + + Serial.print(F("Circles (filled) ")); + Serial.println(testFilledCircles(10, ILI9341_MAGENTA)); + + Serial.print(F("Circles (outline) ")); + Serial.println(testCircles(10, ILI9341_WHITE)); + delay(500); + + Serial.print(F("Triangles (outline) ")); + Serial.println(testTriangles()); + delay(500); + + Serial.print(F("Triangles (filled) ")); + Serial.println(testFilledTriangles()); + delay(500); + + Serial.print(F("Rounded rects (outline) ")); + Serial.println(testRoundRects()); + delay(500); + + Serial.print(F("Rounded rects (filled) ")); + Serial.println(testFilledRoundRects()); + delay(500); + + Serial.println(F("Done!")); + +} + + +void loop(void) { + for(uint8_t rotation=0; rotation<4; rotation++) { + tft.setRotation(rotation); + testText(); + delay(1000); + } +} + +unsigned long testFillScreen() { + unsigned long start = micros(); + tft.fillScreen(ILI9341_BLACK); + yield(); + tft.fillScreen(ILI9341_RED); + yield(); + tft.fillScreen(ILI9341_GREEN); + yield(); + tft.fillScreen(ILI9341_BLUE); + yield(); + tft.fillScreen(ILI9341_BLACK); + yield(); + return micros() - start; +} + +unsigned long testText() { + tft.fillScreen(ILI9341_BLACK); + unsigned long start = micros(); + tft.setCursor(0, 0); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1); + tft.println("Hello World!"); + tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2); + tft.println(1234.56); + tft.setTextColor(ILI9341_RED); tft.setTextSize(3); + tft.println(0xDEADBEEF, HEX); + tft.println(); + tft.setTextColor(ILI9341_GREEN); + tft.setTextSize(5); + tft.println("Groop"); + tft.setTextSize(2); + tft.println("I implore thee,"); + tft.setTextSize(1); + tft.println("my foonting turlingdromes."); + tft.println("And hooptiously drangle me"); + tft.println("with crinkly bindlewurdles,"); + tft.println("Or I will rend thee"); + tft.println("in the gobberwarts"); + tft.println("with my blurglecruncheon,"); + tft.println("see if I don't!"); + return micros() - start; +} + +unsigned long testLines(uint16_t color) { + unsigned long start, t; + int x1, y1, x2, y2, + w = tft.width(), + h = tft.height(); + + tft.fillScreen(ILI9341_BLACK); + yield(); + + x1 = y1 = 0; + y2 = h - 1; + start = micros(); + for(x2=0; x20; i-=6) { + i2 = i / 2; + start = micros(); + tft.fillRect(cx-i2, cy-i2, i, i, color1); + t += micros() - start; + // Outlines are not included in timing results + tft.drawRect(cx-i2, cy-i2, i, i, color2); + yield(); + } + + return t; +} + +unsigned long testFilledCircles(uint8_t radius, uint16_t color) { + unsigned long start; + int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2; + + tft.fillScreen(ILI9341_BLACK); + start = micros(); + for(x=radius; x10; i-=5) { + start = micros(); + tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(0, i*10, i*10)); + t += micros() - start; + tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(i*10, i*10, 0)); + yield(); + } + + return t; +} + +unsigned long testRoundRects() { + unsigned long start; + int w, i, i2, + cx = tft.width() / 2 - 1, + cy = tft.height() / 2 - 1; + + tft.fillScreen(ILI9341_BLACK); + w = min(tft.width(), tft.height()); + start = micros(); + for(i=0; i20; i-=6) { + i2 = i / 2; + tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0)); + yield(); + } + + return micros() - start; +} diff --git a/libraries/Adafruit_GFX_Library/fontconvert/Makefile b/libraries/Adafruit_GFX_Library/fontconvert/Makefile new file mode 100644 index 0000000..47f5a0e --- /dev/null +++ b/libraries/Adafruit_GFX_Library/fontconvert/Makefile @@ -0,0 +1,12 @@ +all: fontconvert + +CC = gcc +CFLAGS = -Wall -I/usr/local/include/freetype2 -I/usr/include/freetype2 -I/usr/include +LIBS = -lfreetype + +fontconvert: fontconvert.c + $(CC) $(CFLAGS) $< $(LIBS) -o $@ + strip $@ + +clean: + rm -f fontconvert diff --git a/libraries/Adafruit_GFX_Library/fontconvert/fontconvert.c b/libraries/Adafruit_GFX_Library/fontconvert/fontconvert.c new file mode 100644 index 0000000..302d1da --- /dev/null +++ b/libraries/Adafruit_GFX_Library/fontconvert/fontconvert.c @@ -0,0 +1,291 @@ +/* +TrueType to Adafruit_GFX font converter. Derived from Peter Jakobs' +Adafruit_ftGFX fork & makefont tool, and Paul Kourany's Adafruit_mfGFX. + +NOT AN ARDUINO SKETCH. This is a command-line tool for preprocessing +fonts to be used with the Adafruit_GFX Arduino library. + +For UNIX-like systems. Outputs to stdout; redirect to header file, e.g.: + ./fontconvert ~/Library/Fonts/FreeSans.ttf 18 > FreeSans18pt7b.h + +REQUIRES FREETYPE LIBRARY. www.freetype.org + +Currently this only extracts the printable 7-bit ASCII chars of a font. +Will eventually extend with some int'l chars a la ftGFX, not there yet. +Keep 7-bit fonts around as an option in that case, more compact. + +See notes at end for glyph nomenclature & other tidbits. +*/ +#ifndef ARDUINO + +#include +#include +#include +#include +#include FT_GLYPH_H +#include FT_MODULE_H +#include FT_TRUETYPE_DRIVER_H +#include "../gfxfont.h" // Adafruit_GFX font structures + +#define DPI 141 // Approximate res. of Adafruit 2.8" TFT + +// Accumulate bits for output, with periodic hexadecimal byte write +void enbit(uint8_t value) { + static uint8_t row = 0, sum = 0, bit = 0x80, firstCall = 1; + if (value) + sum |= bit; // Set bit if needed + if (!(bit >>= 1)) { // Advance to next bit, end of byte reached? + if (!firstCall) { // Format output table nicely + if (++row >= 12) { // Last entry on line? + printf(",\n "); // Newline format output + row = 0; // Reset row counter + } else { // Not end of line + printf(", "); // Simple comma delim + } + } + printf("0x%02X", sum); // Write byte value + sum = 0; // Clear for next byte + bit = 0x80; // Reset bit counter + firstCall = 0; // Formatting flag + } +} + +int main(int argc, char *argv[]) { + int i, j, err, size, first = ' ', last = '~', bitmapOffset = 0, x, y, byte; + char *fontName, c, *ptr; + FT_Library library; + FT_Face face; + FT_Glyph glyph; + FT_Bitmap *bitmap; + FT_BitmapGlyphRec *g; + GFXglyph *table; + uint8_t bit; + + // Parse command line. Valid syntaxes are: + // fontconvert [filename] [size] + // fontconvert [filename] [size] [last char] + // fontconvert [filename] [size] [first char] [last char] + // Unless overridden, default first and last chars are + // ' ' (space) and '~', respectively + + if (argc < 3) { + fprintf(stderr, "Usage: %s fontfile size [first] [last]\n", argv[0]); + return 1; + } + + size = atoi(argv[2]); + + if (argc == 4) { + last = atoi(argv[3]); + } else if (argc == 5) { + first = atoi(argv[3]); + last = atoi(argv[4]); + } + + if (last < first) { + i = first; + first = last; + last = i; + } + + ptr = strrchr(argv[1], '/'); // Find last slash in filename + if (ptr) + ptr++; // First character of filename (path stripped) + else + ptr = argv[1]; // No path; font in local dir. + + // Allocate space for font name and glyph table + if ((!(fontName = malloc(strlen(ptr) + 20))) || + (!(table = (GFXglyph *)malloc((last - first + 1) * sizeof(GFXglyph))))) { + fprintf(stderr, "Malloc error\n"); + return 1; + } + + // Derive font table names from filename. Period (filename + // extension) is truncated and replaced with the font size & bits. + strcpy(fontName, ptr); + ptr = strrchr(fontName, '.'); // Find last period (file ext) + if (!ptr) + ptr = &fontName[strlen(fontName)]; // If none, append + // Insert font size and 7/8 bit. fontName was alloc'd w/extra + // space to allow this, we're not sprintfing into Forbidden Zone. + sprintf(ptr, "%dpt%db", size, (last > 127) ? 8 : 7); + // Space and punctuation chars in name replaced w/ underscores. + for (i = 0; (c = fontName[i]); i++) { + if (isspace(c) || ispunct(c)) + fontName[i] = '_'; + } + + // Init FreeType lib, load font + if ((err = FT_Init_FreeType(&library))) { + fprintf(stderr, "FreeType init error: %d", err); + return err; + } + + // Use TrueType engine version 35, without subpixel rendering. + // This improves clarity of fonts since this library does not + // support rendering multiple levels of gray in a glyph. + // See https://github.com/adafruit/Adafruit-GFX-Library/issues/103 + FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35; + FT_Property_Set(library, "truetype", "interpreter-version", + &interpreter_version); + + if ((err = FT_New_Face(library, argv[1], 0, &face))) { + fprintf(stderr, "Font load error: %d", err); + FT_Done_FreeType(library); + return err; + } + + // << 6 because '26dot6' fixed-point format + FT_Set_Char_Size(face, size << 6, 0, DPI, 0); + + // Currently all symbols from 'first' to 'last' are processed. + // Fonts may contain WAY more glyphs than that, but this code + // will need to handle encoding stuff to deal with extracting + // the right symbols, and that's not done yet. + // fprintf(stderr, "%ld glyphs\n", face->num_glyphs); + + printf("const uint8_t %sBitmaps[] PROGMEM = {\n ", fontName); + + // Process glyphs and output huge bitmap data array + for (i = first, j = 0; i <= last; i++, j++) { + // MONO renderer provides clean image with perfect crop + // (no wasted pixels) via bitmap struct. + if ((err = FT_Load_Char(face, i, FT_LOAD_TARGET_MONO))) { + fprintf(stderr, "Error %d loading char '%c'\n", err, i); + continue; + } + + if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) { + fprintf(stderr, "Error %d rendering char '%c'\n", err, i); + continue; + } + + if ((err = FT_Get_Glyph(face->glyph, &glyph))) { + fprintf(stderr, "Error %d getting glyph '%c'\n", err, i); + continue; + } + + bitmap = &face->glyph->bitmap; + g = (FT_BitmapGlyphRec *)glyph; + + // Minimal font and per-glyph information is stored to + // reduce flash space requirements. Glyph bitmaps are + // fully bit-packed; no per-scanline pad, though end of + // each character may be padded to next byte boundary + // when needed. 16-bit offset means 64K max for bitmaps, + // code currently doesn't check for overflow. (Doesn't + // check that size & offsets are within bounds either for + // that matter...please convert fonts responsibly.) + table[j].bitmapOffset = bitmapOffset; + table[j].width = bitmap->width; + table[j].height = bitmap->rows; + table[j].xAdvance = face->glyph->advance.x >> 6; + table[j].xOffset = g->left; + table[j].yOffset = 1 - g->top; + + for (y = 0; y < bitmap->rows; y++) { + for (x = 0; x < bitmap->width; x++) { + byte = x / 8; + bit = 0x80 >> (x & 7); + enbit(bitmap->buffer[y * bitmap->pitch + byte] & bit); + } + } + + // Pad end of char bitmap to next byte boundary if needed + int n = (bitmap->width * bitmap->rows) & 7; + if (n) { // Pixel count not an even multiple of 8? + n = 8 - n; // # bits to next multiple + while (n--) + enbit(0); + } + bitmapOffset += (bitmap->width * bitmap->rows + 7) / 8; + + FT_Done_Glyph(glyph); + } + + printf(" };\n\n"); // End bitmap array + + // Output glyph attributes table (one per character) + printf("const GFXglyph %sGlyphs[] PROGMEM = {\n", fontName); + for (i = first, j = 0; i <= last; i++, j++) { + printf(" { %5d, %3d, %3d, %3d, %4d, %4d }", table[j].bitmapOffset, + table[j].width, table[j].height, table[j].xAdvance, table[j].xOffset, + table[j].yOffset); + if (i < last) { + printf(", // 0x%02X", i); + if ((i >= ' ') && (i <= '~')) { + printf(" '%c'", i); + } + putchar('\n'); + } + } + printf(" }; // 0x%02X", last); + if ((last >= ' ') && (last <= '~')) + printf(" '%c'", last); + printf("\n\n"); + + // Output font structure + printf("const GFXfont %s PROGMEM = {\n", fontName); + printf(" (uint8_t *)%sBitmaps,\n", fontName); + printf(" (GFXglyph *)%sGlyphs,\n", fontName); + if (face->size->metrics.height == 0) { + // No face height info, assume fixed width and get from a glyph. + printf(" 0x%02X, 0x%02X, %d };\n\n", first, last, table[0].height); + } else { + printf(" 0x%02X, 0x%02X, %ld };\n\n", first, last, + face->size->metrics.height >> 6); + } + printf("// Approx. %d bytes\n", bitmapOffset + (last - first + 1) * 7 + 7); + // Size estimate is based on AVR struct and pointer sizes; + // actual size may vary. + + FT_Done_FreeType(library); + + return 0; +} + +/* ------------------------------------------------------------------------- + +Character metrics are slightly different from classic GFX & ftGFX. +In classic GFX: cursor position is the upper-left pixel of each 5x7 +character; lower extent of most glyphs (except those w/descenders) +is +6 pixels in Y direction. +W/new GFX fonts: cursor position is on baseline, where baseline is +'inclusive' (containing the bottom-most row of pixels in most symbols, +except those with descenders; ftGFX is one pixel lower). + +Cursor Y will be moved automatically when switching between classic +and new fonts. If you switch fonts, any print() calls will continue +along the same baseline. + + ...........#####.. -- yOffset + ..........######.. + ..........######.. + .........#######.. + ........#########. + * = Cursor pos. ........#########. + .......##########. + ......#####..####. + ......#####..####. + *.#.. .....#####...####. + .#.#. ....############## + #...# ...############### + #...# ...############### + ##### ..#####......##### + #...# .#####.......##### +====== #...# ====== #*###.........#### ======= Baseline + || xOffset + +glyph->xOffset and yOffset are pixel offsets, in GFX coordinate space +(+Y is down), from the cursor position to the top-left pixel of the +glyph bitmap. i.e. yOffset is typically negative, xOffset is typically +zero but a few glyphs will have other values (even negative xOffsets +sometimes, totally normal). glyph->xAdvance is the distance to move +the cursor on the X axis after drawing the corresponding symbol. + +There's also some changes with regard to 'background' color and new GFX +fonts (classic fonts unchanged). See Adafruit_GFX.cpp for explanation. +*/ + +#endif /* !ARDUINO */ diff --git a/libraries/Adafruit_GFX_Library/fontconvert/fontconvert_win.md b/libraries/Adafruit_GFX_Library/fontconvert/fontconvert_win.md new file mode 100644 index 0000000..361078b --- /dev/null +++ b/libraries/Adafruit_GFX_Library/fontconvert/fontconvert_win.md @@ -0,0 +1,88 @@ +### A short guide to use fontconvert.c to create your own fonts using MinGW. + +#### STEP 1: INSTALL MinGW + +Install MinGW (Minimalist GNU for Windows) from [MinGW.org](http://www.mingw.org/). +Please read carefully the instructions found on [Getting started page](http://www.mingw.org/wiki/Getting_Started). +I suggest installing with the "Graphical User Interface Installer". +To complete your initial installation you should further install some "packages". +For our purpose you should only install the "Basic Setup" packages. +To do that: + +1. Open the MinGW Installation Manager +2. From the left panel click "Basic Setup". +3. On the right panel choose "mingw32-base", "mingw-gcc-g++", "mingw-gcc-objc" and "msys-base" +and click "Mark for installation" +4. From the Menu click "Installation" and then "Apply changes". In the pop-up window select "Apply". + + +#### STEP 2: INSTALL Freetype Library + +To read about the freetype project visit [freetype.org](https://www.freetype.org/). +To Download the latest version of freetype go to [download page](http://download.savannah.gnu.org/releases/freetype/) +and choose "freetype-2.7.tar.gz" file (or a newer version if available). +To avoid long cd commands later in the command prompt, I suggest you unzip the file in the C:\ directory. +(I also renamed the folder to "ft27") +Before you build the library it's good to read these articles: +* [Using MSYS with MinGW](http://www.mingw.org/wiki/MSYS) +* [Installation and Use of Supplementary Libraries with MinGW](http://www.mingw.org/wiki/LibraryPathHOWTO) +* [Include Path](http://www.mingw.org/wiki/IncludePathHOWTO) + +Inside the unzipped folder there is another folder named "docs". Open it and read the INSTALL.UNIX (using notepad). +Pay attention to paragraph 3 (Build and Install the Library). So, let's begin the installation. +To give the appropriate commands we will use the MSYS command prompt (not cmd.exe of windows) which is UNIX like. +Follow the path C:\MinGW\msys\1.0 and double click "msys.bat". The command prompt environment appears. +Enter "ft27" directory using the cd commands: +``` +cd /c +cd ft27 +``` + +and then type one by one the commands: +``` +./configure --prefix=/mingw +make +make install +``` +Once you're finished, go inside "C:\MinGW\include" and there should be a new folder named "freetype2". +That, hopefully, means that you have installed the library correctly !! + +#### STEP 3: Build fontconvert.c + +Before proceeding I suggest you make a copy of Adafruit_GFX_library folder in C:\ directory. +Then, inside "fontconvert" folder open the "makefile" with an editor ( I used notepad++). +Change the commands so in the end the program looks like : +``` +all: fontconvert + +CC = gcc +CFLAGS = -Wall -I c:/mingw/include/freetype2 +LIBS = -lfreetype + +fontconvert: fontconvert.c + $(CC) $(CFLAGS) $< $(LIBS) -o $@ + +clean: + rm -f fontconvert +``` +Go back in the command prompt and with a cd command enter the fontconvert directory. +``` +cd /c/adafruit_gfx_library\fontconvert +``` +Give the command: +``` +make +``` +This command will, eventually, create a "fontconvert.exe" file inside fontconvert directory. + +#### STEP 4: Create your own font header files + +Now that you have an executable file, you can use it to create your own fonts to work with Adafruit GFX lib. +So, if we suppose that you already have a .ttf file with your favorite fonts, jump to the command prompt and type: +``` +./fontconvert yourfonts.ttf 9 > yourfonts9pt7b.h +``` +You can read more details at: [learn.adafruit](https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts). + +Taraaaaaammm !! you've just created your new font header file. Put it inside the "Fonts" folder, grab a cup of coffee +and start playing with your Arduino (or whatever else ....)+ display module project. diff --git a/libraries/Adafruit_GFX_Library/fontconvert/makefonts.sh b/libraries/Adafruit_GFX_Library/fontconvert/makefonts.sh new file mode 100644 index 0000000..35f07ea --- /dev/null +++ b/libraries/Adafruit_GFX_Library/fontconvert/makefonts.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Ugly little Bash script, generates a set of .h files for GFX using +# GNU FreeFont sources. There are three fonts: 'Mono' (Courier-like), +# 'Sans' (Helvetica-like) and 'Serif' (Times-like); four styles: regular, +# bold, oblique or italic, and bold+oblique or bold+italic; and four +# sizes: 9, 12, 18 and 24 point. No real error checking or anything, +# this just powers through all the combinations, calling the fontconvert +# utility and redirecting the output to a .h file for each combo. + +# Adafruit_GFX repository does not include the source outline fonts +# (huge zipfile, different license) but they're easily acquired: +# http://savannah.gnu.org/projects/freefont/ + +convert=./fontconvert +inpath=~/Desktop/freefont/ +outpath=../Fonts/ +fonts=(FreeMono FreeSans FreeSerif) +styles=("" Bold Italic BoldItalic Oblique BoldOblique) +sizes=(9 12 18 24) + +for f in ${fonts[*]} +do + for index in ${!styles[*]} + do + st=${styles[$index]} + for si in ${sizes[*]} + do + infile=$inpath$f$st".ttf" + if [ -f $infile ] # Does source combination exist? + then + outfile=$outpath$f$st$si"pt7b.h" +# printf "%s %s %s > %s\n" $convert $infile $si $outfile + $convert $infile $si > $outfile + fi + done + done +done diff --git a/libraries/Adafruit_GFX_Library/gfxfont.h b/libraries/Adafruit_GFX_Library/gfxfont.h new file mode 100644 index 0000000..175bad6 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/gfxfont.h @@ -0,0 +1,29 @@ +// Font structures for newer Adafruit_GFX (1.1 and later). +// Example fonts are included in 'Fonts' directory. +// To use a font in your Arduino sketch, #include the corresponding .h +// file and pass address of GFXfont struct to setFont(). Pass NULL to +// revert to 'classic' fixed-space bitmap font. + +#ifndef _GFXFONT_H_ +#define _GFXFONT_H_ + +/// Font data stored PER GLYPH +typedef struct { + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner +} GFXglyph; + +/// Data stored for FONT AS A WHOLE +typedef struct { + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint16_t first; ///< ASCII extents (first char) + uint16_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) +} GFXfont; + +#endif // _GFXFONT_H_ diff --git a/libraries/Adafruit_GFX_Library/glcdfont.c b/libraries/Adafruit_GFX_Library/glcdfont.c new file mode 100644 index 0000000..535da3a --- /dev/null +++ b/libraries/Adafruit_GFX_Library/glcdfont.c @@ -0,0 +1,143 @@ +// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0. +// See gfxfont.h for newer custom bitmap font info. + +#ifndef FONT5X7_H +#define FONT5X7_H + +#ifdef __AVR__ +#include +#include +#elif defined(ESP8266) +#include +#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) +// PROGMEM is defefind for T4 to place data in specific memory section +#undef PROGMEM +#define PROGMEM +#else +#define PROGMEM +#endif + +// Standard ASCII 5x7 font + +static const unsigned char font[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B, + 0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C, + 0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, + 0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24, + 0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E, + 0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F, + 0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C, + 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F, + 0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89, + 0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94, + 0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08, + 0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10, + 0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06, + 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, + 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41, + 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08, + 0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, + 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, + 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F, + 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41, + 0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, + 0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, + 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59, + 0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, + 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49, + 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73, + 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, + 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, + 0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, + 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, + 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32, + 0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, + 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, + 0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, + 0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, + 0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28, + 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, + 0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18, + 0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, + 0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08, + 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24, + 0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, + 0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40, + 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C, + 0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64, + 0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C, + 0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40, + 0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41, + 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut + 0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E, + 0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54, + 0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00, + 0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11, + 0x12, 0x7D, // A-umlaut + 0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54, + 0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49, + 0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut + 0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42, + 0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42, + 0x3D, // O-umlaut + 0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E, + 0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6, + 0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00, + 0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40, + 0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D, + 0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48, + 0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00, + 0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A, + 0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old + // code + 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block + 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14, + 0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10, + 0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17, + 0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00, + 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, + 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00, + 0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F, + 0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17, + 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17, + 0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10, + 0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00, + 0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0, + 0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10, + 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44, + 0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta + 0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55, + 0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E, + 0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C, + 0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D, + 0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D, + 0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44, + 0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0, + 0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36, + 0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F, + 0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP +}; + +// allow clean compilation with [-Wunused-const-variable=] and [-Wall] +static inline void avoid_unused_const_variable_compiler_warning(void) { + (void)font; +} + +#endif // FONT5X7_H diff --git a/libraries/Adafruit_GFX_Library/library.properties b/libraries/Adafruit_GFX_Library/library.properties new file mode 100644 index 0000000..32889bf --- /dev/null +++ b/libraries/Adafruit_GFX_Library/library.properties @@ -0,0 +1,10 @@ +name=Adafruit GFX Library +version=1.10.5 +author=Adafruit +maintainer=Adafruit +sentence=Adafruit GFX graphics core library, this is the 'core' class that all our other graphics libraries derive from. +paragraph=Install this library in addition to the display library for your hardware. +category=Display +url=https://github.com/adafruit/Adafruit-GFX-Library +architectures=* +depends=Adafruit BusIO diff --git a/libraries/Adafruit_GFX_Library/license.txt b/libraries/Adafruit_GFX_Library/license.txt new file mode 100644 index 0000000..7492e93 --- /dev/null +++ b/libraries/Adafruit_GFX_Library/license.txt @@ -0,0 +1,24 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/libraries/Adafruit_Sensor-master/.gitignore b/libraries/Adafruit_Sensor-master/.gitignore new file mode 100644 index 0000000..d04184a --- /dev/null +++ b/libraries/Adafruit_Sensor-master/.gitignore @@ -0,0 +1,108 @@ +# +# NOTE! Don't add files that are generated in specific +# subdirectories here. Add them in the ".gitignore" file +# in that subdirectory instead. +# +# NOTE! Please use 'git ls-files -i --exclude-standard' +# command after changing this file, to see if there are +# any tracked files which get ignored after the change. +# +# Normal rules +# +.* +*.o +*.o.* +*.a +*.s +*.ko +*.so +*.so.dbg +*.mod.c +*.i +*.lst +*.symtypes +*.order +modules.builtin +*.elf +*.bin +*.gz +*.bz2 +*.lzma +*.patch +*.gcno + +# +# Top-level generic files +# +/tags +/TAGS +/linux +/vmlinux +/vmlinuz +/System.map +/Module.markers +/Module.symvers + +# +# git files that we don't want to ignore even it they are dot-files +# +!.gitignore +!.mailmap + +# +# Generated include files +# +include/config +include/linux/version.h +include/generated + +# stgit generated dirs +patches-* + +# quilt's files +patches +series + +# cscope files +cscope.* +ncscope.* + +# gnu global files +GPATH +GRTAGS +GSYMS +GTAGS + +# QT-Creator files +Makefile.am.user +*.config +*.creator +*.creator.user +*.files +*.includes + +*.orig +*~ +\#*# +*.lo +*.la +Makefile +Makefile.in +aclocal.m4 +autoconfig.h +autoconfig.h.in +autom4te.cache/ +build-aux/ +config.log +config.status +configure +libtool +libupnp.pc +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +stamp-h1 +docs/doxygen + diff --git a/libraries/Adafruit_Sensor-master/Adafruit_Sensor.cpp b/libraries/Adafruit_Sensor-master/Adafruit_Sensor.cpp new file mode 100644 index 0000000..2a4513e --- /dev/null +++ b/libraries/Adafruit_Sensor-master/Adafruit_Sensor.cpp @@ -0,0 +1,78 @@ +#include "Adafruit_Sensor.h" + +/**************************************************************************/ +/*! + @brief Prints sensor information to serial console +*/ +/**************************************************************************/ +void Adafruit_Sensor::printSensorDetails(void) { + sensor_t sensor; + getSensor(&sensor); + Serial.println(F("------------------------------------")); + Serial.print(F("Sensor: ")); + Serial.println(sensor.name); + Serial.print(F("Type: ")); + switch ((sensors_type_t)sensor.type) { + case SENSOR_TYPE_ACCELEROMETER: + Serial.print(F("Acceleration (m/s2)")); + break; + case SENSOR_TYPE_MAGNETIC_FIELD: + Serial.print(F("Magnetic (uT)")); + break; + case SENSOR_TYPE_ORIENTATION: + Serial.print(F("Orientation (degrees)")); + break; + case SENSOR_TYPE_GYROSCOPE: + Serial.print(F("Gyroscopic (rad/s)")); + break; + case SENSOR_TYPE_LIGHT: + Serial.print(F("Light (lux)")); + break; + case SENSOR_TYPE_PRESSURE: + Serial.print(F("Pressure (hPa)")); + break; + case SENSOR_TYPE_PROXIMITY: + Serial.print(F("Distance (cm)")); + break; + case SENSOR_TYPE_GRAVITY: + Serial.print(F("Gravity (m/s2)")); + break; + case SENSOR_TYPE_LINEAR_ACCELERATION: + Serial.print(F("Linear Acceleration (m/s2)")); + break; + case SENSOR_TYPE_ROTATION_VECTOR: + Serial.print(F("Rotation vector")); + break; + case SENSOR_TYPE_RELATIVE_HUMIDITY: + Serial.print(F("Relative Humidity (%)")); + break; + case SENSOR_TYPE_AMBIENT_TEMPERATURE: + Serial.print(F("Ambient Temp (C)")); + break; + case SENSOR_TYPE_OBJECT_TEMPERATURE: + Serial.print(F("Object Temp (C)")); + break; + case SENSOR_TYPE_VOLTAGE: + Serial.print(F("Voltage (V)")); + break; + case SENSOR_TYPE_CURRENT: + Serial.print(F("Current (mA)")); + break; + case SENSOR_TYPE_COLOR: + Serial.print(F("Color (RGBA)")); + break; + } + + Serial.println(); + Serial.print(F("Driver Ver: ")); + Serial.println(sensor.version); + Serial.print(F("Unique ID: ")); + Serial.println(sensor.sensor_id); + Serial.print(F("Min Value: ")); + Serial.println(sensor.min_value); + Serial.print(F("Max Value: ")); + Serial.println(sensor.max_value); + Serial.print(F("Resolution: ")); + Serial.println(sensor.resolution); + Serial.println(F("------------------------------------\n")); +} diff --git a/libraries/Adafruit_Sensor-master/Adafruit_Sensor.h b/libraries/Adafruit_Sensor-master/Adafruit_Sensor.h new file mode 100644 index 0000000..087eda5 --- /dev/null +++ b/libraries/Adafruit_Sensor-master/Adafruit_Sensor.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software< /span> + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and + * extended sensor support to include color, voltage and current */ + +#ifndef _ADAFRUIT_SENSOR_H +#define _ADAFRUIT_SENSOR_H + +#ifndef ARDUINO +#include +#elif ARDUINO >= 100 +#include "Arduino.h" +#include "Print.h" +#else +#include "WProgram.h" +#endif + +/* Constants */ +#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ +#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ +#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ +#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) +#define SENSORS_MAGFIELD_EARTH_MAX \ + (60.0F) /**< Maximum magnetic field on Earth's surface */ +#define SENSORS_MAGFIELD_EARTH_MIN \ + (30.0F) /**< Minimum magnetic field on Earth's surface */ +#define SENSORS_PRESSURE_SEALEVELHPA \ + (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ +#define SENSORS_DPS_TO_RADS \ + (0.017453293F) /**< Degrees/s to rad/s multiplier \ + */ +#define SENSORS_RADS_TO_DPS \ + (57.29577793F) /**< Rad/s to degrees/s multiplier */ +#define SENSORS_GAUSS_TO_MICROTESLA \ + (100) /**< Gauss to micro-Tesla multiplier */ + +/** Sensor types */ +typedef enum { + SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ + SENSOR_TYPE_MAGNETIC_FIELD = (2), + SENSOR_TYPE_ORIENTATION = (3), + SENSOR_TYPE_GYROSCOPE = (4), + SENSOR_TYPE_LIGHT = (5), + SENSOR_TYPE_PRESSURE = (6), + SENSOR_TYPE_PROXIMITY = (8), + SENSOR_TYPE_GRAVITY = (9), + SENSOR_TYPE_LINEAR_ACCELERATION = + (10), /**< Acceleration not including gravity */ + SENSOR_TYPE_ROTATION_VECTOR = (11), + SENSOR_TYPE_RELATIVE_HUMIDITY = (12), + SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), + SENSOR_TYPE_OBJECT_TEMPERATURE = (14), + SENSOR_TYPE_VOLTAGE = (15), + SENSOR_TYPE_CURRENT = (16), + SENSOR_TYPE_COLOR = (17) +} sensors_type_t; + +/** struct sensors_vec_s is used to return a vector in a common format. */ +typedef struct { + union { + float v[3]; ///< 3D vector elements + struct { + float x; ///< X component of vector + float y; ///< Y component of vector + float z; ///< Z component of vector + }; ///< Struct for holding XYZ component + /* Orientation sensors */ + struct { + float roll; /**< Rotation around the longitudinal axis (the plane body, 'X + axis'). Roll is positive and increasing when moving + downward. -90 degrees <= roll <= 90 degrees */ + float pitch; /**< Rotation around the lateral axis (the wing span, 'Y + axis'). Pitch is positive and increasing when moving + upwards. -180 degrees <= pitch <= 180 degrees) */ + float heading; /**< Angle between the longitudinal axis (the plane body) + and magnetic north, measured clockwise when viewing from + the top of the device. 0-359 degrees */ + }; ///< Struct for holding roll/pitch/heading + }; ///< Union that can hold 3D vector array, XYZ components or + ///< roll/pitch/heading + int8_t status; ///< Status byte + uint8_t reserved[3]; ///< Reserved +} sensors_vec_t; + +/** struct sensors_color_s is used to return color data in a common format. */ +typedef struct { + union { + float c[3]; ///< Raw 3-element data + /* RGB color space */ + struct { + float r; /**< Red component */ + float g; /**< Green component */ + float b; /**< Blue component */ + }; ///< RGB data in floating point notation + }; ///< Union of various ways to describe RGB colorspace + uint32_t rgba; /**< 24-bit RGBA value */ +} sensors_color_t; + +/* Sensor event (36 bytes) */ +/** struct sensor_event_s is used to provide a single sensor event in a common + * format. */ +typedef struct { + int32_t version; /**< must be sizeof(struct sensors_event_t) */ + int32_t sensor_id; /**< unique sensor identifier */ + int32_t type; /**< sensor type */ + int32_t reserved0; /**< reserved */ + int32_t timestamp; /**< time is in milliseconds */ + union { + float data[4]; ///< Raw data + sensors_vec_t acceleration; /**< acceleration values are in meter per second + per second (m/s^2) */ + sensors_vec_t + magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ + sensors_vec_t orientation; /**< orientation values are in degrees */ + sensors_vec_t gyro; /**< gyroscope values are in rad/s */ + float temperature; /**< temperature is in degrees centigrade (Celsius) */ + float distance; /**< distance in centimeters */ + float light; /**< light in SI lux units */ + float pressure; /**< pressure in hectopascal (hPa) */ + float relative_humidity; /**< relative humidity in percent */ + float current; /**< current in milliamps (mA) */ + float voltage; /**< voltage in volts (V) */ + sensors_color_t color; /**< color in RGB component values */ + }; ///< Union for the wide ranges of data we can carry +} sensors_event_t; + +/* Sensor details (40 bytes) */ +/** struct sensor_s is used to describe basic information about a specific + * sensor. */ +typedef struct { + char name[12]; /**< sensor name */ + int32_t version; /**< version of the hardware + driver */ + int32_t sensor_id; /**< unique sensor identifier */ + int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */ + float max_value; /**< maximum value of this sensor's value in SI units */ + float min_value; /**< minimum value of this sensor's value in SI units */ + float resolution; /**< smallest difference between two values reported by this + sensor */ + int32_t min_delay; /**< min delay in microseconds between events. zero = not a + constant rate */ +} sensor_t; + +/** @brief Common sensor interface to unify various sensors. + * Intentionally modeled after sensors.h in the Android API: + * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h + */ +class Adafruit_Sensor { +public: + // Constructor(s) + Adafruit_Sensor() {} + virtual ~Adafruit_Sensor() {} + + // These must be defined by the subclass + + /*! @brief Whether we should automatically change the range (if possible) for + higher precision + @param enabled True if we will try to autorange */ + virtual void enableAutoRange(bool enabled) { + (void)enabled; /* suppress unused warning */ + }; + + /*! @brief Get the latest sensor event + @returns True if able to fetch an event */ + virtual bool getEvent(sensors_event_t *) = 0; + /*! @brief Get info about the sensor itself */ + virtual void getSensor(sensor_t *) = 0; + + void printSensorDetails(void); + +private: + bool _autoRange; +}; + +#endif diff --git a/libraries/Adafruit_Sensor-master/LICENSE.txt b/libraries/Adafruit_Sensor-master/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/libraries/Adafruit_Sensor-master/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libraries/Adafruit_Sensor-master/README.md b/libraries/Adafruit_Sensor-master/README.md new file mode 100644 index 0000000..97b2c12 --- /dev/null +++ b/libraries/Adafruit_Sensor-master/README.md @@ -0,0 +1,229 @@ +# Adafruit Unified Sensor Driver # + +Many small embedded systems exist to collect data from sensors, analyse the data, and either take an appropriate action or send that sensor data to another system for processing. + +One of the many challenges of embedded systems design is the fact that parts you used today may be out of production tomorrow, or system requirements may change and you may need to choose a different sensor down the road. + +Creating new drivers is a relatively easy task, but integrating them into existing systems is both error prone and time consuming since sensors rarely use the exact same units of measurement. + +By reducing all data to a single **sensors\_event\_t** 'type' and settling on specific, **standardised SI units** for each sensor family the same sensor types return values that are comparable with any other similar sensor. This enables you to switch sensor models with very little impact on the rest of the system, which can help mitigate some of the risks and problems of sensor availability and code reuse. + +The unified sensor abstraction layer is also useful for data-logging and data-transmission since you only have one well-known type to log or transmit over the air or wire. + +## Unified Sensor Drivers ## + +The following drivers are based on the Adafruit Unified Sensor Driver: + +**Accelerometers** + - [Adafruit\_ADXL345](https://github.com/adafruit/Adafruit_ADXL345) + - [Adafruit\_LSM303DLHC](https://github.com/adafruit/Adafruit_LSM303DLHC) + - [Adafruit\_MMA8451\_Library](https://github.com/adafruit/Adafruit_MMA8451_Library) + +**Gyroscope** + - [Adafruit\_L3GD20\_U](https://github.com/adafruit/Adafruit_L3GD20_U) + +**Light** + - [Adafruit\_TSL2561](https://github.com/adafruit/Adafruit_TSL2561) + - [Adafruit\_TSL2591\_Library](https://github.com/adafruit/Adafruit_TSL2591_Library) + +**Magnetometers** + - [Adafruit\_LSM303DLHC](https://github.com/adafruit/Adafruit_LSM303DLHC) + - [Adafruit\_HMC5883\_Unified](https://github.com/adafruit/Adafruit_HMC5883_Unified) + +**Barometric Pressure** + - [Adafruit\_BMP085\_Unified](https://github.com/adafruit/Adafruit_BMP085_Unified) + - [Adafruit\_BMP183\_Unified\_Library](https://github.com/adafruit/Adafruit_BMP183_Unified_Library) + +**Humidity & Temperature** + - [DHT-sensor-library](https://github.com/adafruit/DHT-sensor-library) + +**Humidity, Temperature, & Barometric Pressure** + - [Adafruit_BME280_Library](https://github.com/adafruit/Adafruit_BME280_Library/) + +**Orientation** + - [Adafruit_BNO055](https://github.com/adafruit/Adafruit_BNO055) + +**All in one device** +- [Adafruit_LSM9DS0](https://github.com/adafruit/Adafruit_LSM9DS0_Library) (accelerometer, gyroscope, magnetometer) +- [Adafruit_LSM9DS1](https://github.com/adafruit/Adafruit_LSM9DS1/) (accelerometer, gyroscope, magnetometer) + + +## How Does it Work? ## + +Any driver that supports the Adafruit unified sensor abstraction layer will implement the Adafruit\_Sensor base class. There are two main typedefs and one enum defined in Adafruit_Sensor.h that are used to 'abstract' away the sensor details and values: + +**Sensor Types (sensors\_type\_t)** + +These pre-defined sensor types are used to properly handle the two related typedefs below, and allows us determine what types of units the sensor uses, etc. + +```c++ +/** Sensor types */ +typedef enum +{ + SENSOR_TYPE_ACCELEROMETER = (1), + SENSOR_TYPE_MAGNETIC_FIELD = (2), + SENSOR_TYPE_ORIENTATION = (3), + SENSOR_TYPE_GYROSCOPE = (4), + SENSOR_TYPE_LIGHT = (5), + SENSOR_TYPE_PRESSURE = (6), + SENSOR_TYPE_PROXIMITY = (8), + SENSOR_TYPE_GRAVITY = (9), + SENSOR_TYPE_LINEAR_ACCELERATION = (10), + SENSOR_TYPE_ROTATION_VECTOR = (11), + SENSOR_TYPE_RELATIVE_HUMIDITY = (12), + SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), + SENSOR_TYPE_VOLTAGE = (15), + SENSOR_TYPE_CURRENT = (16), + SENSOR_TYPE_COLOR = (17) +} sensors_type_t; +``` + +**Sensor Details (sensor\_t)** + +This typedef describes the specific capabilities of this sensor, and allows us to know what sensor we are using beneath the abstraction layer. + +```c++ +/* Sensor details (40 bytes) */ +/** struct sensor_s is used to describe basic information about a specific sensor. */ +typedef struct +{ + char name[12]; + int32_t version; + int32_t sensor_id; + int32_t type; + float max_value; + float min_value; + float resolution; + int32_t min_delay; +} sensor_t; +``` + +The individual fields are intended to be used as follows: + +- **name**: The sensor name or ID, up to a maximum of twelve characters (ex. "MPL115A2") +- **version**: The version of the sensor HW and the driver to allow us to differentiate versions of the board or driver +- **sensor\_id**: A unique sensor identifier that is used to differentiate this specific sensor instance from any others that are present on the system or in the sensor network +- **type**: The sensor type, based on **sensors\_type\_t** in sensors.h +- **max\_value**: The maximum value that this sensor can return (in the appropriate SI unit) +- **min\_value**: The minimum value that this sensor can return (in the appropriate SI unit) +- **resolution**: The smallest difference between two values that this sensor can report (in the appropriate SI unit) +- **min\_delay**: The minimum delay in microseconds between two sensor events, or '0' if there is no constant sensor rate + +**Sensor Data/Events (sensors\_event\_t)** + +This typedef is used to return sensor data from any sensor supported by the abstraction layer, using standard SI units and scales. + +```c++ +/* Sensor event (36 bytes) */ +/** struct sensor_event_s is used to provide a single sensor event in a common format. */ +typedef struct +{ + int32_t version; + int32_t sensor_id; + int32_t type; + int32_t reserved0; + int32_t timestamp; + union + { + float data[4]; + sensors_vec_t acceleration; + sensors_vec_t magnetic; + sensors_vec_t orientation; + sensors_vec_t gyro; + float temperature; + float distance; + float light; + float pressure; + float relative_humidity; + float current; + float voltage; + sensors_color_t color; + }; +} sensors_event_t; +``` +It includes the following fields: + +- **version**: Contain 'sizeof(sensors\_event\_t)' to identify which version of the API we're using in case this changes in the future +- **sensor\_id**: A unique sensor identifier that is used to differentiate this specific sensor instance from any others that are present on the system or in the sensor network (must match the sensor\_id value in the corresponding sensor\_t enum above!) +- **type**: the sensor type, based on **sensors\_type\_t** in sensors.h +- **timestamp**: time in milliseconds when the sensor value was read +- **data[4]**: An array of four 32-bit values that allows us to encapsulate any type of sensor data via a simple union (further described below) + +**Required Functions** + +In addition to the two standard types and the sensor type enum, all drivers based on Adafruit_Sensor must also implement the following two functions: + +```c++ +bool getEvent(sensors_event_t*); +``` +Calling this function will populate the supplied sensors\_event\_t reference with the latest available sensor data. You should call this function as often as you want to update your data. + +```c++ +void getSensor(sensor_t*); +``` +Calling this function will provide some basic information about the sensor (the sensor name, driver version, min and max values, etc. + +**Standardised SI values for sensors\_event\_t** + +A key part of the abstraction layer is the standardisation of values on SI units of a particular scale, which is accomplished via the data[4] union in sensors\_event\_t above. This 16 byte union includes fields for each main sensor type, and uses the following SI units and scales: + +- **acceleration**: values are in **meter per second per second** (m/s^2) +- **magnetic**: values are in **micro-Tesla** (uT) +- **orientation**: values are in **degrees** +- **gyro**: values are in **rad/s** +- **temperature**: values in **degrees centigrade** (Celsius) +- **distance**: values are in **centimeters** +- **light**: values are in **SI lux** units +- **pressure**: values are in **hectopascal** (hPa) +- **relative\_humidity**: values are in **percent** +- **current**: values are in **milliamps** (mA) +- **voltage**: values are in **volts** (V) +- **color**: values are in 0..1.0 RGB channel luminosity and 32-bit RGBA format + +## The Unified Driver Abstraction Layer in Practice ## + +Using the unified sensor abstraction layer is relatively easy once a compliant driver has been created. + +Every compliant sensor can now be read using a single, well-known 'type' (sensors\_event\_t), and there is a standardised way of interrogating a sensor about its specific capabilities (via sensor\_t). + +An example of reading the [TSL2561](https://github.com/adafruit/Adafruit_TSL2561) light sensor can be seen below: + +```c++ + Adafruit_TSL2561 tsl = Adafruit_TSL2561(TSL2561_ADDR_FLOAT, 12345); + ... + /* Get a new sensor event */ + sensors_event_t event; + tsl.getEvent(&event); + + /* Display the results (light is measured in lux) */ + if (event.light) + { + Serial.print(event.light); Serial.println(" lux"); + } + else + { + /* If event.light = 0 lux the sensor is probably saturated + and no reliable data could be generated! */ + Serial.println("Sensor overload"); + } +``` + +Similarly, we can get the basic technical capabilities of this sensor with the following code: + +```c++ + sensor_t sensor; + + sensor_t sensor; + tsl.getSensor(&sensor); + + /* Display the sensor details */ + Serial.println("------------------------------------"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" lux"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" lux"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" lux"); + Serial.println("------------------------------------"); + Serial.println(""); +``` diff --git a/libraries/Adafruit_Sensor-master/examples/sensortest/sensortest.ino b/libraries/Adafruit_Sensor-master/examples/sensortest/sensortest.ino new file mode 100644 index 0000000..d87f60d --- /dev/null +++ b/libraries/Adafruit_Sensor-master/examples/sensortest/sensortest.ino @@ -0,0 +1,153 @@ +#include +#include +#include + +/* Assign a unique ID to this sensor at the same time */ +/* Uncomment following line for default Wire bus */ +Adafruit_ADXL343 accel = Adafruit_ADXL343(12345); + +/* NeoTrellis M4, etc. */ +/* Uncomment following line for Wire1 bus */ +//Adafruit_ADXL343 accel = Adafruit_ADXL343(12345, &Wire1); + +void displaySensorDetails(void) +{ + sensor_t sensor; + accel.getSensor(&sensor); + Serial.println("------------------------------------"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" m/s^2"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" m/s^2"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" m/s^2"); + Serial.println("------------------------------------"); + Serial.println(""); + delay(500); +} + +void displayDataRate(void) +{ + Serial.print ("Data Rate: "); + + switch(accel.getDataRate()) + { + case ADXL343_DATARATE_3200_HZ: + Serial.print ("3200 "); + break; + case ADXL343_DATARATE_1600_HZ: + Serial.print ("1600 "); + break; + case ADXL343_DATARATE_800_HZ: + Serial.print ("800 "); + break; + case ADXL343_DATARATE_400_HZ: + Serial.print ("400 "); + break; + case ADXL343_DATARATE_200_HZ: + Serial.print ("200 "); + break; + case ADXL343_DATARATE_100_HZ: + Serial.print ("100 "); + break; + case ADXL343_DATARATE_50_HZ: + Serial.print ("50 "); + break; + case ADXL343_DATARATE_25_HZ: + Serial.print ("25 "); + break; + case ADXL343_DATARATE_12_5_HZ: + Serial.print ("12.5 "); + break; + case ADXL343_DATARATE_6_25HZ: + Serial.print ("6.25 "); + break; + case ADXL343_DATARATE_3_13_HZ: + Serial.print ("3.13 "); + break; + case ADXL343_DATARATE_1_56_HZ: + Serial.print ("1.56 "); + break; + case ADXL343_DATARATE_0_78_HZ: + Serial.print ("0.78 "); + break; + case ADXL343_DATARATE_0_39_HZ: + Serial.print ("0.39 "); + break; + case ADXL343_DATARATE_0_20_HZ: + Serial.print ("0.20 "); + break; + case ADXL343_DATARATE_0_10_HZ: + Serial.print ("0.10 "); + break; + default: + Serial.print ("???? "); + break; + } + Serial.println(" Hz"); +} + +void displayRange(void) +{ + Serial.print ("Range: +/- "); + + switch(accel.getRange()) + { + case ADXL343_RANGE_16_G: + Serial.print ("16 "); + break; + case ADXL343_RANGE_8_G: + Serial.print ("8 "); + break; + case ADXL343_RANGE_4_G: + Serial.print ("4 "); + break; + case ADXL343_RANGE_2_G: + Serial.print ("2 "); + break; + default: + Serial.print ("?? "); + break; + } + Serial.println(" g"); +} + +void setup(void) +{ + Serial.begin(9600); + while (!Serial); + Serial.println("Accelerometer Test"); Serial.println(""); + + /* Initialise the sensor */ + if(!accel.begin()) + { + /* There was a problem detecting the ADXL343 ... check your connections */ + Serial.println("Ooops, no ADXL343 detected ... Check your wiring!"); + while(1); + } + + /* Set the range to whatever is appropriate for your project */ + accel.setRange(ADXL343_RANGE_16_G); + // accel.setRange(ADXL343_RANGE_8_G); + // accel.setRange(ADXL343_RANGE_4_G); + // accel.setRange(ADXL343_RANGE_2_G); + + /* Display some basic information on this sensor */ + displaySensorDetails(); + displayDataRate(); + displayRange(); + Serial.println(""); +} + +void loop(void) +{ + /* Get a new sensor event */ + sensors_event_t event; + accel.getEvent(&event); + + /* Display the results (acceleration is measured in m/s^2) */ + Serial.print("X: "); Serial.print(event.acceleration.x); Serial.print(" "); + Serial.print("Y: "); Serial.print(event.acceleration.y); Serial.print(" "); + Serial.print("Z: "); Serial.print(event.acceleration.z); Serial.print(" ");Serial.println("m/s^2 "); + delay(500); +} diff --git a/libraries/Adafruit_Sensor-master/library.properties b/libraries/Adafruit_Sensor-master/library.properties new file mode 100644 index 0000000..d1c3632 --- /dev/null +++ b/libraries/Adafruit_Sensor-master/library.properties @@ -0,0 +1,11 @@ +name=Adafruit Unified Sensor +version=1.1.4 +author=Adafruit +maintainer=Adafruit +sentence=Required for all Adafruit Unified Sensor based libraries. +paragraph=A unified sensor abstraction layer used by many Adafruit sensor libraries. +category=Sensors +url=https://github.com/adafruit/Adafruit_Sensor +architectures=* +includes=Adafruit_Sensor.h + diff --git a/libraries/Heltec_ESP32_Dev-Boards/LICENSE b/libraries/Heltec_ESP32_Dev-Boards/LICENSE new file mode 100644 index 0000000..bc28085 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2019 Heltec Automation(TM) + +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. + +See more at http://www.heltec.cn diff --git a/libraries/Heltec_ESP32_Dev-Boards/README.md b/libraries/Heltec_ESP32_Dev-Boards/README.md new file mode 100644 index 0000000..d1a467d --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/README.md @@ -0,0 +1,158 @@ +# Heltec_ESP32 Library + +English | [简体中文](#简体中文) + +**This library must work with [Heltec ESP32 develop framework](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series)! A detail document about how to install Heltec ESP32 development framework and this library available here:** + +**[http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino)** + +## CONTENT + +1. [How to install this library](#how-to-install-this-library) + + - [Use Arduino Library Manager](#use-arduino-library-manager) + + - [Use Git](#use-git) + +2. [How to use this library](#how-to-use-this-library) + +3. [API Reference](#api-reference) + +4. [Hardware Reference](#hardware-reference) + + - [PinoutDiagram](#pinoutdiagram) + + - [Schematic Diagram](#schematic-diagram) + + +*** + + +## How to install this library +*`We recommend using the Arduino library manager, it's the simplest way`* + +### Use Arduino Library Manager +Open Arduino IDE, then Select `Sketch`->`Include Library`->`Manage Libraries...` +Search `Heltec ESP32` and install it. + + + +### Use Git + +*Firstly, make sure git and the Arduino IDE have been installed first. If not, please refer [How to install Git and Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_git_and_arduino). When Arduino is installed correctly, you will find a folder in "Username/Documents/Arduino/Library". **this library must in this path!*** + +Open "Git bash" in path "Username/Documents/Arduino/Library", and input: + + git clone https://github.com/HelTecAutomation/Heltec_ESP32.git + +You will see such a new folder in your library path, install done. + +![image](img/location.png) + + +## How to use this library + +**`This library requires the [Heltec ESP32 develop framework](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series)!`** + +A detailed step by step instructions to execute some examples available here: + +[http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino?id=_1-execute-a-example-likes-factorytestino](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino?id=_1-execute-a-example-likes-factorytestino) + +## API Reference +[OLED API](https://github.com/HelTecAutomation/Heltec_ESP32/blob/master/src/oled/OLEDDisplay.h) + +[LoRa API](src/lora/API.md) + +## Hardware Reference + +### Pinout Diagram +[https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram) +### Schematic Diagram +[https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram) + + +#### Note: +* ESP series chips are faster to download, please make sure to use the high-quality Micro USB cable, it will be easier to download with. + +[Summary of common problems](http://www.heltec.cn/summary-of-common-problems-in-wifi-kit-series-continuous-update/?lang=en) + +  +*** +*** +  + +## 简体中文 + +**这个Arduino库必须配合[Heltec ESP32编译环境](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series)一起使用!完整的“编译环境 + 库”的的教程可以参考这里:** + +**[http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino)** + +*** + +## 目录 + +1. [安装方法](#安装方法) + + - [通过Arduino库管理器安装](#通过Arduino库管理器安装) + + - [通过Git进行安装](#use-git) + +2. [怎样使用这个库](#怎样使用这个库) + +3. [API参考](#API参考) + +4. [硬件设计参考](#硬件设计参考) + + - [引脚图](#引脚图) + + - [原理图](#原理图) + + +*** + + +## 安装方法 +*`强烈推荐使用Arduino自带的“库管理器”进行安装!`* + +### 通过Arduino库管理器安装 +打开Arduino IDE, 选择`项目`->`加载库`->`管理库...`,打开“库管理器” +搜索`Heltec ESP32`并安装. + + + +### 通过Git进行安装 + +*首先,请确保`Git`和`Arduino IDE`都已经正确安装。如果没有,请参考这里的安装方法[How to install Git and Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_git_and_arduino)。 * + +**强调一下:这个库的路径必须位于操作系统的“文档/Arduino/libraries”文件夹内!!!文档文件夹是操作系统自带的,必须!必须!必须!** + +在“文档/Arduino/libraries”路径下打开"Git bash",输入: + + git clone https://github.com/HelTecAutomation/Heltec_ESP32.git + +如果一切正常,应该是这样的 + +![image](img/location_cn.png) + + +## 怎样使用这个库 + +如何运行一个例程?详细的使用指南:: + +[http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino?id=_1-execute-a-example-likes-factorytestino](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp32_Arduino?id=_1-execute-a-example-likes-factorytestino) + +## API参考 +[OLED API](https://github.com/HelTecAutomation/Heltec_ESP32/blob/master/src/oled/OLEDDisplay.h) + +[LoRa API](src/lora/API.md) + +## 硬件设计参考 + +### 引脚图 +[https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram) +### 原理图 +[https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram) + + + + diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/ADC_Read_Accurate/ADC_Read_Accurate.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/ADC_Read_Accurate/ADC_Read_Accurate.ino new file mode 100644 index 0000000..ff8b4b2 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/ADC_Read_Accurate/ADC_Read_Accurate.ino @@ -0,0 +1,39 @@ +/* + ADC read voltage via GPIO13 with 1% accuracy. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn +*/ + +void setup() { + Serial.begin(115200); +} + +void loop() { + Serial.println(ReadVoltage(13),3); + Serial.println(analogRead(13)); + delay(1000); +} + +double ReadVoltage(byte pin){ + double reading = analogRead(pin); // Reference voltage is 3v3 so maximum reading is 3v3 = 4095 in range 0 to 4095 + if(reading < 1 || reading >= 4095) + //return 0; + // return -0.000000000009824 * pow(reading,3) + 0.000000016557283 * pow(reading,2) + 0.000854596860691 * reading + 0.065440348345433; + return -0.000000000000016 * pow(reading,4) + 0.000000000118171 * pow(reading,3)- 0.000000301211691 * pow(reading,2)+ 0.001109019271794 * reading + 0.034143524634089; +} // Added an improved polynomial, use either, comment out as required + +//See more APIs about ADC here: https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/cores/esp32/esp32-hal-adc.h + +/* ADC readings v voltage + * y = -0.000000000009824x3 + 0.000000016557283x2 + 0.000854596860691x + 0.065440348345433 + // Polynomial curve match, based on raw data thus: + * 464 0.5 + * 1088 1.0 + * 1707 1.5 + * 2331 2.0 + * 2951 2.5 + * 3775 3.0 + * + */ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/ADC_Read_Simple/ADC_Read_Simple.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/ADC_Read_Simple/ADC_Read_Simple.ino new file mode 100644 index 0000000..16652cb --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/ADC_Read_Simple/ADC_Read_Simple.ino @@ -0,0 +1,44 @@ +/* + ADC read voltage via GPIO13 simple test. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn +*/ + +void setup() { + Serial.begin(115200); + /* + analogReadResolution(12); // Sets the sample bits and read resolution, default is 12-bit (0 - 4095), range is 9 - 12 bits + analogSetWidth(12); // Sets the sample bits and read resolution, default is 12-bit (0 - 4095), range is 9 - 12 bits + // 9-bit gives an ADC range of 0-511 + // 10-bit gives an ADC range of 0-1023 + // 11-bit gives an ADC range of 0-2047 + // 12-bit gives an ADC range of 0-4095 + analogSetCycles(8); // Set number of cycles per sample, default is 8 and provides an optimal result, range is 1 - 255 + analogSetSamples(1); // Set number of samples in the range, default is 1, it has an effect on sensitivity has been multiplied + analogSetClockDiv(1); // Set the divider for the ADC clock, default is 1, range is 1 - 255 + analogSetAttenuation(ADC_11db); // Sets the input attenuation for ALL ADC inputs, default is ADC_11db, range is ADC_0db, ADC_2_5db, ADC_6db, ADC_11db + analogSetPinAttenuation(VP,ADC_11db); // Sets the input attenuation, default is ADC_11db, range is ADC_0db, ADC_2_5db, ADC_6db, ADC_11db + // ADC_0db provides no attenuation so IN/OUT = 1 / 1 an input of 3 volts remains at 3 volts before ADC measurement + // ADC_2_5db provides an attenuation so that IN/OUT = 1 / 1.34 an input of 3 volts is reduced to 2.238 volts before ADC measurement + // ADC_6db provides an attenuation so that IN/OUT = 1 / 2 an input of 3 volts is reduced to 1.500 volts before ADC measurement + // ADC_11db provides an attenuation so that IN/OUT = 1 / 3.6 an input of 3 volts is reduced to 0.833 volts before ADC measurement + adcAttachPin(VP); // Attach a pin to ADC (also clears any other analog mode that could be on), returns TRUE/FALSE result + adcStart(VP); // Starts an ADC conversion on attached pin's bus + adcBusy(VP); // Check if conversion on the pin's ADC bus is currently running, returns TRUE/FALSE result + adcEnd(VP); // Get the result of the conversion (will wait if it have not finished), returns 16-bit integer result + */ + adcAttachPin(13); + analogSetClockDiv(255); // 1338mS +} + +void loop() { + int timer = micros(); + Serial.print(analogRead(13)); + Serial.print(" "); + Serial.println(micros() - timer); +} + +//See more APIs about ADC here: https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/cores/esp32/esp32-hal-adc.h + diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/Battery_power/Battery_power.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/Battery_power/Battery_power.ino new file mode 100644 index 0000000..75958a5 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/Battery_power/Battery_power.ino @@ -0,0 +1,70 @@ +/* + * HelTec Automation(TM) Electricity detection example. + * + * Function summary: + * + * - Vext connected to 3.3V via a MOS-FET, the gate pin connected to GPIO21; + * + * - Battery power detection is achieved by detecting the voltage of GPIO13; + * + * - OLED display and PE4259(RF switch) use Vext as power supply; + * + * - WIFI Kit series V1 don't have Vext control function; + * + * HelTec AutoMation, Chengdu, China. + * 成都惠利特自动化科技有限公司 + * https://heltec.org + * support@heltec.cn + * + * this project also release in GitHub: + * https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series + * +*/ +#include "Arduino.h" +#include +#include "heltec.h" + +#define Fbattery 3700 //The default battery is 3700mv when the battery is fully charged. + +float XS = 0.00225; //The returned reading is multiplied by this XS to get the battery voltage. +uint16_t MUL = 1000; +uint16_t MMUL = 100; + +void setup() +{ + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Enable*/, true /*Serial Enable*/); + + Heltec.display->init(); + Heltec.display->flipScreenVertically(); + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->drawString(0, 0, "OLED Start"); + Heltec.display->display(); + delay(1000); + Heltec.display->clear(); + + adcAttachPin(13); + analogSetClockDiv(255); // 1338mS + + Serial.begin(115200); +} + +void loop() +{ + //WiFi LoRa 32 -- hardare versrion ≥ 2.3 + //WiFi Kit 32 -- hardare versrion ≥ 2 + //Wireless Stick -- hardare versrion ≥ 2.3 + //Wireless Stick Lite -- hardare versrion ≥ 2.3 + //Battery voltage read pin changed from GPIO13 to GPI37 + uint16_t c = analogRead(37)*XS*MUL; + Serial.println(analogRead(37)); + // uint16_t c = analogRead(13)*XS*MUL; + // Serial.println(analogRead(13)); + + Heltec.display->drawString(0, 0, "Remaining battery still has:"); + Heltec.display->drawString(0, 10, "VBAT:"); + Heltec.display->drawString(35, 10, (String)c); + Heltec.display->drawString(60, 10, "(mV)"); + Heltec.display->display(); + delay(2000); + Heltec.display->clear(); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/README.md b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/README.md new file mode 100644 index 0000000..3f48b64 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ADC_Read_Voltage/README.md @@ -0,0 +1,5 @@ +# ADC_Read_Voltage_Simple +Basic ADC example read voltage. + +# ADC_Read_Voltage_Accurate +A function that improves the defaulot ADC reading accuracy to within 1% diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/README.md b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/README.md new file mode 100644 index 0000000..adc33b0 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/README.md @@ -0,0 +1,70 @@ +## Contents +------- +- [The Overview](#the-overview) +- [ShowCore](#showcore) +- [MoveCore](#movecore) +- [SpeedTest](#speedtest) +- [Information](#information) + +# The Overview +-------- +- The ESP32 chip has three cores. +- Two cores are fast cores and one core is a low-power core. +- Which is an example of ESP32 Dual Core on Arduino IDE including Data Passing and Task Synchronization. + + +## ShowCore +-------- +In the first step, we need to know which core the current program is running on. +we need a code to print the current core from the serial port. +``` +Serial.println(xPortGetCoreID()); +``` +![](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/resources/print_core.png) + +- Here are the examples we have prepared for you. +### [example](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/examples/Showcore) + +## MoveCore +-------- +- Run the program with the specified core +![](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/resources/MoveCore.png) + +- We use the following code to perform core switching +``` + xTaskCreatePinnedToCore( + codeForTask1, /*Task Function. */ + "Task_1", /*name of task. */ + 1000, /*Stack size of task. */ + NULL, /* parameter of the task. */ + 1, /* proiority of the task. */ + &Task1, /* Task handel to keep tra ck of created task. */ + 0); /* choose Core */ +``` + +### [example](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/examples/Movecore) + +## SpeedTest +-------- +- Test the speed of the two cores under different conditions +![](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/resources/SpeedTest.png) + +### [example](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/examples/SpeedTest) + +In this example the program is running as follows + +![](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/resources/Task_Synchronization.png) + +The result of the operation is as follows£º +![](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/esp32/libraries/ESP32/examples/ESP32_Dual_Core/resources/Result.png) +At this time, 0 core and 1 core full speed synchronous processing independent tasks + +## Information +-------- +![](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/InstallGuide/win-screenshots/WIFI_LoRa_32.png) + +- [PinoutDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/blob/master/PinoutDiagram/WIFI%20LoRa%2032(V2)%20.pdf) + +- node: [WIFI LoRa 32 V2](https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-17008247508.4.7bdf1d6f2XG3ID&id=575190433694) + +- Arduino 18.04 diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/Movecore/Movecore.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/Movecore/Movecore.ino new file mode 100644 index 0000000..917d47b --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/Movecore/Movecore.ino @@ -0,0 +1,34 @@ +#define LED1 25 +TaskHandle_t Task1; + +void codeForTask1( void * parameter ) +{ + for(;;) { + Serial.print("This Task run on Core: "); + Serial.println(xPortGetCoreID()); + + digitalWrite(LED1,HIGH); + delay(1000); + digitalWrite(LED1,LOW); + delay(1000); + } +} + + +void setup() { + Serial.begin(115200); + pinMode(LED1,OUTPUT); + + xTaskCreatePinnedToCore( + codeForTask1, /*Task Function. */ + "Task_1", /*name of task. */ + 1000, /*Stack size of task. */ + NULL, /* parameter of the task. */ + 1, /* proiority of the task. */ + &Task1, /* Task handel to keep tra ck of created task. */ + 0); /* choose Core */ +} + +void loop() { + +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/Showcore/Showcore.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/Showcore/Showcore.ino new file mode 100644 index 0000000..00a14b1 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/Showcore/Showcore.ino @@ -0,0 +1,15 @@ +#define LED1 25 + +void setup() { + Serial.begin(115200); + pinMode(LED1,OUTPUT); +} + +void loop() { + Serial.print("This loop Task run on Core: "); + Serial.println(xPortGetCoreID()); /*print the current core*/ + digitalWrite(LED1,HIGH); + delay(1000); + digitalWrite(LED1,LOW); + delay(1000); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/SpeedTest/SpeedTest.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/SpeedTest/SpeedTest.ino new file mode 100644 index 0000000..2d29e55 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/examples/SpeedTest/SpeedTest.ino @@ -0,0 +1,90 @@ +#define LED1 25 +long loops1 = 1000; +long loops2 = 1000; +long qq; +float t1; +int t2,t3; + +TaskHandle_t Task1, Task2; +SemaphoreHandle_t baton; + + +void artificialLoad () { + for ( long i = 0;i < loops1; i++){ + for ( long j = 1; j < loops2; j++) { + qq++; + t1 = 5000.0 * i; + t2 = 150 * 1234 * i; + t3 = j % 554 ; + } + } +} +void blink(){ + digitalWrite(LED1,HIGH); + delay(1000); + digitalWrite(LED1,LOW); + delay(1000); + } + +void codeForTask1( void * parameter ) +{ + for(;;){ + long start = millis(); + artificialLoad(); + Serial.print("Finish loop Task run on Core: "); + Serial.print(xPortGetCoreID()); + Serial.print(" Time "); + Serial.println(millis() - start); +// blink(); + } + } + + +void codeForTask2( void * parameter ) +{ + for(;;){ +// long start = millis(); +// artificialLoad(); +// Serial.print(" Finish loop Task run on Core: "); +// Serial.print(xPortGetCoreID()); +// Serial.print(" Time "); +// Serial.println(millis() - start); + delay(1000); + } + } +void setup() { + Serial.begin(115200); + pinMode(LED1,OUTPUT); + baton = xSemaphoreCreateMutex(); + + xTaskCreatePinnedToCore( + codeForTask1, + "Task_1", + 1000, + NULL, + 1, + &Task1, + 0); + + delay(500); + + xTaskCreatePinnedToCore( + codeForTask2, + "Task_2", + 1000, + NULL, + 1, + &Task2, + 1); +} + +void loop() { + long start = millis(); + artificialLoad(); + Serial.print(" Finish loop Task run on Core: "); + Serial.print(xPortGetCoreID()); + Serial.print(" Time "); + Serial.println(millis() - start); + delay(10); +// delay(5000); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/MoveCore.png b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/MoveCore.png new file mode 100644 index 0000000..7c70bfc Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/MoveCore.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/Result.png b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/Result.png new file mode 100644 index 0000000..9ab3046 Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/Result.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/SpeedTest.png b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/SpeedTest.png new file mode 100644 index 0000000..c1d9028 Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/SpeedTest.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/Task_Synchronization.png b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/Task_Synchronization.png new file mode 100644 index 0000000..01a0ddf Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/Task_Synchronization.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/print_core.png b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/print_core.png new file mode 100644 index 0000000..57ce005 Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ESP32_Dual_Core/resources/print_core.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ExternalWakeUp/ExternalWakeUp.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ExternalWakeUp/ExternalWakeUp.ino new file mode 100644 index 0000000..755cf3a --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ExternalWakeUp/ExternalWakeUp.ino @@ -0,0 +1,87 @@ + +/* + * *******************HelTec AutoMation WiFi_Kit_series Example****************** +Deep Sleep with External Wake Up +===================================== +This code displays how to use deep sleep with +an external trigger as a wake up source and how +to store data in RTC memory to use it over reboots + +This code is under Public Domain License. + +Hardware Connections +====================== +Push Button(PRG) to GPIO 0 pulled down with a 10K Ohm +resistor + +NOTE: +====== +Only RTC IO can be used as a source for external wake +source. They are pins: 0,2,4,12-15,25-27,32-39. + +Author: +Pranav Cherukupalli +*/ + +#include "driver/rtc_io.h" +#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex + +RTC_DATA_ATTR int bootCount = 0; + +/* +Method to print the reason by which ESP32 +has been awaken from sleep +*/ +void print_wakeup_reason(){ + esp_sleep_wakeup_cause_t wakeup_reason; + + wakeup_reason = esp_sleep_get_wakeup_cause(); + + switch(wakeup_reason) + { + case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; + case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; + case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break; + case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break; + case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break; + default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; + } +} + +void setup(){ + rtc_gpio_deinit(GPIO_NUM_0); + Serial.begin(115200); + delay(1000); //Take some time to open up the Serial Monitor + + //Increment boot number and print it every reboot + ++bootCount; + Serial.println("Boot number: " + String(bootCount)); + + //Print the wakeup reason for ESP32 + print_wakeup_reason(); + + /* + First we configure the wake up source + We set our ESP32 to wake up for an external trigger. + There are two types for ESP32, ext0 and ext1 . + ext0 uses RTC_IO to wakeup thus requires RTC peripherals + to be on while ext1 uses RTC Controller so doesnt need + peripherals to be powered on. + Note that using internal pullups/pulldowns also requires + RTC peripherals to be turned on. + */ + rtc_gpio_pulldown_en(GPIO_NUM_0); + esp_sleep_enable_ext0_wakeup(GPIO_NUM_0,0); //1 = High, 0 = Low + + //If you were to use ext1, you would use it like + //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH); + + //Go to sleep now + Serial.println("Going to sleep now"); + esp_deep_sleep_start(); + Serial.println("This will never be printed"); +} + +void loop(){ + //This is not going to be called +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/I2C_Scanner/I2C_Scanner.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/I2C_Scanner/I2C_Scanner.ino new file mode 100644 index 0000000..2749895 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/I2C_Scanner/I2C_Scanner.ino @@ -0,0 +1,71 @@ +/* Heltec Automation I2C scanner example (also it's a basic example how to use I2C1) + * + * ESP32 have two I2C (I2C0 and I2C1) bus + * + * OLED is connected to I2C0, so if scan with Wire (I2C0), the return address should be 0x3C. + * + * If you need scan other device address in I2C1... + * - Comment all Wire.***() codes; + * - Uncomment all Wire1.***() codes; + * + * I2C scan example and I2C0 + * + * HelTec AutoMation, Chengdu, China + * 成都惠利特自动化科技有限公司 + * www.heltec.org + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 + * */ + +#include "Arduino.h" +#include "heltec.h" + +void setup() +{ + Heltec.begin(true, false, true); + Wire.begin(SDA_OLED, SCL_OLED); //Scan OLED's I2C address via I2C0 + //Wire1.begin(SDA, SCL); //If there have other device on I2C1, scan the device address via I2C1 +} + +void loop() +{ + byte error, address; + int nDevices; + + Serial.println("Scanning..."); + + nDevices = 0; + for(address = 1; address < 127; address++ ) + { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + +// Wire1.beginTransmission(address); +// error = Wire1.endTransmission(); + + if (error == 0) + { + Serial.print("I2C device found at address 0x"); + if (address<16) + Serial.print("0"); + Serial.print(address,HEX); + Serial.println(" !"); + + nDevices++; + } + else if (error==4) + { + Serial.print("Unknown error at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + else + Serial.println("done\n"); + + delay(5000); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/PSRAM_Test/PSRAM_Test.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/PSRAM_Test/PSRAM_Test.ino new file mode 100644 index 0000000..acbae83 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/PSRAM_Test/PSRAM_Test.ino @@ -0,0 +1,26 @@ +/* Heltec Automation ESP32 external PSRAM read/write example + * + * Must have external PSRAM connected. + * + * HelTec AutoMation, Chengdu, China + * 成都惠利特自动化科技有限公司 + * https://heltec.org + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 + */ + + +#include + +void setup() { + Serial.begin(115200); + + Serial.printf("Total heap: %d\r\n", ESP.getHeapSize()); + Serial.printf("Free heap: %d\r\n", ESP.getFreeHeap()); + Serial.printf("Total PSRAM: %d\r\n", ESP.getPsramSize()); + byte* psdRamBuffer = (byte*)ps_malloc(4000000); + Serial.printf("Free PSRAM: %d\r\n", ESP.getFreePsram()); +} + +void loop() {} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/RTC_counter/RTC_counter.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/RTC_counter/RTC_counter.ino new file mode 100644 index 0000000..0a406ad --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/RTC_counter/RTC_counter.ino @@ -0,0 +1,18 @@ +#include "Arduino.h" +#include "soc/rtc.h" + +/*rtc_time_get() return the RTC counters +* the board using internal 150KHz RTC, but it's not accurate. +*/ +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); +} + +void loop() { + uint64_t rtc_counter1 = rtc_time_get(); + delay(1000); + uint64_t rtc_counter2 = rtc_time_get(); + + Serial.println((uint32_t)(rtc_counter2-rtc_counter1)); +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/Serial2/Serial2.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/Serial2/Serial2.ino new file mode 100644 index 0000000..573cc53 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/Serial2/Serial2.ino @@ -0,0 +1,41 @@ +/* + * Heltec Automation ESP32 Serial 1 & 2 example. + * shows the usage of all 3 hardware uarts + * work with ESP32's IO MUX + * + * to test the software and hardware + * wire Rx1 to Tx1 and Rx2 to Tx2 + * type text in serial monitor + * text will walk trough all 2 serials + * result is echoed to serial (usb) +*/ + + +void setup() { + Serial.begin(115200); + + // Serial1.begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert) + // Serial2.begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert) + // The txPin & rxPin can set to any output pin + + Serial1.begin(115200, SERIAL_8N1, 2, 17); + Serial2.begin(115200, SERIAL_8N1, 22, 23); +} + +void loop() { + + if(Serial.available()) { + int ch = Serial.read(); + Serial1.write(ch); + } + + if(Serial2.available()) { + int ch = Serial2.read(); + Serial2.write(ch); + } + + if(Serial2.available()) { + int ch = Serial2.read(); + Serial.write(ch); + } +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ULP/HoldPinStatus/HoldPinStatus.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ULP/HoldPinStatus/HoldPinStatus.ino new file mode 100644 index 0000000..5a6600e --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/ULP/HoldPinStatus/HoldPinStatus.ino @@ -0,0 +1,52 @@ +/* + * Hold GPIO status in deep sleep mode, use ESP32's ULP (Ultra Low Power) coprocessor + * + * HelTec AutoMation, Chengdu, China. + * 成都惠利特自动化科技有限公司 + * www.heltec.cn + * support@heltec.cn + * + *this project also release in GitHub: + *https://github.com/HelTecAutomation/ESP32_LoRaWAN +*/ +#include "soc/rtc_io_reg.h" +#include "driver/rtc_io.h" + +#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ +#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ + +RTC_DATA_ATTR int bootCount = 0; + +void setup(){ + rtc_gpio_hold_dis(GPIO_NUM_25); + pinMode(25,OUTPUT); + digitalWrite(25,LOW);//The on board LED will be OFF in wake up period + + Serial.begin(115200); + delay(1000); //Take some time to open up the Serial Monitor + + //Increment boot number and print it every reboot + ++bootCount; + Serial.println("Boot number: " + String(bootCount)); + delay(2); + + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + + " Seconds"); + delay(10); + + Serial.println("Going to sleep now"); + delay(2); + + rtc_gpio_init(GPIO_NUM_25); + pinMode(25,OUTPUT); + digitalWrite(25,HIGH);//The on board LED will be ON in deep sleep period + rtc_gpio_hold_en(GPIO_NUM_25); + + esp_deep_sleep_start(); + Serial.println("This will never be printed"); +} + +void loop(){ + //This is not going to be called +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/VextControl/VextControl.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/VextControl/VextControl.ino new file mode 100644 index 0000000..cdf3b8c --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/ESP32/VextControl/VextControl.ino @@ -0,0 +1,48 @@ +/* + * HelTec Automation(TM) Vext control example. + * + * Function summary: + * + * - Vext connected to 3.3V via a MOS-FET, the gate pin connected to GPIO21; + * + * - OLED display and PE4259(RF switch) use Vext as power supply; + * + * - WIFI Kit series V1 don't have Vext control function; + * + * HelTec AutoMation, Chengdu, China. + * 成都惠利特自动化科技有限公司 + * www.heltec.cn + * support@heltec.cn + * + * this project also release in GitHub: + * https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ + +#include "Arduino.h" +#include "heltec.h" +void setup() +{ + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/); + //OLED use Vext power supply, Vext must be turn ON before OLED initialition. + Heltec.display->init(); + + Heltec.display->flipScreenVertically(); + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->drawString(0, 0, "Hello, I'm happy today"); + Heltec.display->display(); + delay(1000); +} + +void loop() +{ + Heltec.display->sleep();//OLED sleep + Heltec.VextON(); + Serial.println("Turn OFF Vext"); + delay(5000); + + Heltec.VextOFF(); + Serial.println("Turn ON Vext"); + Heltec.display->wakeup(); + delay(5000); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WIFI_Kit_32_FactoryTest/WIFI_Kit_32_FactoryTest.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WIFI_Kit_32_FactoryTest/WIFI_Kit_32_FactoryTest.ino new file mode 100644 index 0000000..631c60c --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WIFI_Kit_32_FactoryTest/WIFI_Kit_32_FactoryTest.ino @@ -0,0 +1,139 @@ +/* + * HelTec Automation(TM) WIFI_Kit_32 factory test code, witch includ + * follow functions: + * + * - Basic OLED function test; + * + * - Basic serial port test(in baud rate 115200); + * + * - LED blink test; + * + * - WIFI join and scan test; + * + * - Timer test and some other Arduino basic functions. + * + * by Aaron.Lee from HelTec AutoMation, ChengDu, China + * 成都惠利特自动化科技有限公司 + * www.heltec.cn + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + +#include "heltec.h" +#include "WiFi.h" +#include "images.h" + + +void logo(){ + Heltec.display -> clear(); + Heltec.display -> drawXbm(0,5,logo_width,logo_height,(const unsigned char *)logo_bits); + Heltec.display -> display(); +} + +void WIFISetUp(void) +{ + // Set WiFi to station mode and disconnect from an AP if it was previously connected + WiFi.disconnect(true); + delay(1000); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); + WiFi.begin("Your WIFI SSID","Your WIFI Password"); + delay(100); + + byte count = 0; + while(WiFi.status() != WL_CONNECTED && count < 10) + { + count ++; + delay(500); + Heltec.display -> drawString(0, 0, "Connecting..."); + Heltec.display -> display(); + } + + Heltec.display -> clear(); + if(WiFi.status() == WL_CONNECTED) + { + Heltec.display -> drawString(0, 0, "Connecting...OK."); + Heltec.display -> display(); +// delay(500); + } + else + { + Heltec.display -> clear(); + Heltec.display -> drawString(0, 0, "Connecting...Failed"); + Heltec.display -> display(); +// while(1); + } + Heltec.display -> drawString(0, 10, "WIFI Setup done"); + Heltec.display -> display(); + delay(500); +} + +void WIFIScan(void) +{ + Heltec.display -> drawString(0, 20, "Scan start..."); + Heltec.display -> display(); + + int n = WiFi.scanNetworks(); + Heltec.display -> drawString(0, 30, "Scan done"); + Heltec.display -> display(); + delay(500); + Heltec.display -> clear(); + + if (n == 0) + { + Heltec.display -> clear(); + Heltec.display -> drawString(0, 0, "no network found"); + Heltec.display -> display(); + while(1); + } + else + { + Serial.print(n); + Heltec.display -> drawString(0, 0, (String)n); + Heltec.display -> drawString(14, 0, "networks found:"); + Heltec.display -> display(); + delay(500); + + for (int i = 0; i < n; ++i) { + // Print SSID and RSSI for each network found + Heltec.display -> drawString(0, (i+1)*9,(String)(i + 1)); + Heltec.display -> drawString(6, (i+1)*9, ":"); + Heltec.display -> drawString(12,(i+1)*9, (String)(WiFi.SSID(i))); + Heltec.display -> drawString(90,(i+1)*9, " ("); + Heltec.display -> drawString(98,(i+1)*9, (String)(WiFi.RSSI(i))); + Heltec.display -> drawString(114,(i+1)*9, ")"); + // display.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); + delay(10); + } + } + + Heltec.display -> display(); + delay(800); + Heltec.display -> clear(); + +} + +void setup() +{ + pinMode(LED,OUTPUT); + digitalWrite(LED,HIGH); + + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Enable*/, true /*Serial Enable*/); + + logo(); + delay(300); + Heltec.display->clear(); + WIFISetUp(); + + WiFi.disconnect(true);//重新初始化WIFI + delay(1000); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); +} + +void loop() +{ + WIFIScan(); + delay(2000); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WIFI_Kit_32_FactoryTest/images.h b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WIFI_Kit_32_FactoryTest/images.h new file mode 100644 index 0000000..4ef5640 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WIFI_Kit_32_FactoryTest/images.h @@ -0,0 +1,102 @@ +#ifndef IMAGES_H +#define IMAGES_H + +#define logo_width 128 +#define logo_height 53 +const static char logo_bits[] = { + + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xF0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x07, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0xF0, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0xF8, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xFC, 0x1F, + 0x80, 0xFF, 0x8F, 0x7F, 0xC0, 0xFF, 0x7F, 0xC0, 0xFF, 0x03, 0xC0, 0x3F, + 0xF0, 0x1F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, 0xC0, 0xFF, 0x7F, 0xF0, + 0xFF, 0x01, 0xF8, 0xFF, 0xF0, 0x0F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, + 0xE0, 0xFF, 0x7F, 0xF8, 0xFF, 0x01, 0xFE, 0xFF, 0xF8, 0x0F, 0xFE, 0x0F, + 0xF0, 0xFF, 0x87, 0x3F, 0xE0, 0xFF, 0x7F, 0xFC, 0xFF, 0x01, 0xFF, 0xFF, + 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC7, 0x3F, 0xE0, 0xFF, 0x3F, 0xFC, + 0xFF, 0x81, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC3, 0x1F, + 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xC0, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0x0F, + 0xF8, 0xFF, 0xC3, 0x1F, 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0xFF, 0x7F, + 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x01, 0xFE, + 0x00, 0xF0, 0x3F, 0x70, 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xE0, 0x0F, + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x1F, 0x60, 0xFC, 0xFF, 0xFF, 0x07, + 0xF8, 0x03, 0xE0, 0x0F, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x07, 0x20, + 0xFE, 0xFF, 0xFF, 0x07, 0xFC, 0xFF, 0xE1, 0x0F, 0x00, 0xFF, 0x00, 0xFF, + 0x7F, 0xF8, 0x07, 0x00, 0xFE, 0x83, 0xFF, 0x03, 0xFC, 0xFF, 0xF1, 0x0F, + 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xF8, 0x03, 0x00, 0xFE, 0x83, 0xFF, 0x03, + 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xFC, 0x03, 0x00, + 0xFE, 0x81, 0xFF, 0x03, 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, + 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0xFF, 0xF0, 0x07, + 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, + 0xFE, 0xFF, 0xF8, 0x07, 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x10, + 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0x00, 0xF8, 0x03, 0x80, 0x3F, 0x80, 0x3F, + 0x00, 0xFC, 0x03, 0x0C, 0xFF, 0xC0, 0xFF, 0x01, 0xFF, 0x00, 0xF8, 0x03, + 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0xFC, 0x07, 0x0E, 0xFF, 0xE0, 0xFF, 0x00, + 0x7F, 0x00, 0xF8, 0x03, 0xC0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, + 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x7F, 0xFC, 0xFF, 0xC1, 0x1F, 0xC0, 0xFF, + 0x1F, 0xFC, 0xFF, 0x0F, 0x7F, 0xE0, 0xFF, 0x00, 0xFF, 0x3F, 0xFC, 0xFF, + 0xC1, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, 0x7E, 0xE0, 0xFF, 0x80, + 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, + 0x7C, 0xF0, 0x7F, 0x80, 0xFF, 0x3F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, + 0x0F, 0xF0, 0xFF, 0x07, 0xF8, 0xF0, 0x7F, 0x80, 0xFF, 0x1F, 0xFE, 0xFF, + 0xE0, 0x0F, 0xE0, 0xFF, 0x0F, 0xE0, 0xFF, 0x07, 0xE0, 0xF0, 0x7F, 0x80, + 0xFF, 0x1F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, 0x07, 0x80, 0xFF, 0x03, + 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x8F, 0xF7, 0xFF, 0x7C, + 0xBC, 0xC7, 0xF3, 0xFF, 0xFC, 0xBC, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, + 0x8F, 0x73, 0xFF, 0xFE, 0xBE, 0xC7, 0xFB, 0xFF, 0xFE, 0xBD, 0x03, 0x00, + 0x00, 0xFC, 0x1F, 0x80, 0x8F, 0x73, 0xFF, 0xEF, 0xFE, 0xE7, 0xFB, 0x77, + 0xEF, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0xDF, 0x7B, 0x9C, 0xE7, + 0xFE, 0xF7, 0xE3, 0x71, 0xE7, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0xC0, + 0xDF, 0x79, 0x9E, 0xE3, 0xFE, 0xF3, 0xE3, 0x78, 0xE7, 0xFF, 0x03, 0x00, + 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x39, 0x8E, 0xF3, 0xFF, 0xFB, 0xE7, 0xF8, + 0xE7, 0xFE, 0x01, 0x00, 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x3F, 0x8E, 0x7F, + 0xFF, 0xFF, 0xE7, 0x38, 0x7F, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x0F, 0x70, + 0xDC, 0x1F, 0x0E, 0x3F, 0xFF, 0x9D, 0xF7, 0x38, 0x3F, 0xEF, 0x01, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +const char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; + +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WiFi_LoRa_32FactoryTest/WiFi_LoRa_32FactoryTest.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WiFi_LoRa_32FactoryTest/WiFi_LoRa_32FactoryTest.ino new file mode 100644 index 0000000..4c1298b --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WiFi_LoRa_32FactoryTest/WiFi_LoRa_32FactoryTest.ino @@ -0,0 +1,252 @@ +/* + * HelTec Automation(TM) WIFI_LoRa_32 factory test code, witch includ + * follow functions: + * + * - Basic OLED function test; + * + * - Basic serial port test(in baud rate 115200); + * + * - LED blink test; + * + * - WIFI connect and scan test; + * + * - LoRa Ping-Pong test (DIO0 -- GPIO26 interrup check the new incoming messages); + * + * - Timer test and some other Arduino basic functions. + * + * by Aaron.Lee from HelTec AutoMation, ChengDu, China + * 成都惠利特自动化科技有限公司 + * https://heltec.org + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + +#include "Arduino.h" +#include "heltec.h" +#include "WiFi.h" +#include "images.h" + +#define BAND 868E6 //you can set band here directly,e.g. 868E6,915E6 + +String rssi = "RSSI --"; +String packSize = "--"; +String packet; + +unsigned int counter = 0; + +bool receiveflag = false; // software flag for LoRa receiver, received data makes it true. + +long lastSendTime = 0; // last send time +int interval = 1000; // interval between sends +uint64_t chipid; + +void logo(){ + Heltec.display -> clear(); + Heltec.display -> drawXbm(0,5,logo_width,logo_height,(const unsigned char *)logo_bits); + Heltec.display -> display(); +} + +void WIFISetUp(void) +{ + // Set WiFi to station mode and disconnect from an AP if it was previously connected + WiFi.disconnect(true); + delay(100); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); + WiFi.begin("Your WiFi SSID","Your Password");//fill in "Your WiFi SSID","Your Password" + delay(100); + + byte count = 0; + while(WiFi.status() != WL_CONNECTED && count < 10) + { + count ++; + delay(500); + Heltec.display -> drawString(0, 0, "Connecting..."); + Heltec.display -> display(); + } + + Heltec.display -> clear(); + if(WiFi.status() == WL_CONNECTED) + { + Heltec.display -> drawString(0, 0, "Connecting...OK."); + Heltec.display -> display(); +// delay(500); + } + else + { + Heltec.display -> clear(); + Heltec.display -> drawString(0, 0, "Connecting...Failed"); + Heltec.display -> display(); + //while(1); + } + Heltec.display -> drawString(0, 10, "WIFI Setup done"); + Heltec.display -> display(); + delay(500); +} + +void WIFIScan(unsigned int value) +{ + unsigned int i; + WiFi.mode(WIFI_STA); + + for(i=0;i drawString(0, 20, "Scan start..."); + Heltec.display -> display(); + + int n = WiFi.scanNetworks(); + Heltec.display -> drawString(0, 30, "Scan done"); + Heltec.display -> display(); + delay(500); + Heltec.display -> clear(); + + if (n == 0) + { + Heltec.display -> clear(); + Heltec.display -> drawString(0, 0, "no network found"); + Heltec.display -> display(); + //while(1); + } + else + { + Heltec.display -> drawString(0, 0, (String)n); + Heltec.display -> drawString(14, 0, "networks found:"); + Heltec.display -> display(); + delay(500); + + for (int i = 0; i < n; ++i) { + // Print SSID and RSSI for each network found + Heltec.display -> drawString(0, (i+1)*9,(String)(i + 1)); + Heltec.display -> drawString(6, (i+1)*9, ":"); + Heltec.display -> drawString(12,(i+1)*9, (String)(WiFi.SSID(i))); + Heltec.display -> drawString(90,(i+1)*9, " ("); + Heltec.display -> drawString(98,(i+1)*9, (String)(WiFi.RSSI(i))); + Heltec.display -> drawString(114,(i+1)*9, ")"); + // display.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); + delay(10); + } + } + + Heltec.display -> display(); + delay(800); + Heltec.display -> clear(); + } +} + +bool resendflag=false; +bool deepsleepflag=false; +void interrupt_GPIO0() +{ + delay(10); + if(digitalRead(0)==0) + { + if(digitalRead(LED)==LOW) + {resendflag=true;} + else + { + deepsleepflag=true; + } + } +} + +void setup() +{ + Heltec.begin(true /*DisplayEnable Enable*/, true /*LoRa Enable*/, true /*Serial Enable*/, true /*LoRa use PABOOST*/, BAND /*LoRa RF working band*/); + + logo(); + delay(300); + Heltec.display -> clear(); + + WIFISetUp(); + WiFi.disconnect(); //重新初始化WIFI + WiFi.mode(WIFI_STA); + delay(100); + + WIFIScan(1); + + chipid=ESP.getEfuseMac();//The chip ID is essentially its MAC address(length: 6 bytes). + Serial.printf("ESP32ChipID=%04X",(uint16_t)(chipid>>32));//print High 2 bytes + Serial.printf("%08X\n",(uint32_t)chipid);//print Low 4bytes. + + attachInterrupt(0,interrupt_GPIO0,FALLING); + LoRa.onReceive(onReceive); + send(); + LoRa.receive(); + displaySendReceive(); +} + + +void loop() +{ + if(deepsleepflag) + { + LoRa.end(); + LoRa.sleep(); + delay(100); + pinMode(4,INPUT); + pinMode(5,INPUT); + pinMode(14,INPUT); + pinMode(15,INPUT); + pinMode(16,INPUT); + pinMode(17,INPUT); + pinMode(18,INPUT); + pinMode(19,INPUT); + pinMode(26,INPUT); + pinMode(27,INPUT); + digitalWrite(Vext,HIGH); + delay(2); + esp_deep_sleep_start(); + } + if(resendflag) + { + resendflag=false; + send(); + LoRa.receive(); + displaySendReceive(); + } + if(receiveflag) + { + digitalWrite(25,HIGH); + displaySendReceive(); + delay(1000); + receiveflag = false; + send(); + LoRa.receive(); + displaySendReceive(); + } +} + +void send() +{ + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter++); + LoRa.endPacket(); +} +void displaySendReceive() +{ + Heltec.display -> drawString(0, 50, "Packet " + (String)(counter-1) + " sent done"); + Heltec.display -> drawString(0, 0, "Received Size " + packSize + " packages:"); + Heltec.display -> drawString(0, 10, packet); + Heltec.display -> drawString(0, 20, "With " + rssi + "db"); + Heltec.display -> display(); + delay(100); + Heltec.display -> clear(); +} +void onReceive(int packetSize)//LoRa receiver interrupt service +{ + //if (packetSize == 0) return; + + packet = ""; + packSize = String(packetSize,DEC); + + while (LoRa.available()) + { + packet += (char) LoRa.read(); + } + + Serial.println(packet); + rssi = "RSSI: " + String(LoRa.packetRssi(), DEC); + receiveflag = true; +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WiFi_LoRa_32FactoryTest/images.h b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WiFi_LoRa_32FactoryTest/images.h new file mode 100644 index 0000000..4ef5640 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/WiFi_LoRa_32FactoryTest/images.h @@ -0,0 +1,102 @@ +#ifndef IMAGES_H +#define IMAGES_H + +#define logo_width 128 +#define logo_height 53 +const static char logo_bits[] = { + + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xF0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x07, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0xF0, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0xF8, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xFC, 0x1F, + 0x80, 0xFF, 0x8F, 0x7F, 0xC0, 0xFF, 0x7F, 0xC0, 0xFF, 0x03, 0xC0, 0x3F, + 0xF0, 0x1F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, 0xC0, 0xFF, 0x7F, 0xF0, + 0xFF, 0x01, 0xF8, 0xFF, 0xF0, 0x0F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, + 0xE0, 0xFF, 0x7F, 0xF8, 0xFF, 0x01, 0xFE, 0xFF, 0xF8, 0x0F, 0xFE, 0x0F, + 0xF0, 0xFF, 0x87, 0x3F, 0xE0, 0xFF, 0x7F, 0xFC, 0xFF, 0x01, 0xFF, 0xFF, + 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC7, 0x3F, 0xE0, 0xFF, 0x3F, 0xFC, + 0xFF, 0x81, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC3, 0x1F, + 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xC0, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0x0F, + 0xF8, 0xFF, 0xC3, 0x1F, 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0xFF, 0x7F, + 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x01, 0xFE, + 0x00, 0xF0, 0x3F, 0x70, 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xE0, 0x0F, + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x1F, 0x60, 0xFC, 0xFF, 0xFF, 0x07, + 0xF8, 0x03, 0xE0, 0x0F, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x07, 0x20, + 0xFE, 0xFF, 0xFF, 0x07, 0xFC, 0xFF, 0xE1, 0x0F, 0x00, 0xFF, 0x00, 0xFF, + 0x7F, 0xF8, 0x07, 0x00, 0xFE, 0x83, 0xFF, 0x03, 0xFC, 0xFF, 0xF1, 0x0F, + 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xF8, 0x03, 0x00, 0xFE, 0x83, 0xFF, 0x03, + 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xFC, 0x03, 0x00, + 0xFE, 0x81, 0xFF, 0x03, 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, + 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0xFF, 0xF0, 0x07, + 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, + 0xFE, 0xFF, 0xF8, 0x07, 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x10, + 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0x00, 0xF8, 0x03, 0x80, 0x3F, 0x80, 0x3F, + 0x00, 0xFC, 0x03, 0x0C, 0xFF, 0xC0, 0xFF, 0x01, 0xFF, 0x00, 0xF8, 0x03, + 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0xFC, 0x07, 0x0E, 0xFF, 0xE0, 0xFF, 0x00, + 0x7F, 0x00, 0xF8, 0x03, 0xC0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, + 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x7F, 0xFC, 0xFF, 0xC1, 0x1F, 0xC0, 0xFF, + 0x1F, 0xFC, 0xFF, 0x0F, 0x7F, 0xE0, 0xFF, 0x00, 0xFF, 0x3F, 0xFC, 0xFF, + 0xC1, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, 0x7E, 0xE0, 0xFF, 0x80, + 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, + 0x7C, 0xF0, 0x7F, 0x80, 0xFF, 0x3F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, + 0x0F, 0xF0, 0xFF, 0x07, 0xF8, 0xF0, 0x7F, 0x80, 0xFF, 0x1F, 0xFE, 0xFF, + 0xE0, 0x0F, 0xE0, 0xFF, 0x0F, 0xE0, 0xFF, 0x07, 0xE0, 0xF0, 0x7F, 0x80, + 0xFF, 0x1F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, 0x07, 0x80, 0xFF, 0x03, + 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x8F, 0xF7, 0xFF, 0x7C, + 0xBC, 0xC7, 0xF3, 0xFF, 0xFC, 0xBC, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, + 0x8F, 0x73, 0xFF, 0xFE, 0xBE, 0xC7, 0xFB, 0xFF, 0xFE, 0xBD, 0x03, 0x00, + 0x00, 0xFC, 0x1F, 0x80, 0x8F, 0x73, 0xFF, 0xEF, 0xFE, 0xE7, 0xFB, 0x77, + 0xEF, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0xDF, 0x7B, 0x9C, 0xE7, + 0xFE, 0xF7, 0xE3, 0x71, 0xE7, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0xC0, + 0xDF, 0x79, 0x9E, 0xE3, 0xFE, 0xF3, 0xE3, 0x78, 0xE7, 0xFF, 0x03, 0x00, + 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x39, 0x8E, 0xF3, 0xFF, 0xFB, 0xE7, 0xF8, + 0xE7, 0xFE, 0x01, 0x00, 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x3F, 0x8E, 0x7F, + 0xFF, 0xFF, 0xE7, 0x38, 0x7F, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x0F, 0x70, + 0xDC, 0x1F, 0x0E, 0x3F, 0xFF, 0x9D, 0xF7, 0x38, 0x3F, 0xEF, 0x01, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +const char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; + +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/Wireless_Stick_FactoryTest/Wireless_Stick_FactoryTest.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/Wireless_Stick_FactoryTest/Wireless_Stick_FactoryTest.ino new file mode 100644 index 0000000..620aada --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/Wireless_Stick_FactoryTest/Wireless_Stick_FactoryTest.ino @@ -0,0 +1,228 @@ +/* + * HelTec Automation(TM) Wireless_Stick factory test code, witch include + * follow functions: + * + * - Basic OLED function test; + * + * - Basic serial port test(in baud rate 115200); + * + * - LED blink test; + * + * - WIFI join and scan test; + * + * - LoRa Ping-Pong test(DIO0 -- GPIO26 interrup check the new incoming messages; + * + * - Timer test and some other Arduino basic functions. + * + * by Aaron.Lee from HelTec AutoMation, ChengDu, China + * 成都惠利特自动化科技有限公司 + * www.heltec.cn + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ +#include "heltec.h" +#include "WiFi.h" + +#define BAND 868E6 //you can set band here directly,e.g. 868E6,915E6 + +String rssi = "RSSI --"; +String packSize = "--"; +String packet; + +unsigned int counter = 0; + +bool receiveflag = false; // software flag for LoRa receiver, received data makes it true. + +long lastSendTime = 0; // last send time +int interval = 1000; // interval between sends + + +#define RFOPIN false + + + +void WIFISetUp(void) +{ + // Set WiFi to station mode and disconnect from an AP if it was previously connected + WiFi.disconnect(true); + delay(100); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); + WiFi.begin("Your WiFi SSID","Your Password");//fill in "Your WiFi SSID","Your Password" + delay(100); + Heltec.display->clear(); + + byte count = 0; + while(WiFi.status() != WL_CONNECTED && count < 10) + { + count ++; + delay(500); + Heltec.display->drawString(0, 0, "Connecting..."); + Heltec.display->display(); + } + //Heltec.display->clear(); + if(WiFi.status() == WL_CONNECTED) + { + //Heltec.display->drawString(35, 38, "WIFI SETUP"); + Heltec.display->drawString(0, 9, "OK"); + Heltec.display->display(); + delay(1000); + Heltec.display->clear(); + } + else + { + //Heltec.display->clear(); + Heltec.display->drawString(0, 9, "Failed"); + Heltec.display->display(); + delay(1000); + Heltec.display->clear(); + } +} + +void WIFIScan(unsigned int value) +{ + unsigned int i; + if(WiFi.status() != WL_CONNECTED) + { + WiFi.mode(WIFI_MODE_NULL); + } + for(i=0;idrawString(0, 0, "Scan start..."); + Heltec.display->display(); + + int n = WiFi.scanNetworks(); + Heltec.display->drawString(0, 9, "Scan done"); + Heltec.display->display(); + delay(500); + Heltec.display->clear(); + + if (n == 0) + { + Heltec.display->clear(); + Heltec.display->drawString(0, 18, "no network found"); + Heltec.display->display(); + //while(1); + } + else + { + Heltec.display->drawString(0, 18, (String)n + " nets found"); + Heltec.display->display(); + delay(2000); + Heltec.display->clear(); + } + } +} +bool resendflag=false; +bool deepsleepflag=false; +void interrupt_GPIO0() +{ + delay(10); + if(digitalRead(0)==0) + { + if(digitalRead(LED)==LOW) + {resendflag=true;} + else + { + deepsleepflag=true; + } + } +} +void setup() +{ + pinMode(LED,OUTPUT); + Heltec.begin(true /*DisplayEnable Enable*/, true /*LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /**/); + + WIFISetUp(); + + WiFi.disconnect(true); //重新初始化WIFI + delay(1000); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); + + WIFIScan(1); + + attachInterrupt(0,interrupt_GPIO0,FALLING); + LoRa.onReceive(onReceive); + send(); + LoRa.receive(); + displaySendReceive(); +} + +void loop() +{ + if(deepsleepflag) + { + LoRa.end(); + LoRa.sleep(); + delay(100); + pinMode(4,INPUT); + pinMode(5,INPUT); + pinMode(14,INPUT); + pinMode(15,INPUT); + pinMode(16,INPUT); + pinMode(17,INPUT); + pinMode(18,INPUT); + pinMode(19,INPUT); + pinMode(26,INPUT); + pinMode(27,INPUT); + digitalWrite(Vext,HIGH); + delay(2); + esp_deep_sleep_start(); + } + if(resendflag) + { + resendflag=false; + send(); + LoRa.receive(); + displaySendReceive(); + } + if(receiveflag) + { + digitalWrite(25,HIGH); + displaySendReceive(); + delay(1000); + receiveflag = false; + send(); + LoRa.receive(); + displaySendReceive(); + } +} + +void send() +{ + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter++); + LoRa.endPacket(); +} +void displaySendReceive() +{ + Heltec.display->drawString(0, 0, (String)(counter-1) + " sent done"); + Heltec.display->drawString(0,9, "Received " + packSize); + Heltec.display->drawString(0,16, packet); + Heltec.display->drawString(0,24, rssi); + Heltec.display -> display(); + delay(100); + Heltec.display -> clear(); +} + + +void onReceive(int packetSize)//LoRa receiver interrupt service +{ + //if (packetSize == 0) return; + + packet = ""; + packSize = String(packetSize,DEC); + + while (LoRa.available()) + { + packet += (char) LoRa.read(); + } + + Serial.println(packet); + rssi = "RSSI: " + String(LoRa.packetRssi(), DEC); + + receiveflag = true; +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/Wireless_Stick_Lite_FactoryTest/Wireless_Stick_Lite_FactoryTest.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/Wireless_Stick_Lite_FactoryTest/Wireless_Stick_Lite_FactoryTest.ino new file mode 100644 index 0000000..6131ad1 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Factory_Test/Wireless_Stick_Lite_FactoryTest/Wireless_Stick_Lite_FactoryTest.ino @@ -0,0 +1,221 @@ + + /*......................................................................................... + * HelTec Automation(TM) Wireless Stick Lite factory test code, witch include + * follow functions: + * + * - Node A search WiFi and send WiFi AP number to another node B via LoRa, Node B + * will send a string "abc" back to Node A, if Node A received "abc", test pass. + * + * - Basic serial port test(in baud rate 115200); + * + * - LED blink test; + * + * - WIFI join and scan test; + * + * - LoRa Ping-Pong test(DIO0 -- GPIO26 interrup check the new incoming messages; + * + * - Timer test and some other Arduino basic functions. + * + *by Aaron.Lee from HelTec AutoMation, ChengDu, China + *成都惠利特自动化科技有限公司 + *www.heltec.cn + * + *this project also realess in GitHub: + *https://github.com/HelTecAutomation/Heltec_ESP32 +*/ +#include "Arduino.h" +#include "heltec.h" +#include "WiFi.h" + +#define BAND 868E6 //you can set band here directly,e.g. 868E6,915E6 + +uint64_t chipid; +bool IsACKRecvied = false; + +String rssi = "RSSI --"; +String packSize = "--"; +String packet; + +unsigned int counter = 0; + +void WIFISetUp(void) +{ + //WIFI初始化 + 扫描演示 + // Set WiFi to station mode and disconnect from an AP if it was previously connected + WiFi.disconnect(true); + delay(1000); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); + WiFi.begin("Your WiFi SSID","Your Password");//fill in "Your WiFi SSID","Your Password" + delay(100); + + byte count = 0; + Serial.print("Connecting."); + while(WiFi.status() != WL_CONNECTED && count < 5) + { + count ++; + Serial.print("."); + delay(500); + } + + if(WiFi.status() == WL_CONNECTED) + { + Serial.println("\r\nConnecting...OK."); + } + else + { + Serial.println("Connecting...Failed"); + //while(1); + } + Serial.println("WIFI Setup done"); +} + +void WIFIScan(unsigned int value) +{ + unsigned int i; + if(WiFi.status() != WL_CONNECTED) + { + WiFi.mode(WIFI_MODE_NULL); + } + + for(i=0;i interval) + { + String message = "Hello,I'm coming!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) +{ + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) +{ + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; + + while (LoRa.available()) + { + incoming += (char)LoRa.read(); + } + + if (incomingLength != incoming.length()) + { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaMultipleCommunicationInterrupt/LoRaMultipleCommunicationInterrupt.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaMultipleCommunicationInterrupt/LoRaMultipleCommunicationInterrupt.ino new file mode 100644 index 0000000..49ef4da --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaMultipleCommunicationInterrupt/LoRaMultipleCommunicationInterrupt.ino @@ -0,0 +1,117 @@ +/* + Heltec.LoRa Multiple Communication + + This example provide a simple way to achieve one to multiple devices + communication. + + Each devices send datas in broadcast method. Make sure each devices + working in the same BAND, then set the localAddress and destination + as you want. + + Sends a message every half second, and uses interrup method check + the new incoming messages. Implements a one-byte addressing scheme, + with 0xFD as the broadcast address. You can set this address as you + want. + + The default interrupt pin in SX1276/8(DIO0) connected to ESP32's GPIO26 + + Note: while sending, Heltec.LoRa radio is not listening for incoming messages. + Note2: when using the callback method, you can't use any of the Stream + functions that rely on the timeout, such as readString, parseInt(), etc. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ + +#include "heltec.h" + +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 + + +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to + +String outgoing; // outgoing message +byte msgCount = 0; // count of outgoing messages +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() +{ + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + LoRa.onReceive(onReceive); + LoRa.receive(); + Serial.println("Heltec.LoRa init succeeded."); +} + +void loop() +{ + if (millis() - lastSendTime > interval) + { + String message = "Hello World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + LoRa.receive(); // go back into receive mode + } +} + +void sendMessage(String outgoing) +{ + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) +{ + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; // payload of packet + + while (LoRa.available()) // can't use readString() in callback + { + incoming += (char)LoRa.read(); // add bytes one by one + } + + if (incomingLength != incoming.length()) // check length for error + { + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) + { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaReceiver/LoRaReceiver.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaReceiver/LoRaReceiver.ino new file mode 100644 index 0000000..2aac3aa --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaReceiver/LoRaReceiver.ino @@ -0,0 +1,35 @@ +/* + Check the new incoming messages, and print via serialin 115200 baud rate. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ + +#include "heltec.h" + +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 +void setup() { + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + +} + +void loop() { + // try to parse packet + int packetSize = LoRa.parsePacket(); + if (packetSize) { + // received a packet + Serial.print("Received packet '"); + // read packet + while (LoRa.available()) { + Serial.print((char)LoRa.read()); + } + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); + } +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaReceiverInterrupt/LoRaReceiverInterrupt.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaReceiverInterrupt/LoRaReceiverInterrupt.ino new file mode 100644 index 0000000..bd1d80a --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaReceiverInterrupt/LoRaReceiverInterrupt.ino @@ -0,0 +1,47 @@ +/* + Uses interrup method check the new incoming messages, and print via serial + in 115200 baud rate. + + The default interrupt pin in SX1276/8(DIO0) connected to ESP32's GPIO26 + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ +#include "heltec.h" + +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 + + +void setup() { + + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + // register the receive callback + LoRa.onReceive(onReceive); + + // put the radio into receive mode + LoRa.receive(); +} + +void loop() { + // do nothing +} + +void onReceive(int packetSize) +{ + // received a packet + Serial.print("Received packet '"); + // read packet + for (int i = 0; i < packetSize; i++) + { + Serial.print((char)LoRa.read()); + } + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSender/LoRaSender.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSender/LoRaSender.ino new file mode 100644 index 0000000..ed9a7ec --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSender/LoRaSender.ino @@ -0,0 +1,46 @@ +/* + Basic test program, send date at the BAND you seted. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ +#include "heltec.h" +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 + +int counter = 0; + +void setup() { + + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + +} + +void loop() { + Serial.print("Sending packet: "); + Serial.println(counter); + // send packet + LoRa.beginPacket(); +/* +* LoRa.setTxPower(txPower,RFOUT_pin); +* txPower -- 0 ~ 20 +* RFOUT_pin could be RF_PACONFIG_PASELECT_PABOOST or RF_PACONFIG_PASELECT_RFO +* - RF_PACONFIG_PASELECT_PABOOST -- LoRa single output via PABOOST, maximum output 20dBm +* - RF_PACONFIG_PASELECT_RFO -- LoRa single output via RFO_HF / RFO_LF, maximum output 14dBm +*/ + LoRa.setTxPower(14,RF_PACONFIG_PASELECT_PABOOST); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + counter++; + digitalWrite(25, HIGH); // turn the LED on (HIGH is the voltage level) + delay(1000); // wait for a second + digitalWrite(25, LOW); // turn the LED off by making the voltage LOW + delay(1000); // wait for a second +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSetSpread/LoRaSetSpread.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSetSpread/LoRaSetSpread.ino new file mode 100644 index 0000000..3032abc --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSetSpread/LoRaSetSpread.ino @@ -0,0 +1,84 @@ +/* + Heltec.LoRa Duplex communication with Spreading Factor + + Sends a message every half second, and polls continually + for new incoming messages. Sets the Heltec.LoRa radio's spreading factor. + + Spreading factor affects how far apart the radio's transmissions + are, across the available bandwidth. Radios with different spreading + factors will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + Spreading factor affects reliability of transmission at high rates, however, + so avoid a hugh spreading factor when you're sending continually. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Spreading Factor. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ +#include "heltec.h" +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 + + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() +{ + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + + LoRa.setSpreadingFactor(8); // ranges from 6-12,default 7 see API docs + Serial.println("Heltec.LoRa init succeeded."); +} + +void loop() +{ + if (millis() - lastSendTime > interval) + { + String message = "Heltec.LoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) +{ + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) +{ + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) + { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSetSyncWord/LoRaSetSyncWord.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSetSyncWord/LoRaSetSyncWord.ino new file mode 100644 index 0000000..666fddb --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/LoRaSetSyncWord/LoRaSetSyncWord.ino @@ -0,0 +1,78 @@ +/* + Heltec.LoRa Duplex communication with Sync Word + + Sends a message every half second, and polls continually + for new incoming messages. Sets the Heltec.LoRa radio's Sync Word. + + Spreading factor is basically the radio's network ID. Radios with different + Sync Words will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Sync Word. + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ +#include "heltec.h" +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() +{ + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + LoRa.setSyncWord(0xF3); // ranges from 0-0xFF, default 0x34, see API docs + Serial.println("Heltec.LoRa init succeeded."); +} + +void loop() +{ + if (millis() - lastSendTime > interval) + { + String message = "HeHeltec.LoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) +{ + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) +{ + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) + { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Receiver/OLED_LoRa_Receiver.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Receiver/OLED_LoRa_Receiver.ino new file mode 100644 index 0000000..969bea0 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Receiver/OLED_LoRa_Receiver.ino @@ -0,0 +1,75 @@ +/* + This is a simple example show the Heltec.LoRa recived data in OLED. + + The onboard OLED display is SSD1306 driver and I2C interface. In order to make the + OLED correctly operation, you should output a high-low-high(1-0-1) signal by soft- + ware to OLED's reset pin, the low-level signal at least 5ms. + + OLED pins to ESP32 GPIOs via this connecthin: + OLED_SDA -- GPIO4 + OLED_SCL -- GPIO15 + OLED_RST -- GPIO16 + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ +#include "heltec.h" +#include "images.h" + +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 +String rssi = "RSSI --"; +String packSize = "--"; +String packet ; + +void logo(){ + Heltec.display->clear(); + Heltec.display->drawXbm(0,5,logo_width,logo_height,logo_bits); + Heltec.display->display(); +} + +void LoRaData(){ + Heltec.display->clear(); + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->drawString(0 , 15 , "Received "+ packSize + " bytes"); + Heltec.display->drawStringMaxWidth(0 , 26 , 128, packet); + Heltec.display->drawString(0, 0, rssi); + Heltec.display->display(); +} + +void cbk(int packetSize) { + packet =""; + packSize = String(packetSize,DEC); + for (int i = 0; i < packetSize; i++) { packet += (char) LoRa.read(); } + rssi = "RSSI " + String(LoRa.packetRssi(), DEC) ; + LoRaData(); +} + +void setup() { + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + Heltec.display->init(); + Heltec.display->flipScreenVertically(); + Heltec.display->setFont(ArialMT_Plain_10); + logo(); + delay(1500); + Heltec.display->clear(); + + Heltec.display->drawString(0, 0, "Heltec.LoRa Initial success!"); + Heltec.display->drawString(0, 10, "Wait for incoming data..."); + Heltec.display->display(); + delay(1000); + //LoRa.onReceive(cbk); + LoRa.receive(); +} + +void loop() { + int packetSize = LoRa.parsePacket(); + if (packetSize) { cbk(packetSize); } + delay(10); +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Receiver/images.h b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Receiver/images.h new file mode 100644 index 0000000..f057cdf --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Receiver/images.h @@ -0,0 +1,74 @@ +#define logo_width 128 +#define logo_height 53 +const unsigned char logo_bits[] = { + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xF0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x07, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0xF0, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0xF8, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xFC, 0x1F, + 0x80, 0xFF, 0x8F, 0x7F, 0xC0, 0xFF, 0x7F, 0xC0, 0xFF, 0x03, 0xC0, 0x3F, + 0xF0, 0x1F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, 0xC0, 0xFF, 0x7F, 0xF0, + 0xFF, 0x01, 0xF8, 0xFF, 0xF0, 0x0F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, + 0xE0, 0xFF, 0x7F, 0xF8, 0xFF, 0x01, 0xFE, 0xFF, 0xF8, 0x0F, 0xFE, 0x0F, + 0xF0, 0xFF, 0x87, 0x3F, 0xE0, 0xFF, 0x7F, 0xFC, 0xFF, 0x01, 0xFF, 0xFF, + 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC7, 0x3F, 0xE0, 0xFF, 0x3F, 0xFC, + 0xFF, 0x81, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC3, 0x1F, + 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xC0, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0x0F, + 0xF8, 0xFF, 0xC3, 0x1F, 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0xFF, 0x7F, + 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x01, 0xFE, + 0x00, 0xF0, 0x3F, 0x70, 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xE0, 0x0F, + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x1F, 0x60, 0xFC, 0xFF, 0xFF, 0x07, + 0xF8, 0x03, 0xE0, 0x0F, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x07, 0x20, + 0xFE, 0xFF, 0xFF, 0x07, 0xFC, 0xFF, 0xE1, 0x0F, 0x00, 0xFF, 0x00, 0xFF, + 0x7F, 0xF8, 0x07, 0x00, 0xFE, 0x83, 0xFF, 0x03, 0xFC, 0xFF, 0xF1, 0x0F, + 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xF8, 0x03, 0x00, 0xFE, 0x83, 0xFF, 0x03, + 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xFC, 0x03, 0x00, + 0xFE, 0x81, 0xFF, 0x03, 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, + 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0xFF, 0xF0, 0x07, + 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, + 0xFE, 0xFF, 0xF8, 0x07, 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x10, + 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0x00, 0xF8, 0x03, 0x80, 0x3F, 0x80, 0x3F, + 0x00, 0xFC, 0x03, 0x0C, 0xFF, 0xC0, 0xFF, 0x01, 0xFF, 0x00, 0xF8, 0x03, + 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0xFC, 0x07, 0x0E, 0xFF, 0xE0, 0xFF, 0x00, + 0x7F, 0x00, 0xF8, 0x03, 0xC0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, + 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x7F, 0xFC, 0xFF, 0xC1, 0x1F, 0xC0, 0xFF, + 0x1F, 0xFC, 0xFF, 0x0F, 0x7F, 0xE0, 0xFF, 0x00, 0xFF, 0x3F, 0xFC, 0xFF, + 0xC1, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, 0x7E, 0xE0, 0xFF, 0x80, + 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, + 0x7C, 0xF0, 0x7F, 0x80, 0xFF, 0x3F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, + 0x0F, 0xF0, 0xFF, 0x07, 0xF8, 0xF0, 0x7F, 0x80, 0xFF, 0x1F, 0xFE, 0xFF, + 0xE0, 0x0F, 0xE0, 0xFF, 0x0F, 0xE0, 0xFF, 0x07, 0xE0, 0xF0, 0x7F, 0x80, + 0xFF, 0x1F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, 0x07, 0x80, 0xFF, 0x03, + 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x8F, 0xF7, 0xFF, 0x7C, + 0xBC, 0xC7, 0xF3, 0xFF, 0xFC, 0xBC, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, + 0x8F, 0x73, 0xFF, 0xFE, 0xBE, 0xC7, 0xFB, 0xFF, 0xFE, 0xBD, 0x03, 0x00, + 0x00, 0xFC, 0x1F, 0x80, 0x8F, 0x73, 0xFF, 0xEF, 0xFE, 0xE7, 0xFB, 0x77, + 0xEF, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0xDF, 0x7B, 0x9C, 0xE7, + 0xFE, 0xF7, 0xE3, 0x71, 0xE7, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0xC0, + 0xDF, 0x79, 0x9E, 0xE3, 0xFE, 0xF3, 0xE3, 0x78, 0xE7, 0xFF, 0x03, 0x00, + 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x39, 0x8E, 0xF3, 0xFF, 0xFB, 0xE7, 0xF8, + 0xE7, 0xFE, 0x01, 0x00, 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x3F, 0x8E, 0x7F, + 0xFF, 0xFF, 0xE7, 0x38, 0x7F, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x0F, 0x70, + 0xDC, 0x1F, 0x0E, 0x3F, 0xFF, 0x9D, 0xF7, 0x38, 0x3F, 0xEF, 0x01, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}; diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Sender/OLED_LoRa_Sender.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Sender/OLED_LoRa_Sender.ino new file mode 100644 index 0000000..df2e745 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Sender/OLED_LoRa_Sender.ino @@ -0,0 +1,85 @@ +/* + This is a simple example show the Heltec.LoRa sended data in OLED. + + The onboard OLED display is SSD1306 driver and I2C interface. In order to make the + OLED correctly operation, you should output a high-low-high(1-0-1) signal by soft- + ware to OLED's reset pin, the low-level signal at least 5ms. + + OLED pins to ESP32 GPIOs via this connecthin: + OLED_SDA -- GPIO4 + OLED_SCL -- GPIO15 + OLED_RST -- GPIO16 + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + https://heltec.org + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ + +#include "heltec.h" +#include "images.h" + +#define BAND 868E6 //you can set band here directly,e.g. 868E6,915E6 + +unsigned int counter = 0; +String rssi = "RSSI --"; +String packSize = "--"; +String packet ; + +void logo() +{ + Heltec.display->clear(); + Heltec.display->drawXbm(0,5,logo_width,logo_height,logo_bits); + Heltec.display->display(); +} + +void setup() +{ + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + Heltec.display->init(); + Heltec.display->flipScreenVertically(); + Heltec.display->setFont(ArialMT_Plain_10); + logo(); + delay(1500); + Heltec.display->clear(); + + Heltec.display->drawString(0, 0, "Heltec.LoRa Initial success!"); + Heltec.display->display(); + delay(1000); +} + +void loop() +{ + Heltec.display->clear(); + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->setFont(ArialMT_Plain_10); + + Heltec.display->drawString(0, 0, "Sending packet: "); + Heltec.display->drawString(90, 0, String(counter)); + Heltec.display->display(); + + // send packet + LoRa.beginPacket(); + +/* + * LoRa.setTxPower(txPower,RFOUT_pin); + * txPower -- 0 ~ 20 + * RFOUT_pin could be RF_PACONFIG_PASELECT_PABOOST or RF_PACONFIG_PASELECT_RFO + * - RF_PACONFIG_PASELECT_PABOOST -- LoRa single output via PABOOST, maximum output 20dBm + * - RF_PACONFIG_PASELECT_RFO -- LoRa single output via RFO_HF / RFO_LF, maximum output 14dBm +*/ + LoRa.setTxPower(14,RF_PACONFIG_PASELECT_PABOOST); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + counter++; + digitalWrite(LED, HIGH); // turn the LED on (HIGH is the voltage level) + delay(1000); // wait for a second + digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW + delay(1000); // wait for a second +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Sender/images.h b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Sender/images.h new file mode 100644 index 0000000..e014f70 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/OLED_LoRa_Sender/images.h @@ -0,0 +1,101 @@ +#define logo_width 128 +#define logo_height 53 +const unsigned char logo_bits[] = { + + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xF0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x07, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0xF0, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0xF8, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xFC, 0x1F, + 0x80, 0xFF, 0x8F, 0x7F, 0xC0, 0xFF, 0x7F, 0xC0, 0xFF, 0x03, 0xC0, 0x3F, + 0xF0, 0x1F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, 0xC0, 0xFF, 0x7F, 0xF0, + 0xFF, 0x01, 0xF8, 0xFF, 0xF0, 0x0F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, + 0xE0, 0xFF, 0x7F, 0xF8, 0xFF, 0x01, 0xFE, 0xFF, 0xF8, 0x0F, 0xFE, 0x0F, + 0xF0, 0xFF, 0x87, 0x3F, 0xE0, 0xFF, 0x7F, 0xFC, 0xFF, 0x01, 0xFF, 0xFF, + 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC7, 0x3F, 0xE0, 0xFF, 0x3F, 0xFC, + 0xFF, 0x81, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC3, 0x1F, + 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xC0, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0x0F, + 0xF8, 0xFF, 0xC3, 0x1F, 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0xFF, 0x7F, + 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x01, 0xFE, + 0x00, 0xF0, 0x3F, 0x70, 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xE0, 0x0F, + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x1F, 0x60, 0xFC, 0xFF, 0xFF, 0x07, + 0xF8, 0x03, 0xE0, 0x0F, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x07, 0x20, + 0xFE, 0xFF, 0xFF, 0x07, 0xFC, 0xFF, 0xE1, 0x0F, 0x00, 0xFF, 0x00, 0xFF, + 0x7F, 0xF8, 0x07, 0x00, 0xFE, 0x83, 0xFF, 0x03, 0xFC, 0xFF, 0xF1, 0x0F, + 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xF8, 0x03, 0x00, 0xFE, 0x83, 0xFF, 0x03, + 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xFC, 0x03, 0x00, + 0xFE, 0x81, 0xFF, 0x03, 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, + 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0xFF, 0xF0, 0x07, + 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, + 0xFE, 0xFF, 0xF8, 0x07, 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x10, + 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0x00, 0xF8, 0x03, 0x80, 0x3F, 0x80, 0x3F, + 0x00, 0xFC, 0x03, 0x0C, 0xFF, 0xC0, 0xFF, 0x01, 0xFF, 0x00, 0xF8, 0x03, + 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0xFC, 0x07, 0x0E, 0xFF, 0xE0, 0xFF, 0x00, + 0x7F, 0x00, 0xF8, 0x03, 0xC0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, + 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x7F, 0xFC, 0xFF, 0xC1, 0x1F, 0xC0, 0xFF, + 0x1F, 0xFC, 0xFF, 0x0F, 0x7F, 0xE0, 0xFF, 0x00, 0xFF, 0x3F, 0xFC, 0xFF, + 0xC1, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, 0x7E, 0xE0, 0xFF, 0x80, + 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, + 0x7C, 0xF0, 0x7F, 0x80, 0xFF, 0x3F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, + 0x0F, 0xF0, 0xFF, 0x07, 0xF8, 0xF0, 0x7F, 0x80, 0xFF, 0x1F, 0xFE, 0xFF, + 0xE0, 0x0F, 0xE0, 0xFF, 0x0F, 0xE0, 0xFF, 0x07, 0xE0, 0xF0, 0x7F, 0x80, + 0xFF, 0x1F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, 0x07, 0x80, 0xFF, 0x03, + 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x8F, 0xF7, 0xFF, 0x7C, + 0xBC, 0xC7, 0xF3, 0xFF, 0xFC, 0xBC, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, + 0x8F, 0x73, 0xFF, 0xFE, 0xBE, 0xC7, 0xFB, 0xFF, 0xFE, 0xBD, 0x03, 0x00, + 0x00, 0xFC, 0x1F, 0x80, 0x8F, 0x73, 0xFF, 0xEF, 0xFE, 0xE7, 0xFB, 0x77, + 0xEF, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0xDF, 0x7B, 0x9C, 0xE7, + 0xFE, 0xF7, 0xE3, 0x71, 0xE7, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0xC0, + 0xDF, 0x79, 0x9E, 0xE3, 0xFE, 0xF3, 0xE3, 0x78, 0xE7, 0xFF, 0x03, 0x00, + 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x39, 0x8E, 0xF3, 0xFF, 0xFB, 0xE7, 0xF8, + 0xE7, 0xFE, 0x01, 0x00, 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x3F, 0x8E, 0x7F, + 0xFF, 0xFF, 0xE7, 0x38, 0x7F, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x0F, 0x70, + 0xDC, 0x1F, 0x0E, 0x3F, 0xFF, 0x9D, 0xF7, 0x38, 0x3F, 0xEF, 0x01, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +const char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; + +//Added by Sloeber +#pragma once + diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/ReceiveCubeCellData/ReceiveCubeCellData.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/ReceiveCubeCellData/ReceiveCubeCellData.ino new file mode 100644 index 0000000..8e5e5a5 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/ReceiveCubeCellData/ReceiveCubeCellData.ino @@ -0,0 +1,78 @@ +/* + This is a simple example show the Heltec.LoRa recived data in OLED. + + The onboard OLED display is SSD1306 driver and I2C interface. In order to make the + OLED correctly operation, you should output a high-low-high(1-0-1) signal by soft- + ware to OLED's reset pin, the low-level signal at least 5ms. + + OLED pins to ESP32 GPIOs via this connecthin: + OLED_SDA -- GPIO4 + OLED_SCL -- GPIO15 + OLED_RST -- GPIO16 + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + 成都惠利特自动化科技有限公司 + www.heltec.cn + + this project also realess in GitHub: + https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series +*/ +#include "heltec.h" + + +#define BAND 868E6 //you can set band here directly,e.g. 868E6,915E6 +String rssi = "RSSI --"; +String packSize = "--"; +String packet ; + + + +void LoRaData(){ + Heltec.display->clear(); + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->drawString(0 , 15 , "Received "+ packSize + " bytes"); + Heltec.display->drawStringMaxWidth(0 , 26 , 128, packet); + Heltec.display->drawString(0, 0, rssi); + Heltec.display->display(); +} + +void cbk(int packetSize) { + packet =""; + packSize = String(packetSize,DEC); + for (int i = 0; i < packetSize; i++) { packet += (char) LoRa.read(); } + rssi = "RSSI " + String(LoRa.packetRssi(), DEC) ; + LoRaData(); +} + +void setup() { + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + // + LoRa.setSpreadingFactor(8); + // put in standby mode + LoRa.setSignalBandwidth(125E3); + LoRa.setCodingRate4(4); + LoRa.setSyncWord(0x12); //0x34 + LoRa.setPreambleLength(8); + + Heltec.display->init(); + Heltec.display->flipScreenVertically(); + Heltec.display->setFont(ArialMT_Plain_10); + + delay(1500); + Heltec.display->clear(); + + Heltec.display->drawString(0, 0, "Heltec.LoRa Initial success!"); + Heltec.display->drawString(0, 10, "Wait for incoming data..."); + Heltec.display->display(); + delay(1000); + //LoRa.onReceive(cbk); + LoRa.receive(); +} + +void loop() { + int packetSize = LoRa.parsePacket(); + if (packetSize) { cbk(packetSize); } + delay(10); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/SerialsetLoRaFrequency/SerialsetLoRaFrequency.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/SerialsetLoRaFrequency/SerialsetLoRaFrequency.ino new file mode 100644 index 0000000..9ec649d --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/LoRa/SerialsetLoRaFrequency/SerialsetLoRaFrequency.ino @@ -0,0 +1,141 @@ +/* Heltec Automation send communication test example + * + * Function: + * 1. Send data from a CubeCell device over hardware + * 2. LoRa sending set frequency via serial + * + * this example must work with Heltec ESP32 LoRaWAN library: + * https://github.com/HelTecAutomation/ESP32_LoRaWAN + * */ + +#include +#include "Arduino.h" + +uint32_t license[4] = {0x947025A9, 0x7E6C7FE4, 0x943C2CDA, 0xE04CD68B}; + +#define RF_FREQUENCY 868000000 // Hz + +#define TX_OUTPUT_POWER 14 // dBm + +#define LORA_BANDWIDTH 0 // [0: 125 kHz, + // 1: 250 kHz, + // 2: 500 kHz, + // 3: Reserved] +#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] +#define LORA_CODINGRATE 1 // [1: 4/5, + // 2: 4/6, + // 3: 4/7, + // 4: 4/8] +#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx +#define LORA_SYMBOL_TIMEOUT 0 // Symbols +#define LORA_FIX_LENGTH_PAYLOAD_ON false +#define LORA_IQ_INVERSION_ON false + + +#define RX_TIMEOUT_VALUE 1000 +#define BUFFER_SIZE 30 // Define the payload size here + +char txpacket[BUFFER_SIZE]; +char rxpacket[BUFFER_SIZE]; + +static RadioEvents_t RadioEvents; + +double txNumber; + +int16_t rssi,rxSize; +void DoubleToString( char *str, double double_num,unsigned int len); + +void setup() { + Serial.begin(115200); + while (!Serial); + + SPI.begin(SCK,MISO,MOSI,SS); + Mcu.init(SS,RST_LoRa,DIO0,DIO1,license); + txNumber=0; + rssi=0; + + Radio.Init( &RadioEvents ); + Radio.SetChannel( RF_FREQUENCY ); + Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, + LORA_SPREADING_FACTOR, LORA_CODINGRATE, + LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, + true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); + } + + +uint32_t starttime = 0; +uint8_t data[128]; +uint8_t dataIndex = 0; + +uint32_t freq = 868000000; +void loop() +{ + starttime = millis(); + while( (millis() - starttime ) < 6000 ) + { + if(Serial.available()) + { + dataIndex = 0; + while(Serial.available()) + { + data[dataIndex++]=Serial.read(); + if(dataIndex == 128) + { + break; + } + } + + if(dataIndex == 5 && data[3] == '.') + { + if( data[0] <= '9' && data[0] >= '0' && data[1] <= '9' && data[1] >= '0' && data[2] <= '9' && data[2] >= '0' && data[4] <= '9' && data[4] >= '0' ) + { + uint32_t freq = (data[0]-'0')*100*1000000 + (data[1]-'0')*10*1000000+(data[2]-'0')*1000000+(data[4]-'0')*100000; + Serial.print("Freq set to "); +// Serial.println(freq); + Serial.write(data,dataIndex); + Serial.println("MHz"); + Radio.Sleep(); + Radio.SetChannel( freq ); + Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, + LORA_SPREADING_FACTOR, LORA_CODINGRATE, + LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, + true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); + } + else + { + Serial.println("Input Error. Input example : 868.1"); + } + } + else + { + Serial.println("Input Error. Input example : 868.1"); + } + } + } + + txNumber += 0.01; + sprintf(txpacket,"%s","Hello world number"); //start a package +// sprintf(txpacket+strlen(txpacket),"%d",txNumber); //add to the end of package + +// DoubleToString(txpacket,txNumber,3); //add to the end of package + +// Serial.printf("\r\nsending packet \"%s\" , length %d\r\n",txpacket, strlen(txpacket)); + +// Serial.println( Radio.TimeOnAir(MODEM_LORA, strlen(txpacket)) ); + Radio.Send( (uint8_t *)txpacket, strlen(txpacket) ); //send the package out +} + +/** + * @brief Double To String + * @param str: Array or pointer for storing strings + * @param double_num: Number to be converted + * @param len: Fractional length to keep + * @retval None + */ +void DoubleToString( char *str, double double_num,unsigned int len) { + double fractpart, intpart; + fractpart = modf(double_num, &intpart); + fractpart = fractpart * (pow(10,len)); + sprintf(str + strlen(str),"%d", (int)(intpart)); //Integer part + sprintf(str + strlen(str), ".%d", (int)(fractpart)); //Decimal part +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Low_Power/Low_Power.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/Low_Power/Low_Power.ino new file mode 100644 index 0000000..8770e73 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Low_Power/Low_Power.ino @@ -0,0 +1,150 @@ +/* + * HelTec Automation(TM) Low_Power test code, witch includ + * Function summary: + * + * - Vext connected to 3.3V via a MOS-FET, the gate pin connected to GPIO21; + * + * - OLED display and PE4259(RF switch) use Vext as power supply; + * + * - WIFI Kit series V1 don't have Vext control function; + * + * - Basic LoRa Function; + * + * - Esp_Deep_Sleep Function; + * + * by lxyzn from HelTec AutoMation, ChengDu, China + *  + * www.heltec.cn + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + +#include "heltec.h" + + +#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ +#define TIME_TO_SLEEP 10 /* Time ESP32 will go to sleep (in seconds) */ + +#define BAND 433E6 //you can set band here directly,e.g. 868E6,915E6 + + + +RTC_DATA_ATTR int bootCount = 0; + +/* +Method to print the reason by which ESP32 +has been awaken from sleep +*/ +void print_wakeup_reason(){ + esp_sleep_wakeup_cause_t wakeup_reason; + + wakeup_reason = esp_sleep_get_wakeup_cause(); + + switch(wakeup_reason) + { + case 1 : + { + Serial.println("Wakeup caused by external signal using RTC_IO"); + delay(2); + } break; + case 2 : + { + Serial.println("Wakeup caused by external signal using RTC_CNTL"); + delay(2); + } break; + case 3 : + { + Serial.println("Wakeup caused by timer"); + delay(2); + } break; + case 4 : + { + Serial.println("Wakeup caused by touchpad"); + delay(2); + } break; + case 5 : + { + Serial.println("Wakeup caused by ULP program"); + delay(2); + } break; + default : + { + Serial.println("Wakeup was not caused by deep sleep"); + delay(2); + } break; + } +} + +void setup(){ + //WIFI Kit series V1 not support Vext control + Heltec.begin(true /*DisplayEnable Enable*/, true /*LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); + + //Increment boot number and print it every reboot + ++bootCount; + Serial.println("Boot number: " + String(bootCount)); + delay(2); + + + + + //Print the wakeup reason for ESP32 + print_wakeup_reason(); + + /* + First we configure the wake up source + We set our ESP32 to wake up every 5 seconds + */ + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + + " Seconds"); + delay(10); + + /* + Next we decide what all peripherals to shut down/keep on + By default, ESP32 will automatically power down the peripherals + not needed by the wakeup source, but if you want to be a poweruser + this is for you. Read in detail at the API docs + http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html + Left the line commented as an example of how to configure peripherals. + The line below turns off all RTC peripherals in deep sleep. + */ + //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); + //Serial.println("Configured all RTC Peripherals to be powered down in sleep"); + + /* + Now that we have setup a wake cause and if needed setup the + peripherals state in deep sleep, we can now start going to + deep sleep. + In the case that no wake up sources were provided but deep + sleep was started, it will sleep forever unless hardware + reset occurs. + */ + LoRa.end(); + LoRa.sleep(); + delay(100); + + + pinMode(5,INPUT); + + pinMode(14,INPUT); + pinMode(15,INPUT); + pinMode(16,INPUT); + pinMode(17,INPUT); +pinMode(18,INPUT); + pinMode(19,INPUT); + + pinMode(26,INPUT); + pinMode(27,INPUT); + + + delay(100); + Serial.println("Going to sleep now"); + delay(2); + esp_deep_sleep_start(); + Serial.println("This will never be printed"); +} + +void loop(){ + //This is not going to be called +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/OTA_OLED/OTA_OLED.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/OTA_OLED/OTA_OLED.ino new file mode 100644 index 0000000..b7a14cb --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/OTA_OLED/OTA_OLED.ino @@ -0,0 +1,289 @@ +/* + * HelTec Automation(TM) WIFI_LoRa_32 factory test code, witch includ + * follow functions: + * + * - Basic OLED function test; + * + * - Basic serial port test(in baud rate 115200); + * + * - Basic LED test; + * + * - WIFI join and scan test; + * + * - ArduinoOTA By Wifi; + * + * - Timer test and some other Arduino basic functions. + * + * by lxyzn from HelTec AutoMation, ChengDu, China + * 成都惠利特自动化科技有限公司 + * www.heltec.cn + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + + +#include +#include +#include +#include "heltec.h" + + +/********************************************** WIFI Client 注意编译时要设置此值 ********************************* + * wifi client + */ +const char* ssid = "xxxxxx"; //replace "xxxxxx" with your WIFI's ssid +const char* password = "xxxxxx"; //replace "xxxxxx" with your WIFI's password + +//WiFi&OTA 参数 +//#define HOSTNAME "HelTec_OTA_OLED" +#define PASSWORD "HT.123456" //the password for OTA upgrade, can set it in any char you want + +/************************************************ 注意编译时要设置此值 ********************************* + * 是否使用静态IP + */ +#define USE_STATIC_IP false +#if USE_STATIC_IP + IPAddress staticIP(192,168,1,22); + IPAddress gateway(192,168,1,9); + IPAddress subnet(255,255,255,0); + IPAddress dns1(8, 8, 8, 8); + IPAddress dns2(114,114,114,114); +#endif + +/******************************************************************* + * OLED Arguments + */ +//#define RST_OLED 16 //OLED Reset引脚,需要手动Reset,否则不显示 +#define OLED_UPDATE_INTERVAL 500 //OLED屏幕刷新间隔ms +//SSD1306 display(0x3C, 4, 15); //引脚4,15是绑定在Kit 32的主板上的,不能做其它用 + + +/******************************************************************** + * OTA升级配置 + */ +void setupOTA() +{ + //Port defaults to 8266 + //ArduinoOTA.setPort(8266); + + //Hostname defaults to esp8266-[ChipID] +// ArduinoOTA.setHostname(HOSTNAME); + + //No authentication by default + ArduinoOTA.setPassword(PASSWORD); + + //Password can be set with it's md5 value as well + //MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 + //ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); + + ArduinoOTA.onStart([]() + { + Heltec.display->clear(); + Heltec.display->setFont(ArialMT_Plain_10); //设置字体大小 + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);//设置字体对齐方式 + Heltec.display->drawString(0, 0, "Start Updating...."); + + Serial.printf("Start Updating....Type:%s\n", (ArduinoOTA.getCommand() == U_FLASH) ? "sketch" : "filesystem"); + }); + + ArduinoOTA.onEnd([]() + { + Heltec.display->clear(); + Heltec.display->drawString(0, 0, "Update Complete!"); + Serial.println("Update Complete!"); + + ESP.restart(); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) + { + String pro = String(progress / (total / 100)) + "%"; + int progressbar = (progress / (total / 100)); + //int progressbar = (progress / 5) % 100; + //int pro = progress / (total / 100); + + Heltec.display->clear(); +#if defined (Wireless_Stick) + Heltec.display->drawProgressBar(0, 11, 64, 8, progressbar); // draw the progress bar + Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER); // draw the percentage as String + Heltec.display->drawString(10, 20, pro); +#else + Heltec.display->drawProgressBar(0, 32, 120, 10, progressbar); // draw the progress bar + Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER); // draw the percentage as String + Heltec.display->drawString(64, 15, pro); +#endif + Heltec.display->display(); + + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + + ArduinoOTA.onError([](ota_error_t error) + { + Serial.printf("Error[%u]: ", error); + String info = "Error Info:"; + switch(error) + { + case OTA_AUTH_ERROR: + info += "Auth Failed"; + Serial.println("Auth Failed"); + break; + + case OTA_BEGIN_ERROR: + info += "Begin Failed"; + Serial.println("Begin Failed"); + break; + + case OTA_CONNECT_ERROR: + info += "Connect Failed"; + Serial.println("Connect Failed"); + break; + + case OTA_RECEIVE_ERROR: + info += "Receive Failed"; + Serial.println("Receive Failed"); + break; + + case OTA_END_ERROR: + info += "End Failed"; + Serial.println("End Failed"); + break; + } + + Heltec.display->clear(); + Heltec.display->drawString(0, 0, info); + ESP.restart(); + }); + + ArduinoOTA.begin(); +} + +/********************************************************************* + * setup wifi + */ +void setupWIFI() +{ + Heltec.display->clear(); + Heltec.display->drawString(0, 0, "Connecting..."); + Heltec.display->drawString(0, 10, String(ssid)); + Heltec.display->display(); + + //连接WiFi,删除旧的配置,关闭WIFI,准备重新配置 + WiFi.disconnect(true); + delay(1000); + + WiFi.mode(WIFI_STA); + //WiFi.onEvent(WiFiEvent); + WiFi.setAutoConnect(true); + WiFi.setAutoReconnect(true); //断开WiFi后自动重新连接,ESP32不可用 + //WiFi.setHostname(HOSTNAME); + WiFi.begin(ssid, password); +#if USE_STATIC_IP + WiFi.config(staticIP, gateway, subnet); +#endif + + //等待5000ms,如果没有连接上,就继续往下 + //不然基本功能不可用 + byte count = 0; + while(WiFi.status() != WL_CONNECTED && count < 10) + { + count ++; + delay(500); + Serial.print("."); + } + + Heltec.display->clear(); + if(WiFi.status() == WL_CONNECTED) + Heltec.display->drawString(0, 0, "Connected"); + else + Heltec.display->drawString(0, 0, "Connect False"); + Heltec.display->display(); +} + +/****************************************************** + * arduino setup + */ +void setup() +{ + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/); + pinMode(25, OUTPUT); + digitalWrite(25,HIGH); + + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + Serial.println("Initialize..."); + + setupWIFI(); + setupOTA(); +} + +/****************************************************** + * arduino loop + */ +void loop() +{ + ArduinoOTA.handle(); + unsigned long ms = millis(); + if(ms % 1000 == 0) + { + Serial.println("hello,OTA now"); + } +} + +/**************************************************** + * [通用函数]ESP32 WiFi Kit 32事件处理 + */ +void WiFiEvent(WiFiEvent_t event) +{ + Serial.printf("[WiFi-event] event: %d\n", event); + switch(event) + { + case SYSTEM_EVENT_WIFI_READY: /**< ESP32 WiFi ready */ + break; + case SYSTEM_EVENT_SCAN_DONE: /**< ESP32 finish scanning AP */ + break; + + case SYSTEM_EVENT_STA_START: /**< ESP32 station start */ + break; + case SYSTEM_EVENT_STA_STOP: /**< ESP32 station stop */ + break; + + case SYSTEM_EVENT_STA_CONNECTED: /**< ESP32 station connected to AP */ + break; + + case SYSTEM_EVENT_STA_DISCONNECTED: /**< ESP32 station disconnected from AP */ + break; + + case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: /**< the auth mode of AP connected by ESP32 station changed */ + break; + + case SYSTEM_EVENT_STA_GOT_IP: /**< ESP32 station got IP from connected AP */ + case SYSTEM_EVENT_STA_LOST_IP: /**< ESP32 station lost IP and the IP is reset to 0 */ + break; + + case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: /**< ESP32 station wps succeeds in enrollee mode */ + case SYSTEM_EVENT_STA_WPS_ER_FAILED: /**< ESP32 station wps fails in enrollee mode */ + case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: /**< ESP32 station wps timeout in enrollee mode */ + case SYSTEM_EVENT_STA_WPS_ER_PIN: /**< ESP32 station wps pin code in enrollee mode */ + break; + + case SYSTEM_EVENT_AP_START: /**< ESP32 soft-AP start */ + case SYSTEM_EVENT_AP_STOP: /**< ESP32 soft-AP stop */ + case SYSTEM_EVENT_AP_STACONNECTED: /**< a station connected to ESP32 soft-AP */ + case SYSTEM_EVENT_AP_STADISCONNECTED: /**< a station disconnected from ESP32 soft-AP */ + case SYSTEM_EVENT_AP_PROBEREQRECVED: /**< Receive probe request packet in soft-AP interface */ + case SYSTEM_EVENT_AP_STA_GOT_IP6: /**< ESP32 station or ap interface v6IP addr is preferred */ + case SYSTEM_EVENT_AP_STAIPASSIGNED: + break; + + case SYSTEM_EVENT_ETH_START: /**< ESP32 ethernet start */ + case SYSTEM_EVENT_ETH_STOP: /**< ESP32 ethernet stop */ + case SYSTEM_EVENT_ETH_CONNECTED: /**< ESP32 ethernet phy link up */ + case SYSTEM_EVENT_ETH_DISCONNECTED: /**< ESP32 ethernet phy link down */ + case SYSTEM_EVENT_ETH_GOT_IP: /**< ESP32 ethernet got IP from connected AP */ + case SYSTEM_EVENT_MAX: + break; + } +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306DrawingDemo/SSD1306DrawingDemo.ino new file mode 100644 index 0000000..e3c64e4 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306DrawingDemo/SSD1306DrawingDemo.ino @@ -0,0 +1,185 @@ +/* + * HelTec Automation(TM) ESP32 Series Dev boards OLED Drawing Function test code + * + * - Some OLED Drawing Function function test; + * + * by lxyzn from HelTec AutoMation, ChengDu, China + * 成都惠利特自动化科技有限公司 + * https://heltec.org + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + + +// This example just provide basic function test; +// For more informations, please vist www.heltec.cn or mail to support@heltec.cn + +#include "Arduino.h" +#include "heltec.h" + + +// Adapted from Adafruit_SSD1306 +void drawLines() { + for (int16_t i=0; idrawLine(0, 0, i, DISPLAY_HEIGHT-1); + Heltec.display->display(); + delay(10); + } + for (int16_t i=0; idrawLine(0, 0, DISPLAY_WIDTH-1, i); + Heltec.display->display(); + delay(10); + } + delay(250); + + Heltec.display->clear(); + for (int16_t i=0; idrawLine(0, DISPLAY_HEIGHT-1, i, 0); + Heltec.display->display(); + delay(10); + } + for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) { + Heltec.display->drawLine(0, DISPLAY_HEIGHT-1, DISPLAY_WIDTH-1, i); + Heltec.display->display(); + delay(10); + } + delay(250); + + Heltec.display->clear(); + for (int16_t i=DISPLAY_WIDTH-1; i>=0; i-=4) { + Heltec.display->drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, i, 0); + Heltec.display->display(); + delay(10); + } + for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) { + Heltec.display->drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0, i); + Heltec.display->display(); + delay(10); + } + delay(250); + Heltec.display->clear(); + for (int16_t i=0; idrawLine(DISPLAY_WIDTH-1, 0, 0, i); + Heltec.display->display(); + delay(10); + } + for (int16_t i=0; idrawLine(DISPLAY_WIDTH-1, 0, i, DISPLAY_HEIGHT-1); + Heltec.display->display(); + delay(10); + } + delay(250); +} + +// Adapted from Adafruit_SSD1306 +void drawRect(void) { + for (int16_t i=0; idrawRect(i, i, DISPLAY_WIDTH-2*i, DISPLAY_HEIGHT-2*i); + Heltec.display->display(); + delay(10); + } +} + +// Adapted from Adafruit_SSD1306 +void fillRect(void) { + uint8_t color = 1; + for (int16_t i=0; isetColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors + Heltec.display->fillRect(i, i, DISPLAY_WIDTH - i*2, DISPLAY_HEIGHT - i*2); + Heltec.display->display(); + delay(10); + color++; + } + // Reset back to WHITE + Heltec.display->setColor(WHITE); +} + +// Adapted from Adafruit_SSD1306 +void drawCircle(void) { + for (int16_t i=0; idrawCircle(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, i); + Heltec.display->display(); + delay(10); + } + delay(1000); + Heltec.display->clear(); + + // This will draw the part of the circel in quadrant 1 + // Quadrants are numberd like this: + // 0010 | 0001 + // ------|----- + // 0100 | 1000 + // + Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000001); + Heltec.display->display(); + delay(200); + Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000011); + Heltec.display->display(); + delay(200); + Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000111); + Heltec.display->display(); + delay(200); + Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00001111); + Heltec.display->display(); +} + +void printBuffer(void) { + // Initialize the log buffer + // allocate memory to store 8 lines of text and 30 chars per line. + Heltec.display->setLogBuffer(5, 30); + + // Some test data + const char* test[] = { + "Hello", + "World" , + "----", + "Show off", + "how", + "the log buffer", + "is", + "working.", + "Even", + "scrolling is", + "working" + }; + + for (uint8_t i = 0; i < 11; i++) { + Heltec.display->clear(); + // Print to the screen + Heltec.display->println(test[i]); + // Draw it to the internal screen buffer + Heltec.display->drawLogBuffer(0, 0); + // Display it on the screen + Heltec.display->display(); + delay(500); + } +} + +void setup() { + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/); + + Heltec.display->setContrast(255); + + drawLines(); + delay(1000); + Heltec.display->clear(); + + drawRect(); + delay(1000); + Heltec.display->clear(); + + fillRect(); + delay(1000); + Heltec.display->clear(); + + drawCircle(); + delay(1000); + Heltec.display->clear(); + + printBuffer(); + delay(1000); + Heltec.display->clear(); +} + +void loop() { } \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306SimpleDemo/SSD1306SimpleDemo.ino new file mode 100644 index 0000000..151fc5e --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306SimpleDemo/SSD1306SimpleDemo.ino @@ -0,0 +1,142 @@ +/* + * HelTec Automation(TM) ESP32 Series Dev boards OLED draw Simple Function test code + * + * - Some OLED draw Simple Function function test; + * + * by LXYZN from HelTec AutoMation, ChengDu, China + *  + * www.heltec.cn + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + + +// This example just provide basic function test; +// For more informations, please vist www.heltec.cn or mail to support@heltec.cn + +#include "Arduino.h" +#include "heltec.h" +#include "images.h" + + + +#define DEMO_DURATION 3000 +typedef void (*Demo)(void); + +int demoMode = 0; +int counter = 1; + +void setup() { + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/); + + + + Heltec.display->flipScreenVertically(); + Heltec.display->setFont(ArialMT_Plain_10); + +} + +void drawFontFaceDemo() { + // Font Demo1 + // create more fonts at http://oleddisplay.squix.ch/ + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->drawString(0, 0, "Hello world"); + Heltec.display->setFont(ArialMT_Plain_16); + Heltec.display->drawString(0, 10, "Hello world"); + Heltec.display->setFont(ArialMT_Plain_24); + Heltec.display->drawString(0, 26, "Hello world"); +} + +void drawTextFlowDemo() { + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->drawStringMaxWidth(0, 0, 128, + "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." ); +} + +void drawTextAlignmentDemo() { + // Text alignment demo + Heltec.display->setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->drawString(0, 10, "Left aligned (0,10)"); + + // The coordinates define the center of the text + Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER); + Heltec.display->drawString(64, 22, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + Heltec.display->setTextAlignment(TEXT_ALIGN_RIGHT); + Heltec.display->drawString(128, 33, "Right aligned (128,33)"); +} + +void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + Heltec.display->setPixel(i, i); + Heltec.display->setPixel(10 - i, i); + } + Heltec.display->drawRect(12, 12, 20, 20); + + // Fill the rectangle + Heltec.display->fillRect(14, 14, 17, 17); + + // Draw a line horizontally + Heltec.display->drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + Heltec.display->drawVerticalLine(40, 0, 20); +} + +void drawCircleDemo() { + for (int i=1; i < 8; i++) { + Heltec.display->setColor(WHITE); + Heltec.display->drawCircle(32, 32, i*3); + if (i % 2 == 0) { + Heltec.display->setColor(BLACK); + } + Heltec.display->fillCircle(96, 32, 32 - i* 3); + } +} + +void drawProgressBarDemo() { + int progress = (counter / 5) % 100; + // draw the progress bar + Heltec.display->drawProgressBar(0, 32, 120, 10, progress); + + // draw the percentage as String + Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER); + Heltec.display->drawString(64, 15, String(progress) + "%"); +} + +void drawImageDemo() { + // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html + // on how to create xbm files + Heltec.display->drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo}; +int demoLength = (sizeof(demos) / sizeof(Demo)); +long timeSinceLastModeSwitch = 0; + +void loop() { + // clear the display + Heltec.display->clear(); + // draw the current demo method + demos[demoMode](); + + Heltec.display->setTextAlignment(TEXT_ALIGN_RIGHT); + Heltec.display->drawString(10, 128, String(millis())); + // write the buffer to the display + Heltec.display->display(); + + if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) { + demoMode = (demoMode + 1) % demoLength; + timeSinceLastModeSwitch = millis(); + } + counter++; + delay(10); +} \ No newline at end of file diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306SimpleDemo/images.h b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306SimpleDemo/images.h new file mode 100644 index 0000000..e6036b6 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306SimpleDemo/images.h @@ -0,0 +1,32 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const unsigned char WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +//Added by Sloeber +#pragma once + diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306UiDemo/SSD1306UiDemo.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306UiDemo/SSD1306UiDemo.ino new file mode 100644 index 0000000..2f6582e --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306UiDemo/SSD1306UiDemo.ino @@ -0,0 +1,101 @@ +/* + * HelTec Automation(TM) ESP32 Series Dev boards OLED draw UI test code + * + * - Some OLED draw UI function test; + * + * by Aaron.Lee from HelTec AutoMation, ChengDu, China + * 成都惠利特自动化科技有限公司 + * www.heltec.cn + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + +#include "Arduino.h" +#include "heltec.h" +#include "oled/OLEDDisplayUi.h" +#include "images.h" + + +#define DEMO_DURATION 3000 +typedef void (*Demo)(void); + +extern Heltec_ESP32 Heltec; +OLEDDisplayUi ui( Heltec.display ); + +void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->setFont(ArialMT_Plain_10); + display->drawString(128, 0, String(millis())); +} + +void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->drawXbm(x, y, BT_width, BT_height, BT_bits); + display->drawXbm(x + 12 + 1, y, WIFI_width, WIFI_height, WIFI_bits); + display->drawXbm(x + 108, y, BAT_width, BAT_height, BAT_bits); + display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->drawXbm(x, y, BT_width, BT_height, BT_bits); + display->drawXbm(x + 12 + 1, y, WIFI_width, WIFI_height, WIFI_bits); + display->drawXbm(x + 108, y, BAT_width, BAT_height, BAT_bits); + display->drawXbm(x + 34, y + 12, LoRa_Logo_width, LoRa_Logo_height, LoRa_Logo_bits); +} + +void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->drawXbm(x, y + 5, HelTec_LOGO_width, HelTec_LOGO_height, HelTec_LOGO_bits); +} + +void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_16); + display->drawString(x, y, "HelTec"); + display->setFont(ArialMT_Plain_10); + display->drawString(x, y + 25, "HelTec AutoMation"); + display->drawString(x, y + 35, "www.heltec.cn"); +} + +FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4 }; + +int frameCount = 4; + +void setup() { + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/); + + ui.setTargetFPS(30); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(BOTTOM); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Initialising the UI will init the display too. + ui.init(); + + Heltec.display->flipScreenVertically(); +} + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } + } diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306UiDemo/images.h b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306UiDemo/images.h new file mode 100644 index 0000000..aa661eb --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/OLED/SSD1306UiDemo/images.h @@ -0,0 +1,194 @@ +#define LoRa_Logo_width 59 +#define LoRa_Logo_height 39 + +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 + +#define HelTec_LOGO_width 128 +#define HelTec_LOGO_height 53 + +#define BT_width 8 +#define BT_height 10 + +#define BAT_width 20 +#define BAT_height 9 + +#define WIFI_width 14 +#define WIFI_height 8 + +const unsigned char LoRa_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xFF, 0xF1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xFF, 0xC1, 0xFF, 0x07, 0x00, 0x00, + 0x1F, 0xC0, 0xFF, 0xE7, 0xFF, 0x0F, 0x00, 0x00, 0x1F, 0xC0, 0x83, 0xE7, + 0xFF, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x70, 0x00, + 0x1F, 0x00, 0xFE, 0xE0, 0x03, 0x1F, 0xFE, 0x01, 0x1F, 0x00, 0xFF, 0xE1, + 0x03, 0x1F, 0xFF, 0x03, 0x1F, 0x80, 0xFF, 0xE3, 0x03, 0x9F, 0xFF, 0x07, + 0x1F, 0xC0, 0xFF, 0xE7, 0x03, 0x9F, 0xFF, 0x07, 0x1F, 0xC0, 0xC7, 0xE7, + 0xFF, 0x9F, 0x8F, 0x07, 0x1F, 0xC0, 0x83, 0xE7, 0xFF, 0x0F, 0xF0, 0x07, + 0x1F, 0xC0, 0x83, 0xE7, 0xFF, 0x07, 0xFF, 0x07, 0x1F, 0xC0, 0x83, 0xE7, + 0xFF, 0x83, 0xFF, 0x07, 0x1F, 0xC0, 0x83, 0xE7, 0xE3, 0x87, 0x9F, 0x07, + 0x1F, 0xC0, 0x83, 0xE7, 0xE3, 0xC7, 0x87, 0x07, 0xFF, 0xCF, 0xC7, 0xE7, + 0xC3, 0xCF, 0xC7, 0x07, 0xFF, 0xCF, 0xFF, 0xE7, 0xC3, 0xDF, 0xFF, 0x07, + 0xFF, 0x8F, 0xFF, 0xE3, 0x83, 0x9F, 0xFF, 0x07, 0xFF, 0x0F, 0xFF, 0xE1, + 0x03, 0x1F, 0xFF, 0x07, 0xFF, 0x0F, 0xFE, 0xE0, 0x03, 0x1F, 0xBE, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x83, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xFF, 0xF1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned char WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const unsigned char HelTec_LOGO_bits[] PROGMEM = { + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xF0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x07, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0xF0, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0xF8, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x1F, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0xFC, 0x1F, + 0x80, 0xFF, 0x8F, 0x7F, 0xC0, 0xFF, 0x7F, 0xC0, 0xFF, 0x03, 0xC0, 0x3F, + 0xF0, 0x1F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, 0xC0, 0xFF, 0x7F, 0xF0, + 0xFF, 0x01, 0xF8, 0xFF, 0xF0, 0x0F, 0xFC, 0x1F, 0xE0, 0xFF, 0x87, 0x3F, + 0xE0, 0xFF, 0x7F, 0xF8, 0xFF, 0x01, 0xFE, 0xFF, 0xF8, 0x0F, 0xFE, 0x0F, + 0xF0, 0xFF, 0x87, 0x3F, 0xE0, 0xFF, 0x7F, 0xFC, 0xFF, 0x01, 0xFF, 0xFF, + 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC7, 0x3F, 0xE0, 0xFF, 0x3F, 0xFC, + 0xFF, 0x81, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xC3, 0x1F, + 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xC0, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, 0x0F, + 0xF8, 0xFF, 0xC3, 0x1F, 0xE0, 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0xFF, 0x7F, + 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xC0, 0x1F, 0x00, 0xFE, 0x01, 0xFE, + 0x00, 0xF0, 0x3F, 0x70, 0xFC, 0xFF, 0xFF, 0x07, 0xF8, 0x03, 0xE0, 0x0F, + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x1F, 0x60, 0xFC, 0xFF, 0xFF, 0x07, + 0xF8, 0x03, 0xE0, 0x0F, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xF0, 0x07, 0x20, + 0xFE, 0xFF, 0xFF, 0x07, 0xFC, 0xFF, 0xE1, 0x0F, 0x00, 0xFF, 0x00, 0xFF, + 0x7F, 0xF8, 0x07, 0x00, 0xFE, 0x83, 0xFF, 0x03, 0xFC, 0xFF, 0xF1, 0x0F, + 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xF8, 0x03, 0x00, 0xFE, 0x83, 0xFF, 0x03, + 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, 0x7F, 0xFC, 0x03, 0x00, + 0xFE, 0x81, 0xFF, 0x03, 0xFC, 0xFF, 0xF0, 0x07, 0x00, 0x7F, 0x00, 0xFF, + 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0xFF, 0xF0, 0x07, + 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x00, 0xFF, 0xC1, 0xFF, 0x01, + 0xFE, 0xFF, 0xF8, 0x07, 0x80, 0x3F, 0x80, 0xFF, 0x3F, 0xFC, 0x03, 0x10, + 0xFF, 0xC1, 0xFF, 0x01, 0xFE, 0x00, 0xF8, 0x03, 0x80, 0x3F, 0x80, 0x3F, + 0x00, 0xFC, 0x03, 0x0C, 0xFF, 0xC0, 0xFF, 0x01, 0xFF, 0x00, 0xF8, 0x03, + 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0xFC, 0x07, 0x0E, 0xFF, 0xE0, 0xFF, 0x00, + 0x7F, 0x00, 0xF8, 0x03, 0xC0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, + 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x7F, 0xFC, 0xFF, 0xC1, 0x1F, 0xC0, 0xFF, + 0x1F, 0xFC, 0xFF, 0x0F, 0x7F, 0xE0, 0xFF, 0x00, 0xFF, 0x3F, 0xFC, 0xFF, + 0xC1, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, 0x7E, 0xE0, 0xFF, 0x80, + 0xFF, 0x3F, 0xFC, 0xFF, 0xE0, 0x1F, 0xC0, 0xFF, 0x0F, 0xF8, 0xFF, 0x07, + 0x7C, 0xF0, 0x7F, 0x80, 0xFF, 0x3F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, + 0x0F, 0xF0, 0xFF, 0x07, 0xF8, 0xF0, 0x7F, 0x80, 0xFF, 0x1F, 0xFE, 0xFF, + 0xE0, 0x0F, 0xE0, 0xFF, 0x0F, 0xE0, 0xFF, 0x07, 0xE0, 0xF0, 0x7F, 0x80, + 0xFF, 0x1F, 0xFE, 0xFF, 0xE0, 0x0F, 0xE0, 0xFF, 0x07, 0x80, 0xFF, 0x03, + 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x8F, 0xF7, 0xFF, 0x7C, + 0xBC, 0xC7, 0xF3, 0xFF, 0xFC, 0xBC, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, + 0x8F, 0x73, 0xFF, 0xFE, 0xBE, 0xC7, 0xFB, 0xFF, 0xFE, 0xBD, 0x03, 0x00, + 0x00, 0xFC, 0x1F, 0x80, 0x8F, 0x73, 0xFF, 0xEF, 0xFE, 0xE7, 0xFB, 0x77, + 0xEF, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0xDF, 0x7B, 0x9C, 0xE7, + 0xFE, 0xF7, 0xE3, 0x71, 0xE7, 0xFD, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0xC0, + 0xDF, 0x79, 0x9E, 0xE3, 0xFE, 0xF3, 0xE3, 0x78, 0xE7, 0xFF, 0x03, 0x00, + 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x39, 0x8E, 0xF3, 0xFF, 0xFB, 0xE7, 0xF8, + 0xE7, 0xFE, 0x01, 0x00, 0x00, 0xFC, 0x0F, 0xE0, 0xDF, 0x3F, 0x8E, 0x7F, + 0xFF, 0xFF, 0xE7, 0x38, 0x7F, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x0F, 0x70, + 0xDC, 0x1F, 0x0E, 0x3F, 0xFF, 0x9D, 0xF7, 0x38, 0x3F, 0xEF, 0x01, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const unsigned char BT_bits[] PROGMEM = { + 0x18, 0x28, 0x4A, 0x2C, 0x18, 0x2C, 0x4A, 0x28, 0x18, 0x00, + }; + +const unsigned char BAT_bits[] PROGMEM = { + 0xFC, 0xFF, 0x0F, 0x04, 0x00, 0x08, 0xF7, 0xDE, 0x0B, 0xF1, 0xDE, 0x0B, + 0xF1, 0xDE, 0x0B, 0xF1, 0xDE, 0x0B, 0xF7, 0xDE, 0x0B, 0x04, 0x00, 0x08, + 0xFC, 0xFF, 0x0F, + }; + +const unsigned char WIFI_bits[] PROGMEM = { + 0xF0, 0x03, 0x04, 0x08, 0xF2, 0x13, 0x09, 0x24, 0xE4, 0x09, 0x10, 0x02, + 0xC0, 0x00, 0xC0, 0x00, + }; + + +//屏幕下方的小圆点 +const unsigned char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const unsigned char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; + +//Added by Sloeber +#pragma once + + +//Added by Sloeber +#pragma once + diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD.JPG b/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD.JPG new file mode 100644 index 0000000..1839448 Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD.JPG differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD_Time/SD_Time.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD_Time/SD_Time.ino new file mode 100644 index 0000000..5d9deac --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD_Time/SD_Time.ino @@ -0,0 +1,220 @@ +/* + This is a simple example to test Micro TF card via SPI 1. + Debug log output via UART. + + Micro TF card pins to ESP32 GPIOs via this connecthin: + SD_CS -- GPIO22 + SD_MOSI -- GPIO23 + SD_SCK -- GPIO17 + SD_MISO -- GPIO13 + + by Aaron.Lee from HelTec AutoMation, ChengDu, China + ɶԶƼ޹˾ + https://heltec.org + + this project also realess in GitHub: + https://github.com/HelTecAutomation/Heltec_ESP32 +*/ + +#include "FS.h" +#include "SD.h" +#include "SPI.h" +#include +#include + +SPIClass spi1; + +const char* ssid = "your_ssid"; +const char* password = "your_password"; + +long timezone = 1; +byte daysavetime = 1; + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if(!root){ + Serial.println("Failed to open directory"); + return; + } + if(!root.isDirectory()){ + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + Serial.print(" DIR : "); + Serial.print (file.name()); + time_t t= file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.print(file.size()); + time_t t= file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } +} + +void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } +} + +void readFile(fs::FS &fs, const char * path){ + Serial.printf("Reading file: %s\n", path); + + File file = fs.open(path); + if(!file){ + Serial.println("Failed to open file for reading"); + return; + } + + Serial.print("Read from file: "); + while(file.available()){ + Serial.write(file.read()); + } + file.close(); +} + +void writeFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Writing file: %s\n", path); + + File file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + if(file.print(message)){ + Serial.println("File written"); + } else { + Serial.println("Write failed"); + } + file.close(); +} + +void appendFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Appending to file: %s\n", path); + + File file = fs.open(path, FILE_APPEND); + if(!file){ + Serial.println("Failed to open file for appending"); + return; + } + if(file.print(message)){ + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } + file.close(); +} + +void renameFile(fs::FS &fs, const char * path1, const char * path2){ + Serial.printf("Renaming file %s to %s\n", path1, path2); + if (fs.rename(path1, path2)) { + Serial.println("File renamed"); + } else { + Serial.println("Rename failed"); + } +} + +void deleteFile(fs::FS &fs, const char * path){ + Serial.printf("Deleting file: %s\n", path); + if(fs.remove(path)){ + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } +} + +void setup(){ + Serial.begin(115200); + // We start by connecting to a WiFi network + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println("Contacting Time Server"); + configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); + struct tm tmstruct ; + delay(2000); + tmstruct.tm_year = 0; + getLocalTime(&tmstruct, 5000); + Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct.tm_year)+1900,( tmstruct.tm_mon)+1, tmstruct.tm_mday,tmstruct.tm_hour , tmstruct.tm_min, tmstruct.tm_sec); + Serial.println(""); + + SPIClass(1); + spi1.begin(17, 13, 23, 22); + + if(!SD.begin(22, spi1)){ + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD.cardType(); + + if(cardType == CARD_NONE){ + Serial.println("No SD card attached"); + return; + } + + Serial.print("SD Card Type: "); + if(cardType == CARD_MMC){ + Serial.println("MMC"); + } else if(cardType == CARD_SD){ + Serial.println("SDSC"); + } else if(cardType == CARD_SDHC){ + Serial.println("SDHC"); + } else { + Serial.println("UNKNOWN"); + } + + uint64_t cardSize = SD.cardSize() / (1024 * 1024); + Serial.printf("SD Card Size: %lluMB\n", cardSize); + + listDir(SD, "/", 0); + removeDir(SD, "/mydir"); + createDir(SD, "/mydir"); + deleteFile(SD, "/hello.txt"); + writeFile(SD, "/hello.txt", "Hello "); + appendFile(SD, "/hello.txt", "World!\n"); + listDir(SD, "/", 0); +} + +void loop(){ + +} + diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD_Time/TestInfo.png b/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD_Time/TestInfo.png new file mode 100644 index 0000000..e3fb852 Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/examples/SD/SD_Time/TestInfo.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/Sensor/BMP180basic/BMP180basic.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/Sensor/BMP180basic/BMP180basic.ino new file mode 100644 index 0000000..71d1570 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/Sensor/BMP180basic/BMP180basic.ino @@ -0,0 +1,89 @@ +/* Heltec Automation BMP180 Sensors test example + * + * Function: + * Temperature and Pressure measurement + * + * HelTec AutoMation, Chengdu, China + * www.heltec.org + * + * this project also realess in GitHub: + * https://github.com/HelTecAutomation/ASR650x-Arduino + * +*/ +#include "Arduino.h" +#include "heltec.h" +#include +#include +#include "string.h" + +BMP085 bmp; + +uint8_t T[20] = {"Temperature"}; + +void setup() { + pinMode(Vext,OUTPUT); + digitalWrite(Vext,LOW); + Serial.begin(115200); + +} +void drawFontFaceDemo(double T,double P,double A,double R) { + Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/); +// Heltec.display->flipScreenVertically(); + Heltec.display->clear(); + Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); + Heltec.display->setFont(ArialMT_Plain_10); + Heltec.display->drawString(0 , 0 , "Temperature ="); + Heltec.display->drawString(76 , 0 , (String)T); + Heltec.display->drawString(106 , 0 , " *C"); + Heltec.display->drawString(0 , 16 , "Pressure ="); + Heltec.display->drawString(56 , 16 , (String)P); + Heltec.display->drawString(100 , 16 , "Pa"); + Heltec.display->drawString(0 , 32 , "Altitude ="); + Heltec.display->drawString(56 , 32 , (String)A); + Heltec.display->drawString(86 , 32 , " m"); + Heltec.display->drawString(0 , 48 , "Real altitude ="); + Heltec.display->drawString(76 , 48 , (String)R); + Heltec.display->drawString(106 , 48 , " m"); + Heltec.display->display(); +} +void loop() { + if (!bmp.begin()) { + Serial.println("Could not find a valid BMP085 sensor, check wiring!"); + while (1) {} + } + double T =bmp.readTemperature(); + Serial.print("Temperature = "); + Serial.print(T); + Serial.println(" *C"); + + double P = bmp.readPressure(); + Serial.print("Pressure = "); + Serial.print(bmp.readPressure()); + Serial.println(" Pa"); + + double A = bmp.readAltitude(); + // Calculate altitude assuming 'standard' barometric + // pressure of 1013.25 millibar = 101325 Pascal + Serial.print("Altitude = "); + Serial.print(bmp.readAltitude()); + Serial.println(" meters"); + + + Serial.print("Pressure at sealevel (calculated) = "); + Serial.print(bmp.readSealevelPressure()); + Serial.println(" Pa"); + + double R = bmp.readAltitude(101500); + // you can get a more precise measurement of altitude + // if you know the current sea level pressure which will + // vary with weather and such. If it is 1015 millibars + // that is equal to 101500 Pascals. + Serial.print("Real altitude = "); + Serial.print(bmp.readAltitude(101500)); + Serial.println(" meters"); + + Serial.println(); + delay(500); + drawFontFaceDemo(T, P, A, R) ; + delay(5000); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/examples/TimeNTP_ESP32WiFi/TimeNTP_ESP32WiFi.ino b/libraries/Heltec_ESP32_Dev-Boards/examples/TimeNTP_ESP32WiFi/TimeNTP_ESP32WiFi.ino new file mode 100644 index 0000000..9938029 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/examples/TimeNTP_ESP32WiFi/TimeNTP_ESP32WiFi.ino @@ -0,0 +1,150 @@ +/* + * TimeNTP_ESP32WiFi.ino + * Example showing time sync to NTP time source + * + * This sketch uses the ESP32WiFi library + * This sketch uses the Time library https://github.com/PaulStoffregen/Time + */ + +#include +#include +#include + +const char ssid[] = "ssid"; // your network SSID (name) +const char pass[] = "password"; // your network password + +// NTP Servers: +static const char ntpServerName[] = "pool.ntp.org"; + +const int timeZone = 0; // UTC + +WiFiUDP Udp; +unsigned int localPort = 8888; // local port to listen for UDP packets + +time_t getNtpTime(); +void digitalClockDisplay(); +void printDigits(int digits); +void sendNTPpacket(IPAddress &address); + +void setup() +{ + Serial.begin(115200); + while (!Serial) ; // Needed for Leonardo only + delay(250); + Serial.println("TimeNTP Example"); + Serial.print("Connecting to "); + Serial.println(ssid); + WiFi.disconnect(); + WiFi.mode(WIFI_MODE_STA); + WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.print("IP number assigned by DHCP is "); + Serial.println(WiFi.localIP()); + Serial.println("Starting UDP"); + Udp.begin(localPort); + Serial.print("Local port: "); +// Serial.println(Udp.localPort()); + Serial.println("waiting for sync"); + setSyncProvider(getNtpTime); + setSyncInterval(300); +} + +time_t prevDisplay = 0; // when the digital clock was displayed + +void loop() +{ + if (timeStatus() != timeNotSet) { + if (now() != prevDisplay) { //update the display only if time has changed + prevDisplay = now(); + digitalClockDisplay(); + } + } +} + +void digitalClockDisplay() +{ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print("."); + Serial.print(month()); + Serial.print("."); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits) +{ + // utility for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if (digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +/*-------- NTP code ----------*/ + +const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message +byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets + +time_t getNtpTime() +{ + IPAddress ntpServerIP; // NTP server's ip address + + while (Udp.parsePacket() > 0) ; // discard any previously received packets + Serial.println("Transmit NTP Request"); + // get a random server from the pool + WiFi.hostByName(ntpServerName, ntpServerIP); + Serial.print(ntpServerName); + Serial.print(": "); + Serial.println(ntpServerIP); + sendNTPpacket(ntpServerIP); + uint32_t beginWait = millis(); + while (millis() - beginWait < 1500) { + int size = Udp.parsePacket(); + if (size >= NTP_PACKET_SIZE) { + Serial.println("Receive NTP Response"); + Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer + unsigned long secsSince1900; + // convert four bytes starting at location 40 to a long integer + secsSince1900 = (unsigned long)packetBuffer[40] << 24; + secsSince1900 |= (unsigned long)packetBuffer[41] << 16; + secsSince1900 |= (unsigned long)packetBuffer[42] << 8; + secsSince1900 |= (unsigned long)packetBuffer[43]; + return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; + } + } + Serial.println("No NTP Response :-("); + return 0; // return 0 if unable to get the time +} + +// send an NTP request to the time server at the given address +void sendNTPpacket(IPAddress &address) +{ + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer, NTP_PACKET_SIZE); + Udp.endPacket(); +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/img/location.png b/libraries/Heltec_ESP32_Dev-Boards/img/location.png new file mode 100644 index 0000000..f014a1d Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/img/location.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/img/location_cn.png b/libraries/Heltec_ESP32_Dev-Boards/img/location_cn.png new file mode 100644 index 0000000..70f25bd Binary files /dev/null and b/libraries/Heltec_ESP32_Dev-Boards/img/location_cn.png differ diff --git a/libraries/Heltec_ESP32_Dev-Boards/keywords.txt b/libraries/Heltec_ESP32_Dev-Boards/keywords.txt new file mode 100644 index 0000000..5336da9 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/keywords.txt @@ -0,0 +1,94 @@ +####################################### +# Syntax Coloring Map For Heltec_ESP32 +####################################### + +####################################### +# Library (KEYWORD3) +####################################### + +Heltec KEYWORD3 + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LoRa KEYWORD1 +display KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 +write KEYWORD2 +sleep KEYWORD2 + +beginPacket KEYWORD2 +endPacket KEYWORD2 +parsePacket KEYWORD2 +packetRssi KEYWORD2 +packetSnr KEYWORD2 +available KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 +onReceive KEYWORD2 +receive KEYWORD2 +read KEYWORD2 +idle KEYWORD2 +setTxPower KEYWORD2 +setTxPowerMax KEYWORD2 +setFrequency KEYWORD2 +setSpreadingFactor KEYWORD2 +setSignalBandwidth KEYWORD2 +setCodingRate4 KEYWORD2 +setPreambleLength KEYWORD2 +setSyncWord KEYWORD2 +enableCrc KEYWORD2 +disableCrc KEYWORD2 +crc KEYWORD2 +noCrc KEYWORD2 +random KEYWORD2 +setpins KEYWORD2 +setSPIFrequency KEYWORD2 +dumpRegisters KEYWORD2 + +drawString KEYWORD2 +display KEYWORD2 +wakeup KEYWORD2 +resetDisplay KEYWORD2 +setColor KEYWORD2 +getColor KEYWORD2 +setPixel KEYWORD2 +drawLine KEYWORD2 +drawRect KEYWORD2 +fillRect KEYWORD2 +drawCircle KEYWORD2 +drawCircleQuads KEYWORD2 +fillCircle KEYWORD2 +drawHorizontalLine KEYWORD2 +drawVerticalLine KEYWORD2 +drawProgressBar KEYWORD2 +drawFastImage KEYWORD2 +drawXbm KEYWORD2 +drawStringMaxWidth KEYWORD2 +getStringWidth KEYWORD2 +getStringWidth KEYWORD2 +setTextAlignment KEYWORD2 +setFont KEYWORD2 +setFontTableLookupFunction KEYWORD2 +displayOn KEYWORD2 +displayOff KEYWORD2 +invertDisplay KEYWORD2 +normalDisplay KEYWORD2 +setContrast KEYWORD2 +setBrightness KEYWORD2 +resetOrientation KEYWORD2 +flipScreenVertically KEYWORD2 +mirrorScreen KEYWORD2 +clear KEYWORD2 +setLogBuffer KEYWORD2 +drawLogBuffer KEYWORD2 +getWidth KEYWORD2 +getHeight KEYWORD2 + diff --git a/libraries/Heltec_ESP32_Dev-Boards/library.json b/libraries/Heltec_ESP32_Dev-Boards/library.json new file mode 100644 index 0000000..4537028 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/library.json @@ -0,0 +1,16 @@ +{ + "name": "Heltec_ESP32", + "keywords": "heltec, esp32, lora, wifi kit, oled", + "description": "A library for Heltec ESP32 (or ESP32+LoRa) based board", + "repository":{ + "type": "git", + "url": "https://github.com/HelTecAutomation/Heltec_ESP32.git" + }, + "authors":{ + "name": "Heltec Automation(TM)", + "email": "support@heltec.cn" + }, + "version": "1.1.0", + "frameworks": "arduino", + "platforms": "espressif32" +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/library.properties b/libraries/Heltec_ESP32_Dev-Boards/library.properties new file mode 100644 index 0000000..8709bdc --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/library.properties @@ -0,0 +1,10 @@ +name=Heltec ESP32 Dev-Boards +version=1.1.0 +author= Heltec Automation +maintainer=HelTec +sentence=Library for Heltec ESP32 (or ESP32+LoRa) based boards +paragraph=Includes: WiFi Kit 32, WiFi LoRa 32, Wireless Stick, Wireless Shell, see more on http://heltec.cn +category=Device Control +url=https://github.com/HelTecAutomation/Heltec_ESP32.git +architectures=esp32 +includes=heltec.h diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/BMP180.cpp b/libraries/Heltec_ESP32_Dev-Boards/src/BMP180.cpp new file mode 100644 index 0000000..52f6a97 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/BMP180.cpp @@ -0,0 +1,259 @@ +#include "BMP180.h" + +BMP085::BMP085() { +} + + +boolean BMP085::begin(uint8_t mode) { + if (mode > BMP085_ULTRAHIGHRES) + mode = BMP085_ULTRAHIGHRES; + oversampling = mode; +//begin(int sdaPin, int sclPin, uint32_t frequency) + Wire.begin(13, 12, 100000); + + if (read8(0xD0) != 0x55) return false; + + /* read calibration data */ + ac1 = read16(BMP085_CAL_AC1); + ac2 = read16(BMP085_CAL_AC2); + ac3 = read16(BMP085_CAL_AC3); + ac4 = read16(BMP085_CAL_AC4); + ac5 = read16(BMP085_CAL_AC5); + ac6 = read16(BMP085_CAL_AC6); + + b1 = read16(BMP085_CAL_B1); + b2 = read16(BMP085_CAL_B2); + + mb = read16(BMP085_CAL_MB); + mc = read16(BMP085_CAL_MC); + md = read16(BMP085_CAL_MD); +#if (BMP085_DEBUG == 1) + Serial.print("ac1 = "); Serial.println(ac1, DEC); + Serial.print("ac2 = "); Serial.println(ac2, DEC); + Serial.print("ac3 = "); Serial.println(ac3, DEC); + Serial.print("ac4 = "); Serial.println(ac4, DEC); + Serial.print("ac5 = "); Serial.println(ac5, DEC); + Serial.print("ac6 = "); Serial.println(ac6, DEC); + + Serial.print("b1 = "); Serial.println(b1, DEC); + Serial.print("b2 = "); Serial.println(b2, DEC); + + Serial.print("mb = "); Serial.println(mb, DEC); + Serial.print("mc = "); Serial.println(mc, DEC); + Serial.print("md = "); Serial.println(md, DEC); +#endif + + return true; +} + +int32_t BMP085::computeB5(int32_t UT) { + int32_t X1 = (UT - (int32_t)ac6) * ((int32_t)ac5) >> 15; + int32_t X2 = ((int32_t)mc << 11) / (X1+(int32_t)md); + return X1 + X2; +} + +uint16_t BMP085::readRawTemperature(void) { + write8(BMP085_CONTROL, BMP085_READTEMPCMD); + delay(5); +#if BMP085_DEBUG == 1 + Serial.print("Raw temp: "); Serial.println(read16(BMP085_TEMPDATA)); +#endif + return read16(BMP085_TEMPDATA); +} + +uint32_t BMP085::readRawPressure(void) { + uint32_t raw; + + write8(BMP085_CONTROL, BMP085_READPRESSURECMD + (oversampling << 6)); + + if (oversampling == BMP085_ULTRALOWPOWER) + delay(5); + else if (oversampling == BMP085_STANDARD) + delay(8); + else if (oversampling == BMP085_HIGHRES) + delay(14); + else + delay(26); + + raw = read16(BMP085_PRESSUREDATA); + + raw <<= 8; + raw |= read8(BMP085_PRESSUREDATA+2); + raw >>= (8 - oversampling); + + /* this pull broke stuff, look at it later? + if (oversampling==0) { + raw <<= 8; + raw |= read8(BMP085_PRESSUREDATA+2); + raw >>= (8 - oversampling); + } + */ + +#if BMP085_DEBUG == 1 + Serial.print("Raw pressure: "); Serial.println(raw); +#endif + return raw; +} + + +int32_t BMP085::readPressure(void) { + int32_t UT, UP, B3, B5, B6, X1, X2, X3, p; + uint32_t B4, B7; + + UT = readRawTemperature(); + UP = readRawPressure(); + +#if BMP085_DEBUG == 1 + // use datasheet numbers! + UT = 27898; + UP = 23843; + ac6 = 23153; + ac5 = 32757; + mc = -8711; + md = 2868; + b1 = 6190; + b2 = 4; + ac3 = -14383; + ac2 = -72; + ac1 = 408; + ac4 = 32741; + oversampling = 0; +#endif + + B5 = computeB5(UT); + +#if BMP085_DEBUG == 1 + Serial.print("X1 = "); Serial.println(X1); + Serial.print("X2 = "); Serial.println(X2); + Serial.print("B5 = "); Serial.println(B5); +#endif + + // do pressure calcs + B6 = B5 - 4000; + X1 = ((int32_t)b2 * ( (B6 * B6)>>12 )) >> 11; + X2 = ((int32_t)ac2 * B6) >> 11; + X3 = X1 + X2; + B3 = ((((int32_t)ac1*4 + X3) << oversampling) + 2) / 4; + +#if BMP085_DEBUG == 1 + Serial.print("B6 = "); Serial.println(B6); + Serial.print("X1 = "); Serial.println(X1); + Serial.print("X2 = "); Serial.println(X2); + Serial.print("B3 = "); Serial.println(B3); +#endif + + X1 = ((int32_t)ac3 * B6) >> 13; + X2 = ((int32_t)b1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + B4 = ((uint32_t)ac4 * (uint32_t)(X3 + 32768)) >> 15; + B7 = ((uint32_t)UP - B3) * (uint32_t)( 50000UL >> oversampling ); + +#if BMP085_DEBUG == 1 + Serial.print("X1 = "); Serial.println(X1); + Serial.print("X2 = "); Serial.println(X2); + Serial.print("B4 = "); Serial.println(B4); + Serial.print("B7 = "); Serial.println(B7); +#endif + + if (B7 < 0x80000000) { + p = (B7 * 2) / B4; + } else { + p = (B7 / B4) * 2; + } + X1 = (p >> 8) * (p >> 8); + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * p) >> 16; + +#if BMP085_DEBUG == 1 + Serial.print("p = "); Serial.println(p); + Serial.print("X1 = "); Serial.println(X1); + Serial.print("X2 = "); Serial.println(X2); +#endif + + p = p + ((X1 + X2 + (int32_t)3791)>>4); +#if BMP085_DEBUG == 1 + Serial.print("p = "); Serial.println(p); +#endif + return p; +} + +int32_t BMP085::readSealevelPressure(float altitude_meters) { + float pressure = readPressure(); + return (int32_t)(pressure / pow(1.0-altitude_meters/44330, 5.255)); +} + +float BMP085::readTemperature(void) { + int32_t UT, B5; // following ds convention + float temp; + + UT = readRawTemperature(); + +#if BMP085_DEBUG == 1 + // use datasheet numbers! + UT = 27898; + ac6 = 23153; + ac5 = 32757; + mc = -8711; + md = 2868; +#endif + + B5 = computeB5(UT); + temp = (B5+8) >> 4; + temp /= 10; + + return temp; +} + +float BMP085::readAltitude(float sealevelPressure) { + float altitude; + + float pressure = readPressure(); + + altitude = 44330 * (1.0 - pow(pressure /sealevelPressure,0.1903)); + + return altitude; +} + + +/*********************************************************************/ + +uint8_t BMP085::read8(uint8_t a) { + uint8_t ret; + + Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device + + Wire.write(a); // sends register address to read from + + Wire.endTransmission(); // end transmission + + Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device + Wire.requestFrom(BMP085_I2CADDR, 1);// send data n-bytes read + ret = Wire.read(); // receive DATA + Wire.endTransmission(); // end transmission + + return ret; +} + +uint16_t BMP085::read16(uint8_t a) { + uint16_t ret; + + Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device + Wire.write(a); // sends register address to read from + Wire.endTransmission(); // end transmission + + Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device + Wire.requestFrom(BMP085_I2CADDR, 2);// send data n-bytes read + ret = Wire.read(); // receive DATA + ret <<= 8; + ret |= Wire.read(); // receive DATA + Wire.endTransmission(); // end transmission + + return ret; +} + +void BMP085::write8(uint8_t a, uint8_t d) { + Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device + Wire.write(a); // sends register address to read from + Wire.write(d); // write data + Wire.endTransmission(); // end transmission +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/BMP180.h b/libraries/Heltec_ESP32_Dev-Boards/src/BMP180.h new file mode 100644 index 0000000..029c66c --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/BMP180.h @@ -0,0 +1,58 @@ +#ifndef BMP180_H +#define BMP180_H + +#include "Arduino.h" +#include "Wire.h" + +#define BMP085_DEBUG 0 + +#define BMP085_I2CADDR 0x77 + +#define BMP085_ULTRALOWPOWER 0 +#define BMP085_STANDARD 1 +#define BMP085_HIGHRES 2 +#define BMP085_ULTRAHIGHRES 3 +#define BMP085_CAL_AC1 0xAA // R Calibration data (16 bits) +#define BMP085_CAL_AC2 0xAC // R Calibration data (16 bits) +#define BMP085_CAL_AC3 0xAE // R Calibration data (16 bits) +#define BMP085_CAL_AC4 0xB0 // R Calibration data (16 bits) +#define BMP085_CAL_AC5 0xB2 // R Calibration data (16 bits) +#define BMP085_CAL_AC6 0xB4 // R Calibration data (16 bits) +#define BMP085_CAL_B1 0xB6 // R Calibration data (16 bits) +#define BMP085_CAL_B2 0xB8 // R Calibration data (16 bits) +#define BMP085_CAL_MB 0xBA // R Calibration data (16 bits) +#define BMP085_CAL_MC 0xBC // R Calibration data (16 bits) +#define BMP085_CAL_MD 0xBE // R Calibration data (16 bits) + +#define BMP085_CONTROL 0xF4 +#define BMP085_TEMPDATA 0xF6 +#define BMP085_PRESSUREDATA 0xF6 +#define BMP085_READTEMPCMD 0x2E +#define BMP085_READPRESSURECMD 0x34 + + +class BMP085 { + public: + BMP085(); + boolean begin(uint8_t mode = BMP085_ULTRAHIGHRES); // by default go highres + float readTemperature(void); + int32_t readPressure(void); + int32_t readSealevelPressure(float altitude_meters = 0); + float readAltitude(float sealevelPressure = 101325); // std atmosphere + uint16_t readRawTemperature(void); + uint32_t readRawPressure(void); + + private: + int32_t computeB5(int32_t UT); + uint8_t read8(uint8_t addr); + uint16_t read16(uint8_t addr); + void write8(uint8_t addr, uint8_t data); + + uint8_t oversampling; + + int16_t ac1, ac2, ac3, b1, b2, mb, mc, md; + uint16_t ac4, ac5, ac6; +}; + + +#endif // _BMP085_H diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/heltec.cpp b/libraries/Heltec_ESP32_Dev-Boards/src/heltec.cpp new file mode 100644 index 0000000..819d507 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/heltec.cpp @@ -0,0 +1,119 @@ +// Copyright (c) Heltec Automation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "heltec.h" + + +Heltec_ESP32::Heltec_ESP32(){ + +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) + display = new SSD1306Wire(0x3c, SDA_OLED, SCL_OLED, RST_OLED, GEOMETRY_128_64); +#elif defined( Wireless_Stick ) + display = new SSD1306Wire(0x3c, SDA_OLED, SCL_OLED, RST_OLED, GEOMETRY_64_32); +#endif +} + +Heltec_ESP32::~Heltec_ESP32(){ +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) + delete display; +#endif +} + +void Heltec_ESP32::begin(bool DisplayEnable, bool LoRaEnable, bool SerialEnable, bool PABOOST, long BAND) { + +#if defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) || defined( Wireless_Stick_Lite ) || defined(WIFI_Kit_32) + + VextON(); +#endif + + // UART + if (SerialEnable) { + Serial.begin(115200); + Serial.flush(); + delay(50); + Serial.print("Serial initial done\r\n"); + } + + // OLED + if (DisplayEnable) + { +#if defined( Wireless_Stick_Lite ) + if(SerialEnable && Wireless_Stick_Lite) + { + Serial.print("The Wireless Stick Lite not have an on board display, Display option must be FALSE!!!\r\n"); + } +#endif + +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) + display->init(); + display->flipScreenVertically(); + display->setFont(ArialMT_Plain_10); + display->drawString(0, 0, "OLED initial done!"); + display->display(); + + if (SerialEnable){ + Serial.print("you can see OLED printed OLED initial done!\r\n"); + } +#endif + } + + // LoRa INIT + if (LoRaEnable) + { +#if defined(WIFI_Kit_32) + if(SerialEnable && WIFI_Kit_32){ + Serial.print("The WiFi Kit 32 not have LoRa function, LoRa option must be FALSE!!!\r\n"); + } +#endif + + +#if defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) || defined( Wireless_Stick_Lite ) + //LoRaClass LoRa; + + SPI.begin(SCK,MISO,MOSI,SS); + LoRa.setPins(SS,RST_LoRa,DIO0); + if (!LoRa.begin(BAND,PABOOST)) + { + if (SerialEnable){ + Serial.print("Starting LoRa failed!\r\n"); + } +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) + if(DisplayEnable){ + display->clear(); + display->drawString(0, 0, "Starting LoRa failed!"); + display->display(); + delay(300); + } +#endif + while (1); + } + if (SerialEnable){ + Serial.print("LoRa Initial success!\r\n"); + } +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) + if(DisplayEnable){ + display->clear(); + display->drawString(0, 0, "LoRa Initial success!"); + display->display(); + delay(300); + } +#endif + +#endif + } + pinMode(LED,OUTPUT); +} + +void Heltec_ESP32::VextON(void) +{ + pinMode(Vext,OUTPUT); + digitalWrite(Vext, LOW); +} + +void Heltec_ESP32::VextOFF(void) //Vext default OFF +{ + pinMode(Vext,OUTPUT); + digitalWrite(Vext, HIGH); +} + +Heltec_ESP32 Heltec; diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/heltec.h b/libraries/Heltec_ESP32_Dev-Boards/src/heltec.h new file mode 100644 index 0000000..81c2eba --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/heltec.h @@ -0,0 +1,47 @@ + + +#ifndef _HELTEC_H_ +#define _HELTEC_H_ + +#if defined(ESP32) + +#include +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) +#include +#include "oled/SSD1306Wire.h" +#endif + +#if defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) || defined( Wireless_Stick_Lite ) + #include + #include "lora/LoRa.h" +#endif + + +class Heltec_ESP32 { + + public: + Heltec_ESP32(); + ~Heltec_ESP32(); + + void begin(bool DisplayEnable=true, bool LoRaEnable=true, bool SerialEnable=true, bool PABOOST=true, long BAND=470E6); +#if defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) || defined( Wireless_Stick_Lite ) + LoRaClass LoRa; +#endif + +#if defined( WIFI_Kit_32 ) || defined( WIFI_LoRa_32 ) || defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) + SSD1306Wire *display; +#endif + +/*wifi kit 32 and WiFi LoRa 32(V1) do not have vext*/ + void VextON(void); + void VextOFF(void); +}; + +extern Heltec_ESP32 Heltec; + +#else +#error "This library only supports boards with ESP32 processor." +#endif + + +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/lora/API.md b/libraries/Heltec_ESP32_Dev-Boards/src/lora/API.md new file mode 100644 index 0000000..159a539 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/lora/API.md @@ -0,0 +1,315 @@ +# Heltec LoRa basic library API + +## Include Library + +```arduino +#include +``` + +## Setup + +### Begin + +Initialize the library with the specified frequency. + +```arduino +LoRa.begin(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +Returns `1` on success, `0` on failure. + +### Set pins + +Override the default `NSS`, `NRESET`, and `DIO0` pins used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setPins(ss, reset, dio0); +``` + * `ss` - new slave select pin to use, defaults to `18` + * `reset` - new reset pin to use, defaults to `14` + * `dio0` - new DIO0 pin to use, defaults to `26` + +This call is optional and only needs to be used if you need to change the default pins used. + +### Set SPI Frequency + +Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPIFrequency(frequency); +``` + * `frequency` - new SPI frequency to use, defaults to `10E6` + +This call is optional and only needs to be used if you need to change the default SPI frequency used. Some logic level converters cannot support high speeds such as 10 MHz, so a lower SPI frequency can be selected with `LoRa.setSPIFrequency(frequency)`. + +### End + +Stop the library + +```arduino +LoRa.end() +``` + +## Sending data + +### Begin packet + +Start the sequence of sending a packet. + +```arduino +LoRa.beginPacket(); + +LoRa.beginPacket(implicitHeader); +``` + + * `implicitHeader` - (optional) `true` enables implicit header mode, `false` enables explicit header mode (default) + +Returns `1` on success, `0` on failure. + +### Writing + +Write data to the packet. Each packet can contain up to 255 bytes. + +```arduino +LoRa.write(byte); + +LoRa.write(buffer, length); +``` +* `byte` - single byte to write to packet + +or + +* `buffer` - data to write to packet +* `length` - size of data to write + +Returns the number of bytes written. + +**Note:** Other Arduino `Print` API's can also be used to write data into the packet + +### End packet + +End the sequence of sending a packet. + +```arduino +LoRa.endPacket() +``` + +Returns `1` on success, `0` on failure. + +## Receiving data + +### Parsing packet + +Check if a packet has been received. + +```arduino +int packetSize = LoRa.parsePacket(); + +int packetSize = LoRa.parsePacket(size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + + +Returns the packet size in bytes or `0` if no packet was received. + +### Continuous receive mode + +#### Register callback + +Register a callback function for when a packet is received. + +```arduino +LoRa.onReceive(onReceive); + +void onReceive(int packetSize) { + // ... +} +``` + + * `onReceive` - function to call when a packet is received. + +#### Receive mode + +Puts the radio in continuous receive mode. + +```arduino +LoRa.receive(); + +LoRa.receive(int size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + +The `onReceive` callback will be called when a packet is received. + +### Packet RSSI + +```arduino +int rssi = LoRa.packetRssi(); +``` + +Returns the RSSI of the received packet. + +### Packet SNR + +```arduino +float snr = LoRa.packetSnr(); +``` + +Returns the estimated SNR of the received packet in dB. + +### Available + +```arduino +int availableBytes = LoRa.available() +``` + +Returns number of bytes available for reading. + +### Peeking + +Peek at the next byte in the packet. + +```arduino +byte b = LoRa.peek(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +### Reading + +Read the next byte from the packet. + +```arduino +byte b = LoRa.read(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet + +## Other radio modes + +### Idle mode + +Put the radio in idle (standby) mode. + +```arduino +LoRa.idle(); +``` + +### Sleep mode + +Put the radio in sleep mode. + +```arduino +LoRa.sleep(); +``` + +## Radio parameters + +### TX Power + +Change the TX power of the radio. + +```arduino +LoRa.setTxPower(txPower); + +LoRa.setTxPower(txPower, outputPin); +``` + * `txPower` - TX power in dB, defaults to `14` + * `outputPin` - (optional) PA output pin, supported values are `PA_OUTPUT_RFO_PIN` and `PA_OUTPUT_PA_BOOST_PIN`, defaults to `PA_OUTPUT_PA_BOOST_PIN`. + +Supported values are between `2` and `17` for `PA_OUTPUT_PA_BOOST_PIN`, `0` and `14` for `PA_OUTPUT_RFO_PIN`. + +Most modules have the PA output pin connected to PA BOOST, + +### Frequency + +Change the frequency of the radio. + +```arduino +LoRa.setFrequency(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +### Spreading Factor + +Change the spreading factor of the radio. + +```arduino +LoRa.setSpreadingFactor(spreadingFactor); +``` + * `spreadingFactor` - spreading factor, defaults to `7` + +Supported values are between `6` and `12`. If a spreading factor of `6` is set, implicit header mode must be used to transmit and receive packets. + +### Signal Bandwidth + +Change the signal bandwidth of the radio. + +```arduino +LoRa.setSignalBandwidth(signalBandwidth); +``` + + * `signalBandwidth` - signal bandwidth in Hz, defaults to `125E3`. + +Supported values are `7.8E3`, `10.4E3`, `15.6E3`, `20.8E3`, `31.25E3`, `41.7E3`, `62.5E3`, `125E3`, and `250E3`. + +### Coding Rate + +Change the coding rate of the radio. + +```arduino +LoRa.setCodingRate4(codingRateDenominator); +``` + + * `codingRateDenominator` - denominator of the coding rate, defaults to `5` + +Supported values are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. The coding rate numerator is fixed at `4`. + +### Preamble Length + +Change the preamble length of the radio. + +```arduino +LoRa.setPreambleLength(preambleLength); +``` + + * `preambleLength` - preamble length in symbols, defaults to `8` + +Supported values are between `6` and `65535`. + +### Sync Word + +Change the sync word of the radio. + +```arduino +LoRa.setSyncWord(syncWord); +``` + + * `syncWord` - byte value to use as the sync word, defaults to `0x34` + +### CRC + +Enable or disable CRC usage, by default a CRC is not used. + +```arduino +LoRa.enableCrc(); + +LoRa.disableCrc(); +``` + +## Other functions + +### Random + +Generate a random byte, based on the Wideband RSSI measurement. + +``` +byte b = LoRa.random(); +``` + +Returns random byte. diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/lora/LoRa.cpp b/libraries/Heltec_ESP32_Dev-Boards/src/lora/LoRa.cpp new file mode 100644 index 0000000..ccf933a --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/lora/LoRa.cpp @@ -0,0 +1,551 @@ +#include "LoRa.h" + +// registers +#define REG_FIFO 0x00 +#define REG_OP_MODE 0x01 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define REG_LR_OCP 0X0b +#define REG_LNA 0x0c +#define REG_FIFO_ADDR_PTR 0x0d +#define REG_FIFO_TX_BASE_ADDR 0x0e +#define REG_FIFO_RX_BASE_ADDR 0x0f +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_RX_NB_BYTES 0x13 +#define REG_PKT_RSSI_VALUE 0x1a +#define REG_PKT_SNR_VALUE 0x1b +#define REG_MODEM_CONFIG_1 0x1d +#define REG_MODEM_CONFIG_2 0x1e +#define REG_PREAMBLE_MSB 0x20 +#define REG_PREAMBLE_LSB 0x21 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_MODEM_CONFIG_3 0x26 +#define REG_RSSI_WIDEBAND 0x2c +#define REG_DETECTION_OPTIMIZE 0x31 +#define REG_DETECTION_THRESHOLD 0x37 +#define REG_SYNC_WORD 0x39 +#define REG_DIO_MAPPING_1 0x40 +#define REG_VERSION 0x42 +#define REG_PaDac 0x4d//add REG_PaDac + +// modes +#define MODE_LONG_RANGE_MODE 0x80 +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x01 +#define MODE_TX 0x03 +#define MODE_RX_CONTINUOUS 0x05 +#define MODE_RX_SINGLE 0x06 + +// PA config +//#define PA_BOOST 0x80 +//#define RFO 0x70 +// IRQ masks +#define IRQ_TX_DONE_MASK 0x08 +#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 +#define IRQ_RX_DONE_MASK 0x40 + + +#define MAX_PKT_LENGTH 255 + +LoRaClass::LoRaClass() : + _spiSettings(8E6, MSBFIRST, SPI_MODE0), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), + _frequency(0), + _packetIndex(0), + _implicitHeaderMode(0), + _onReceive(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +int LoRaClass::begin(long frequency,bool PABOOST) +{ + // setup pins + pinMode(_ss, OUTPUT); + pinMode(_reset, OUTPUT); + pinMode(_dio0, INPUT); + // perform reset + digitalWrite(_reset, LOW); + delay(20); + digitalWrite(_reset, HIGH); + delay(50); + // set SS high + digitalWrite(_ss, HIGH); + // start SPI + SPI.begin(); + // check version + uint8_t version = readRegister(REG_VERSION); + if (version != 0x12) { + return 0; + } + // put in sleep mode + sleep(); + // set frequency + setFrequency(frequency); + // set base addresses + writeRegister(REG_FIFO_TX_BASE_ADDR, 0); + writeRegister(REG_FIFO_RX_BASE_ADDR, 0); + // set LNA boost + writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + // set output power to 14 dBm + if(PABOOST == true) + setTxPower(14, RF_PACONFIG_PASELECT_PABOOST); + else + setTxPower(14, RF_PACONFIG_PASELECT_RFO); + setSpreadingFactor(11); + // put in standby mode + setSignalBandwidth(125E3); + //setCodingRate4(5); + setSyncWord(0x34); + disableCrc(); + crc(); + idle(); + return 1; +} + +void LoRaClass::end() +{ + // put in sleep mode + sleep(); + // stop SPI + SPI.end(); +} + +int LoRaClass::beginPacket(int implicitHeader) +{ + // put in standby mode + idle(); + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + // reset FIFO address and paload length + writeRegister(REG_FIFO_ADDR_PTR, 0); + writeRegister(REG_PAYLOAD_LENGTH, 0); + return 1; +} + +int LoRaClass::endPacket(bool async) +{ + // put in TX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); + + if (async) { + // grace time is required for the radio + delayMicroseconds(150); + } else { + // wait for TX done + while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { + yield(); + } + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return 1; +} + +int LoRaClass::parsePacket(int size) +{ + int packetLength = 0; + int irqFlags = readRegister(REG_IRQ_FLAGS); + + if (size > 0) { + implicitHeaderMode(); + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + // read packet length + if (_implicitHeaderMode) { + packetLength = readRegister(REG_PAYLOAD_LENGTH); + } else { + packetLength = readRegister(REG_RX_NB_BYTES); + } + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + // put in standby mode + idle(); + } + else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { + // not currently in RX mode + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + // put in single RX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); + } + return packetLength; +} + +int LoRaClass::packetRssi() +{ + int8_t snr=0; + int8_t SnrValue = readRegister( 0x19 ); + int16_t rssi = readRegister(REG_PKT_RSSI_VALUE); + + if( SnrValue & 0x80 ) // The SNR sign bit is 1 + { + // Invert and divide by 4 + snr = ( ( ~SnrValue + 1 ) & 0xFF ) >> 2; + snr = -snr; + } + else + { + // Divide by 4 + snr = ( SnrValue & 0xFF ) >> 2; + } + if(snr<0) + { + rssi = rssi - (_frequency < 525E6 ? 164 : 157) + ( rssi >> 4 ) + snr; + } + else + { + rssi = rssi - (_frequency < 525E6 ? 164 : 157) + ( rssi >> 4 ); + } + + return ( rssi ); +} + +float LoRaClass::packetSnr() +{ + return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; +} + +size_t LoRaClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t LoRaClass::write(const uint8_t *buffer, size_t size) +{ + int currentLength = readRegister(REG_PAYLOAD_LENGTH); + // check size + if ((currentLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - currentLength; + } + // write data + for (size_t i = 0; i < size; i++) { + writeRegister(REG_FIFO, buffer[i]); + } + // update length + writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); + return size; +} + +int LoRaClass::available() +{ + return (readRegister(REG_RX_NB_BYTES) - _packetIndex); +} + +int LoRaClass::read() +{ + if (!available()) { + return -1; + } + _packetIndex++; + return readRegister(REG_FIFO); +} + +int LoRaClass::peek() +{ + if (!available()) { + return -1; + } + // store current FIFO address + int currentAddress = readRegister(REG_FIFO_ADDR_PTR); + // read + uint8_t b = readRegister(REG_FIFO); + // restore FIFO address + writeRegister(REG_FIFO_ADDR_PTR, currentAddress); + return b; +} + +void LoRaClass::flush() +{ +} + +void LoRaClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + writeRegister(REG_DIO_MAPPING_1, 0x00); + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); +// attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); + } +} + +void LoRaClass::receive(int size) +{ + if (size > 0) { + implicitHeaderMode(); + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); +} + +void LoRaClass::idle() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); +} + +void LoRaClass::sleep() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); +} + +void LoRaClass::setTxPower(int8_t power, int8_t outputPin) +{ + uint8_t paConfig = 0; + uint8_t paDac = 0; + + paConfig = readRegister( REG_PA_CONFIG ); + paDac = readRegister( REG_PaDac ); + + paConfig = ( paConfig & RF_PACONFIG_PASELECT_MASK ) | outputPin; + paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK ) | 0x70; + + if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST ) + { + if( power > 17 ) + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_ON; + } + else + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_OFF; + } + if( ( paDac & RF_PADAC_20DBM_ON ) == RF_PADAC_20DBM_ON ) + { + if( power < 5 ) + { + power = 5; + } + if( power > 20 ) + { + power = 20; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 5 ) & 0x0F ); + } + else + { + if( power < 2 ) + { + power = 2; + } + if( power > 17 ) + { + power = 17; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 2 ) & 0x0F ); + } + } + else + { + if( power < -1 ) + { + power = -1; + } + if( power > 14 ) + { + power = 14; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power + 1 ) & 0x0F ); + } + writeRegister( REG_PA_CONFIG, paConfig ); + writeRegister( REG_PaDac, paDac ); +} + +void LoRaClass::setTxPowerMax(int level) +{ + if (level < 5) { + level = 5; + } + else if(level > 20) { + level = 20; + } + writeRegister(REG_LR_OCP,0x3f); + writeRegister(REG_PaDac,0x87);//Open PA_BOOST + writeRegister(REG_PA_CONFIG, RF_PACONFIG_PASELECT_PABOOST | (level - 5)); +} + +void LoRaClass::setFrequency(long frequency) +{ + _frequency = frequency; + uint64_t frf = ((uint64_t)frequency << 19) / 32000000; + writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); + writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); + writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); +} + +void LoRaClass::setSpreadingFactor(int sf) +{ + if (sf < 6) { + sf = 6; + } + else if (sf > 12) { + sf = 12; + } + if (sf == 6) { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); + writeRegister(REG_DETECTION_THRESHOLD, 0x0c); + } else { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); + writeRegister(REG_DETECTION_THRESHOLD, 0x0a); + } + writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); +} + +void LoRaClass::setSignalBandwidth(long sbw) +{ + int bw; + + if (sbw <= 7.8E3) { bw = 0; } + else if (sbw <= 10.4E3) { bw = 1; } + else if (sbw <= 15.6E3) { bw = 2; } + else if (sbw <= 20.8E3) { bw = 3; } + else if (sbw <= 31.25E3) { bw = 4; } + else if (sbw <= 41.7E3) { bw = 5; } + else if (sbw <= 62.5E3) { bw = 6; } + else if (sbw <= 125E3) { bw = 7; } + else if (sbw <= 250E3) { bw = 8; } + else /*if (sbw <= 250E3)*/ { bw = 9; } + writeRegister(REG_MODEM_CONFIG_1,(readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); +} + +void LoRaClass::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + int cr = denominator - 4; + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); +} + +void LoRaClass::setPreambleLength(long length) +{ + writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); + writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); +} + +void LoRaClass::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD, sw); +} + +void LoRaClass::enableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); +} + +void LoRaClass::disableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); +} + +byte LoRaClass::random() +{ + return readRegister(REG_RSSI_WIDEBAND); +} + +void LoRaClass::setPins(int ss, int reset, int dio0) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; +} + +void LoRaClass::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void LoRaClass::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 128; i++) { + out.print("0x"); + out.print(i, HEX); + out.print(": 0x"); + out.println(readRegister(i), HEX); + } +} + +void LoRaClass::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); +} + +void LoRaClass::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); +} + +void LoRaClass::handleDio0Rise() +{ + int irqFlags = readRegister(REG_IRQ_FLAGS); + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + // read packet length + int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + if (_onReceive) { _onReceive(packetLength); } + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + } +} + +uint8_t LoRaClass::readRegister(uint8_t address) +{ + return singleTransfer(address & 0x7f, 0x00); +} + +void LoRaClass::writeRegister(uint8_t address, uint8_t value) +{ + singleTransfer(address | 0x80, value); +} + +uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) +{ + uint8_t response; + digitalWrite(_ss, LOW); + SPI.beginTransaction(_spiSettings); + SPI.transfer(address); + response = SPI.transfer(value); + SPI.endTransaction(); + digitalWrite(_ss, HIGH); + return response; +} + +void LoRaClass::onDio0Rise() +{ + LoRa.handleDio0Rise(); +} + +LoRaClass LoRa; diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/lora/LoRa.h b/libraries/Heltec_ESP32_Dev-Boards/src/lora/LoRa.h new file mode 100644 index 0000000..ebbc5f6 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/lora/LoRa.h @@ -0,0 +1,117 @@ +#ifndef LORA_H +#define LORA_H + +#include +#include + +#define LORA_DEFAULT_SS_PIN 18 +#define LORA_DEFAULT_RESET_PIN 14 +#define LORA_DEFAULT_DIO0_PIN 26 + +#define PA_OUTPUT_PA_BOOST_PIN 1 +#define PA_OUTPUT_RFO_PIN 0 + +/*! + * RegPaConfig + */ +#define RF_PACONFIG_PASELECT_MASK 0x7F +#define RF_PACONFIG_PASELECT_PABOOST 0x80 +#define RF_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RF_PACONFIG_MAX_POWER_MASK 0x8F + +#define RF_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaDac + */ +#define RF_PADAC_20DBM_MASK 0xF8 +#define RF_PADAC_20DBM_ON 0x07 +#define RF_PADAC_20DBM_OFF 0x04 // Default + + + +#if defined (__STM32F1__) +inline unsigned char digitalPinToInterrupt(unsigned char Interrupt_pin) { return Interrupt_pin; } //This isn't included in the stm32duino libs (yet) +#define portOutputRegister(port) (volatile byte *)( &(port->regs->ODR) ) //These are defined in STM32F1/variants/generic_stm32f103c/variant.h but return a non byte* value +#define portInputRegister(port) (volatile byte *)( &(port->regs->IDR) ) //These are defined in STM32F1/variants/generic_stm32f103c/variant.h but return a non byte* value +#endif + +class LoRaClass : public Stream { +public: + LoRaClass(); + + int begin(long frequency,bool PABOOST); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(bool async = false); + + int parsePacket(int size = 0); + int packetRssi(); + float packetSnr(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + + void onReceive(void(*callback)(int)); + + void receive(int size = 0); + void idle(); + void sleep(); + + void setTxPower(int8_t power, int8_t outputPin); + void setTxPowerMax(int level); + void setFrequency(long frequency); + void setSpreadingFactor(int sf); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + void enableCrc(); + void disableCrc(); + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + +private: + SPISettings _spiSettings; + int _ss; + int _reset; + int _dio0; + int _frequency; + int _packetIndex; + int _implicitHeaderMode; + void (*_onReceive)(int); +}; + +extern LoRaClass LoRa; + +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplay.cpp b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplay.cpp new file mode 100644 index 0000000..b56ac24 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplay.cpp @@ -0,0 +1,964 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include "OLEDDisplay.h" + +OLEDDisplay::~OLEDDisplay() { + end(); +} + +bool OLEDDisplay::init() { + if (!this->connect()) { + //DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); + Serial.print("Can't establish connection to display\n"); + return false; + } + + if(this->buffer==NULL) { + this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize); + + if(!this->buffer) { + //DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); + Serial.print("Not enough memory to create display\n"); + return false; + } + } + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if(this->buffer_back==NULL) { + this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize); + + if(!this->buffer_back) { + //DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); + Serial.print("Not enough memory to create back buffer\n"); + free(this->buffer); + return false; + } + } + #endif + + resetDisplay(16); + sendInitCommands(); + + clear(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + memset(buffer_back, 1, displayBufferSize); + #endif + display(); + return true; +} + +void OLEDDisplay::end() { + if (this->buffer) { free(this->buffer); this->buffer = NULL; } + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if (this->buffer_back) { free(this->buffer_back); this->buffer_back = NULL; } + #endif + if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; } +} + +void OLEDDisplay::sleep() { + sendCommand(0x8D); + sendCommand(0x10); + sendCommand(0xAE); +} + +void OLEDDisplay::wakeup() { + sendCommand(0x8D); + sendCommand(0x14); + sendCommand(0xAF); +} + +void OLEDDisplay::resetDisplay(uint8_t rstPin) { + pinMode(rstPin, OUTPUT); + digitalWrite(rstPin,LOW); + delay(100); + digitalWrite(rstPin,HIGH); +} + +void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) { + this->color = color; +} + +OLEDDISPLAY_COLOR OLEDDisplay::getColor() { + return this->color; +} + +void OLEDDisplay::setPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +// Bresenham's algorithm - thx wikipedia and Adafruit_GFX +void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + setPixel(y0, x0); + } else { + setPixel(x0, y0); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) { + drawHorizontalLine(x, y, width); + drawVerticalLine(x, y, height); + drawVerticalLine(x + width - 1, y, height); + drawHorizontalLine(x, y + height - 1, width); +} + +void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) { + for (int16_t x = xMove; x < xMove + width; x++) { + drawVerticalLine(x, yMove, height); + } +} + +void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + 2 * (++x) + 3; + else + dp = dp + 2 * (++x) - 2 * (--y) + 5; + + setPixel(x0 + x, y0 + y); //For the 8 octants + setPixel(x0 - x, y0 + y); + setPixel(x0 + x, y0 - y); + setPixel(x0 - x, y0 - y); + setPixel(x0 + y, y0 + x); + setPixel(x0 - y, y0 + x); + setPixel(x0 + y, y0 - x); + setPixel(x0 - y, y0 - x); + + } while (x < y); + + setPixel(x0 + radius, y0); + setPixel(x0, y0 + radius); + setPixel(x0 - radius, y0); + setPixel(x0, y0 - radius); +} + +void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + while (x < y) { + if (dp < 0) + dp = dp + 2 * (++x) + 3; + else + dp = dp + 2 * (++x) - 2 * (--y) + 5; + if (quads & 0x1) { + setPixel(x0 + x, y0 - y); + setPixel(x0 + y, y0 - x); + } + if (quads & 0x2) { + setPixel(x0 - y, y0 - x); + setPixel(x0 - x, y0 - y); + } + if (quads & 0x4) { + setPixel(x0 - y, y0 + x); + setPixel(x0 - x, y0 + y); + } + if (quads & 0x8) { + setPixel(x0 + x, y0 + y); + setPixel(x0 + y, y0 + x); + } + } + if (quads & 0x1 && quads & 0x8) { + setPixel(x0 + radius, y0); + } + if (quads & 0x4 && quads & 0x8) { + setPixel(x0, y0 + radius); + } + if (quads & 0x2 && quads & 0x4) { + setPixel(x0 - radius, y0); + } + if (quads & 0x1 && quads & 0x2) { + setPixel(x0, y0 - radius); + } +} + + +void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + 2 * (++x) + 3; + else + dp = dp + 2 * (++x) - 2 * (--y) + 5; + + drawHorizontalLine(x0 - x, y0 - y, 2*x); + drawHorizontalLine(x0 - x, y0 + y, 2*x); + drawHorizontalLine(x0 - y, y0 - x, 2*y); + drawHorizontalLine(x0 - y, y0 + x, 2*y); + + + } while (x < y); + drawHorizontalLine(x0 - radius, y0, 2 * radius); + +} + +void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) { + if (y < 0 || y >= this->height()) { return; } + + if (x < 0) { + length += x; + x = 0; + } + + if ( (x + length) > this->width()) { + length = (this->width() - x); + } + + if (length <= 0) { return; } + + uint8_t * bufferPtr = buffer; + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + uint8_t drawBit = 1 << (y & 7); + + switch (color) { + case WHITE: while (length--) { + *bufferPtr++ |= drawBit; + }; break; + case BLACK: drawBit = ~drawBit; while (length--) { + *bufferPtr++ &= drawBit; + }; break; + case INVERSE: while (length--) { + *bufferPtr++ ^= drawBit; + }; break; + } +} + +void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) { + if (x < 0 || x >= this->width()) return; + + if (y < 0) { + length += y; + y = 0; + } + + if ( (y + length) > this->height()) { + length = (this->height() - y); + } + + if (length <= 0) return; + + + uint8_t yOffset = y & 7; + uint8_t drawBit; + uint8_t *bufferPtr = buffer; + + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + if (yOffset) { + yOffset = 8 - yOffset; + drawBit = ~(0xFF >> (yOffset)); + + if (length < yOffset) { + drawBit &= (0xFF >> (yOffset - length)); + } + + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + + if (length < yOffset) return; + + length -= yOffset; + bufferPtr += this->width(); + } + + if (length >= 8) { + switch (color) { + case WHITE: + case BLACK: + drawBit = (color == WHITE) ? 0xFF : 0x00; + do { + *bufferPtr = drawBit; + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + case INVERSE: + do { + *bufferPtr = ~(*bufferPtr); + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + } + } + + if (length > 0) { + drawBit = (1 << (length & 7)) - 1; + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + } +} + +void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) { + uint16_t radius = height / 2; + uint16_t xRadius = x + radius; + uint16_t yRadius = y + radius; + uint16_t doubleRadius = 2 * radius; + uint16_t innerRadius = radius - 2; + + setColor(WHITE); + drawCircleQuads(xRadius, yRadius, radius, 0b00000110); + drawHorizontalLine(xRadius, y, width - doubleRadius + 1); + drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1); + drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001); + + uint16_t maxProgressWidth = (width - doubleRadius + 1) * progress / 100; + + fillCircle(xRadius, yRadius, innerRadius); + fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3); + fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius); +} + +void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *image) { + drawInternal(xMove, yMove, width, height, image, 0, 0); +} + +void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *xbm) { + int16_t widthInXbm = (width + 7) / 8; + uint8_t data = 0; + + for(int16_t y = 0; y < height; y++) { + for(int16_t x = 0; x < width; x++ ) { + if (x & 7) { + data >>= 1; // Move a bit + } else { // Read new data every 8 bit + data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm); + } + // if there is a bit draw it + if (data & 0x01) { + setPixel(xMove + x, yMove + y); + } + } + } +} + +void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES; + + uint8_t cursorX = 0; + uint8_t cursorY = 0; + + switch (textAlignment) { + case TEXT_ALIGN_CENTER_BOTH: + yMove -= textHeight >> 1; + // Fallthrough + case TEXT_ALIGN_CENTER: + xMove -= textWidth >> 1; // divide by 2 + break; + case TEXT_ALIGN_RIGHT: + xMove -= textWidth; + break; + case TEXT_ALIGN_LEFT: + break; + } + + // Don't draw anything if it is not on the screen. + if (xMove + textWidth < 0 || xMove > this->width() ) {return;} + if (yMove + textHeight < 0 || yMove > this->width() ) {return;} + + for (uint16_t j = 0; j < textLength; j++) { + int16_t xPos = xMove + cursorX; + int16_t yPos = yMove + cursorY; + + byte code = text[j]; + if (code >= firstChar) { + byte charCode = code - firstChar; + + // 4 Bytes per char code + byte msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress + byte lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / + byte charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size + byte currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width + + // Test if the char is drawable + if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { + // Get the position of the char data + uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar); + drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize); + } + + cursorX += currentCharWidth; + } + } +} + + +void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + // char* text must be freed! + char* text = utf8ascii(strUser); + + uint16_t yOffset = 0; + // If the string should be centered vertically too + // we need to now how heigh the string is. + if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { + uint16_t lb = 0; + // Find number of linebreaks in text + for (uint16_t i=0;text[i] != 0; i++) { + lb += (text[i] == 10); + } + // Calculate center + yOffset = (lb * lineHeight) / 2; + } + + uint16_t line = 0; + char* textPart = strtok(text,"\n"); + while (textPart != NULL) { + uint16_t length = strlen(textPart); + drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); + textPart = strtok(NULL, "\n"); + } + free(text); +} +//void OLEDDisplay::drawdata(int16_t xMove, int16_t yMove, int16_t Num) { +// uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); +// unsigned char c = 0,i = 0,j = 0,ch[3]; +// String strUser; +// +// ch[0] = Num/100 + 48;//锟斤拷锟斤拷十锟斤拷锟狡碉拷48锟斤拷为锟剿革拷Num锟斤拷锟斤拷ASCLL锟斤拷母锟�4位0011 0000锟斤拷 +// ch[1] = Num%100/10 + 48; +// ch[2] = Num%10 + 48; +// +// if(ch[0] == 48) //锟斤拷锟节帮拷锟斤拷锟斤拷每位为"0"时锟斤拷锟斤拷煽崭瘢锟斤拷锟斤拷锟绞撅拷锟� +// { +// ch[0] = 32; +// if(ch[1] == 48) +// { +// ch[1] = 32; +// if(ch[2] == 48) +// { +// ch[2] = 32; +// } +// else{ch[2] = Num%10 + 48;} +// } +// else{ch[1] = Num%100/10 + 48;} +// } +// else {ch[0] = Num/100 + 48;} +// +// // char* text must be freed! +// char* text = utf8ascii(strUser); +// +// uint16_t yOffset = 0; +// // If the string should be centered vertically too +// // we need to now how heigh the string is. +// if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { +// uint16_t lb = 0; +// // Find number of linebreaks in text +// for (uint16_t i=0;text[i] != 0; i++) { +// lb += (text[i] == 10); +// } +// // Calculate center +// yOffset = (lb * lineHeight) / 2; +// } +// +// uint16_t line = 0; +// char* textPart = strtok(text,"\n"); +// while (textPart != NULL) { +// uint16_t length = strlen(textPart); +// drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); +// textPart = strtok(NULL, "\n"); +// } +// free(text); +//} + +void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + char* text = utf8ascii(strUser); + + uint16_t length = strlen(text); + uint16_t lastDrawnPos = 0; + uint16_t lineNumber = 0; + uint16_t strWidth = 0; + + uint16_t preferredBreakpoint = 0; + uint16_t widthAtBreakpoint = 0; + + for (uint16_t i = 0; i < length; i++) { + strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + + // Always try to break on a space or dash + if (text[i] == ' ' || text[i]== '-') { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + + if (strWidth >= maxLineWidth) { + if (preferredBreakpoint == 0) { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint); + lastDrawnPos = preferredBreakpoint + 1; + // It is possible that we did not draw all letters to i so we need + // to account for the width of the chars from `i - preferredBreakpoint` + // by calculating the width we did not draw yet. + strWidth = strWidth - widthAtBreakpoint; + preferredBreakpoint = 0; + } + } + + // Draw last part if needed + if (lastDrawnPos < length) { + drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos)); + } + + free(text); +} + +uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + + uint16_t stringWidth = 0; + uint16_t maxWidth = 0; + + while (length--) { + stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + if (text[length] == 10) { + maxWidth = max(maxWidth, stringWidth); + stringWidth = 0; + } + } + + return max(maxWidth, stringWidth); +} + +uint16_t OLEDDisplay::getStringWidth(String strUser) { + char* text = utf8ascii(strUser); + uint16_t length = strlen(text); + uint16_t width = getStringWidth(text, length); + free(text); + return width; +} + +void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) { + this->textAlignment = textAlignment; +} + +void OLEDDisplay::setFont(const uint8_t *fontData) { + this->fontData = fontData; +} + +void OLEDDisplay::displayOn(void) { + sendCommand(DISPLAYON); +} + +void OLEDDisplay::displayOff(void) { + sendCommand(DISPLAYOFF); +} + +void OLEDDisplay::invertDisplay(void) { + sendCommand(INVERTDISPLAY); +} + +void OLEDDisplay::normalDisplay(void) { + sendCommand(NORMALDISPLAY); +} + +void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect) { + sendCommand(SETPRECHARGE); //0xD9 + sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F + sendCommand(SETCONTRAST); + sendCommand(contrast); // 0-255 + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(comdetect); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(DISPLAYON); +} + +void OLEDDisplay::setBrightness(uint8_t brightness) { + uint8_t contrast = brightness; + if (brightness < 128) { + // Magic values to get a smooth/ step-free transition + contrast = brightness * 1.171; + } else { + contrast = brightness * 1.171 - 43; + } + + uint8_t precharge = 241; + if (brightness == 0) { + precharge = 0; + } + uint8_t comdetect = brightness / 8; + + setContrast(contrast, precharge, comdetect); +} + +void OLEDDisplay::resetOrientation() { + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); //Reset screen rotation or mirroring +} + +void OLEDDisplay::flipScreenVertically() { + sendCommand(SEGREMAP | 0x01); + sendCommand(COMSCANDEC); //Rotate screen 180 Deg +} + +void OLEDDisplay::mirrorScreen() { + sendCommand(SEGREMAP); + sendCommand(COMSCANDEC); //Mirror screen +} + +void OLEDDisplay::clear(void) { + memset(buffer, 0, displayBufferSize); +} + +void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + // Always align left + setTextAlignment(TEXT_ALIGN_LEFT); + + // State values + uint16_t length = 0; + uint16_t line = 0; + uint16_t lastPos = 0; + + for (uint16_t i=0;ilogBufferFilled;i++){ + // Everytime we have a \n print + if (this->logBuffer[i] == 10) { + length++; + // Draw string on line `line` from lastPos to length + // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT + drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0); + // Remember last pos + lastPos = i; + // Reset length + length = 0; + } else { + // Count chars until next linebreak + length++; + } + } + // Draw the remaining string + if (length > 0) { + drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0); + } +} + +uint16_t OLEDDisplay::getWidth(void) { + return displayWidth; +} + +uint16_t OLEDDisplay::getHeight(void) { + return displayHeight; +} + +bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){ + if (logBuffer != NULL) free(logBuffer); + uint16_t size = lines * chars; + if (size > 0) { + this->logBufferLine = 0; // Lines printed + this->logBufferFilled = 0; // Nothing stored yet + this->logBufferMaxLines = lines; // Lines max printable + this->logBufferSize = size; // Total number of characters the buffer can hold + this->logBuffer = (char *) malloc(size * sizeof(uint8_t)); + if(!this->logBuffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n"); + return false; + } + } + return true; +} + +size_t OLEDDisplay::write(uint8_t c) { + if (this->logBufferSize > 0) { + // Don't waste space on \r\n line endings, dropping \r + if (c == 13) return 1; + + // convert UTF-8 character to font table index + c = (this->fontTableLookupFunction)(c); + // drop unknown character + if (c == 0) return 1; + + bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines; + bool bufferNotFull = this->logBufferFilled < this->logBufferSize; + + // Can we write to the buffer? + if (bufferNotFull && maxLineNotReached) { + this->logBuffer[logBufferFilled] = c; + this->logBufferFilled++; + // Keep track of lines written + if (c == 10) this->logBufferLine++; + } else { + // Max line number is reached + if (!maxLineNotReached) this->logBufferLine--; + + // Find the end of the first line + uint16_t firstLineEnd = 0; + for (uint16_t i=0;ilogBufferFilled;i++) { + if (this->logBuffer[i] == 10){ + // Include last char too + firstLineEnd = i + 1; + break; + } + } + // If there was a line ending + if (firstLineEnd > 0) { + // Calculate the new logBufferFilled value + this->logBufferFilled = logBufferFilled - firstLineEnd; + // Now we move the lines infront of the buffer + memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled); + } else { + // Let's reuse the buffer if it was full + if (!bufferNotFull) { + this->logBufferFilled = 0; + }// else { + // Nothing to do here + //} + } + write(c); + } + } + // We are always writing all uint8_t to the buffer + return 1; +} + +size_t OLEDDisplay::write(const char* str) { + if (str == NULL) return 0; + size_t length = strlen(str); + for (size_t i = 0; i < length; i++) { + write(str[i]); + } + return length; +} + +// Private functions +void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g) { + this->geometry = g; + if (g == GEOMETRY_128_64) { + this->displayWidth = 128; + this->displayHeight = 64; + } else if (g == GEOMETRY_128_32) { + this->displayWidth = 128; + this->displayHeight = 32; + } else if (g == GEOMETRY_64_32) { + this->displayWidth = 64; + this->displayHeight = 32; + } + this->displayBufferSize = displayWidth*displayHeight/8; +} + +void OLEDDisplay::sendInitCommands(void) { + sendCommand(DISPLAYOFF); + sendCommand(SETDISPLAYCLOCKDIV); + sendCommand(0xF0); // Increase speed of the display max ~96Hz + sendCommand(SETMULTIPLEX); + sendCommand(this->height() - 1); + sendCommand(SETDISPLAYOFFSET); + sendCommand(0x00); + sendCommand(SETSTARTLINE); + sendCommand(CHARGEPUMP); + sendCommand(0x14); + sendCommand(MEMORYMODE); + sendCommand(0x00); + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); + sendCommand(SETCOMPINS); + + if ((geometry == GEOMETRY_128_64) || (geometry == GEOMETRY_64_32)) { + sendCommand(0x12); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x02); + } + + sendCommand(SETCONTRAST); + + if ((geometry == GEOMETRY_128_64) || (geometry == GEOMETRY_64_32)) { + sendCommand(0xCF); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x8F); + } + + sendCommand(SETPRECHARGE); + sendCommand(0xF1); + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(0x40); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(0x2e); // stop scroll + sendCommand(DISPLAYON); +} + +void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) { + if (width < 0 || height < 0) return; + if (yMove + height < 0 || yMove > this->height()) return; + if (xMove + width < 0 || xMove > this->width()) return; + + uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0) + int8_t yOffset = yMove & 7; + + bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData; + + int16_t initYMove = yMove; + int8_t initYOffset = yOffset; + + + for (uint16_t i = 0; i < bytesInData; i++) { + + // Reset if next horizontal drawing phase is started. + if ( i % rasterHeight == 0) { + yMove = initYMove; + yOffset = initYOffset; + } + + byte currentByte = pgm_read_byte(data + offset + i); + + int16_t xPos = xMove + (i / rasterHeight); + int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width(); + +// int16_t yScreenPos = yMove + yOffset; + int16_t dataPos = xPos + yPos; + + if (dataPos >= 0 && dataPos < displayBufferSize && + xPos >= 0 && xPos < this->width() ) { + + if (yOffset >= 0) { + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte << yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break; + } + + if (dataPos < (displayBufferSize - this->width())) { + switch (this->color) { + case WHITE: buffer[dataPos + this->width()] |= currentByte >> (8 - yOffset); break; + case BLACK: buffer[dataPos + this->width()] &= ~(currentByte >> (8 - yOffset)); break; + case INVERSE: buffer[dataPos + this->width()] ^= currentByte >> (8 - yOffset); break; + } + } + } else { + // Make new offset position + yOffset = -yOffset; + + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte >> yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break; + } + + // Prepare for next iteration by moving one block up + yMove -= 8; + + // and setting the new yOffset + yOffset = 8 - yOffset; + } + + yield(); + } + } +} + +// You need to free the char! +char* OLEDDisplay::utf8ascii(String str) { + uint16_t k = 0; + uint16_t length = str.length() + 1; + + // Copy the string into a char array + char* s = (char*) malloc(length * sizeof(char)); + if(!s) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n"); + return (char*) str.c_str(); + } + str.toCharArray(s, length); + + length--; + + for (uint16_t i=0; i < length; i++) { + char c = (this->fontTableLookupFunction)(s[i]); + if (c!=0) { + s[k++]=c; + } + } + + s[k]=0; + + // This will leak 's' be sure to free it in the calling function. + return s; +} + +void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { + this->fontTableLookupFunction = function; +} + + diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplay.h b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplay.h new file mode 100644 index 0000000..49c3a6b --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplay.h @@ -0,0 +1,335 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAY_h +#define OLEDDISPLAY_h + +#include +#include "OLEDDisplayFonts.h" + +//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAY +#define DEBUG_OLEDDISPLAY(...) +#endif + +// Use DOUBLE BUFFERING by default +#ifndef OLEDDISPLAY_REDUCE_MEMORY +#define OLEDDISPLAY_DOUBLE_BUFFER +#endif + +// Header Values +#define JUMPTABLE_BYTES 4 + +#define JUMPTABLE_LSB 1 +#define JUMPTABLE_SIZE 2 +#define JUMPTABLE_WIDTH 3 +#define JUMPTABLE_START 4 + +#define WIDTH_POS 0 +#define HEIGHT_POS 1 +#define FIRST_CHAR_POS 2 +#define CHAR_NUM_POS 3 + + +// Display commands +#define CHARGEPUMP 0x8D +#define COLUMNADDR 0x21 +#define COMSCANDEC 0xC8 +#define COMSCANINC 0xC0 +#define DISPLAYALLON 0xA5 +#define DISPLAYALLON_RESUME 0xA4 +#define DISPLAYOFF 0xAE +#define DISPLAYON 0xAF +#define EXTERNALVCC 0x1 +#define INVERTDISPLAY 0xA7 +#define MEMORYMODE 0x20 +#define NORMALDISPLAY 0xA6 +#define PAGEADDR 0x22 +#define SEGREMAP 0xA0 +#define SETCOMPINS 0xDA +#define SETCONTRAST 0x81 +#define SETDISPLAYCLOCKDIV 0xD5 +#define SETDISPLAYOFFSET 0xD3 +#define SETHIGHCOLUMN 0x10 +#define SETLOWCOLUMN 0x00 +#define SETMULTIPLEX 0xA8 +#define SETPRECHARGE 0xD9 +#define SETSEGMENTREMAP 0xA1 +#define SETSTARTLINE 0x40 +#define SETVCOMDETECT 0xDB +#define SWITCHCAPVCC 0x2 + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#endif + +enum OLEDDISPLAY_COLOR { + BLACK = 0, + WHITE = 1, + INVERSE = 2 +}; + +enum OLEDDISPLAY_TEXT_ALIGNMENT { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_RIGHT = 1, + TEXT_ALIGN_CENTER = 2, + TEXT_ALIGN_CENTER_BOTH = 3 +}; + + +enum OLEDDISPLAY_GEOMETRY { + GEOMETRY_128_64 = 0, + GEOMETRY_128_32 = 1, + GEOMETRY_64_32 = 2 //Wireless Stick +}; + +typedef byte (*FontTableLookupFunction)(const byte ch); + + +class OLEDDisplay : public Print { + + public: + virtual ~OLEDDisplay(); + + uint16_t width(void) const { return displayWidth; }; + uint16_t height(void) const { return displayHeight; }; + + // Initialize the display + bool init(); + + // Free the memory used by the display + void end(); + + void sleep(); + + void wakeup(); + + // Cycle through the initialization + void resetDisplay(uint8_t rstPin); + + /* Drawing functions */ + // Sets the color of all pixel operations + void setColor(OLEDDISPLAY_COLOR color); + + // Returns the current color. + OLEDDISPLAY_COLOR getColor(); + + // Draw a pixel at given position + void setPixel(int16_t x, int16_t y); + + // Draw a line from position 0 to position 1 + void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + + // Draw the border of a rectangle at the given location + void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Fill the rectangle + void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Draw the border of a circle + void drawCircle(int16_t x, int16_t y, int16_t radius); + + // Draw all Quadrants specified in the quads bit mask + void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads); + + // Fill circle + void fillCircle(int16_t x, int16_t y, int16_t radius); + + // Draw a line horizontally + void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + + // Draw a line vertically + void drawVerticalLine(int16_t x, int16_t y, int16_t length); + + // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is + // a unsigned byte value between 0 and 100 + void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + + // Draw a bitmap in the internal image format + void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + + // Draw a XBM + void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm); + + /* Text functions */ + + // Draws a string at the given location + void drawString(int16_t x, int16_t y, String text); + + // Draws a String with a maximum width at the given location. + // If the given String is wider than the specified width + // The text will be wrapped to the next line at a space or dash + void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text); + + // Returns the width of the const char* with the current + // font settings + uint16_t getStringWidth(const char* text, uint16_t length); + + // Convencience method for the const char version + uint16_t getStringWidth(String text); + + // Specifies relative to which anchor point + // the text is rendered. Available constants: + // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH + void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + + // Sets the current font. Available default fonts + // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 + void setFont(const uint8_t *fontData); + + // Set the function that will convert utf-8 to font table index + void setFontTableLookupFunction(FontTableLookupFunction function); + + /* Display functions */ + + // Turn the display on + void displayOn(void); + + // Turn the display offs + void displayOff(void); + + // Inverted display mode + void invertDisplay(void); + + // Normal display mode + void normalDisplay(void); + + // Set display contrast + // really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 + // normal brightness & contrast: contrast = 100 + void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + + // Convenience method to access + void setBrightness(uint8_t); + + // Reset display rotation or mirroring + void resetOrientation(); + + // Turn the display upside down + void flipScreenVertically(); + + // Mirror the display (to be used in a mirror or as a projector) + void mirrorScreen(); + + // Write the buffer to the display memory + virtual void display(void) = 0; + + // Clear the local pixel buffer + void clear(void); + + // Log buffer implementation + + // This will define the lines and characters you can + // print to the screen. When you exeed the buffer size (lines * chars) + // the output may be truncated due to the size constraint. + bool setLogBuffer(uint16_t lines, uint16_t chars); + + // Draw the log buffer at position (x, y) + void drawLogBuffer(uint16_t x, uint16_t y); + + // Get screen geometry + uint16_t getWidth(void); + uint16_t getHeight(void); + + // Implement needed function to be compatible with Print class + size_t write(uint8_t c); + size_t write(const char* s); + + uint8_t *buffer = NULL; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t *buffer_back = NULL; + #endif + + protected: + + OLEDDISPLAY_GEOMETRY geometry = GEOMETRY_128_64; + + uint16_t displayWidth = 128; + uint16_t displayHeight = 64; + uint16_t displayBufferSize = 1024; + + // Set the correct height, width and buffer for the geometry + void setGeometry(OLEDDISPLAY_GEOMETRY g); + + OLEDDISPLAY_TEXT_ALIGNMENT textAlignment = TEXT_ALIGN_LEFT; + OLEDDISPLAY_COLOR color = WHITE; + + const uint8_t *fontData = ArialMT_Plain_10; + + // State values for logBuffer + uint16_t logBufferSize = 0; + uint16_t logBufferFilled = 0; + uint16_t logBufferLine = 0; + uint16_t logBufferMaxLines = 0; + char *logBuffer = NULL; + + // Send a command to the display (low level function) + virtual void sendCommand(uint8_t com) {(void)com;}; + + // Connect to the display + virtual bool connect() { return false; }; + + // Send all the init commands + void sendInitCommands(); + + // converts utf8 characters to extended ascii + char* utf8ascii(String s); + + void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); + + void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth); + + // UTF-8 to font table index converter + // Code form http://playground.arduino.cc/Main/Utf8ascii + FontTableLookupFunction fontTableLookupFunction = [](const byte ch) { + static uint8_t LASTCHAR; + + if (ch < 128) { // Standard ASCII-set 0..0x7F handling + LASTCHAR = 0; + return ch; + } + + uint8_t last = LASTCHAR; // get last char + LASTCHAR = ch; + + switch (last) { // conversion depnding on first UTF8-character + case 0xC2: return (uint8_t) ch; + case 0xC3: return (uint8_t) (ch | 0xC0); + case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol + } + + return (uint8_t) 0; // otherwise: return zero, if character has to be ignored + }; +}; + +#endif + diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayFonts.h b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayFonts.h new file mode 100644 index 0000000..3544edb --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayFonts.h @@ -0,0 +1,1274 @@ +#ifndef OLEDDISPLAYFONTS_h +#define OLEDDISPLAYFONTS_h + +const uint8_t ArialMT_Plain_10[] PROGMEM = { + 0x0A, // Width: 10 + 0x0D, // Height: 13 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x03, // 32:65535 + 0x00, 0x00, 0x04, 0x03, // 33:0 + 0x00, 0x04, 0x05, 0x04, // 34:4 + 0x00, 0x09, 0x09, 0x06, // 35:9 + 0x00, 0x12, 0x0A, 0x06, // 36:18 + 0x00, 0x1C, 0x10, 0x09, // 37:28 + 0x00, 0x2C, 0x0E, 0x07, // 38:44 + 0x00, 0x3A, 0x01, 0x02, // 39:58 + 0x00, 0x3B, 0x06, 0x03, // 40:59 + 0x00, 0x41, 0x06, 0x03, // 41:65 + 0x00, 0x47, 0x05, 0x04, // 42:71 + 0x00, 0x4C, 0x09, 0x06, // 43:76 + 0x00, 0x55, 0x04, 0x03, // 44:85 + 0x00, 0x59, 0x03, 0x03, // 45:89 + 0x00, 0x5C, 0x04, 0x03, // 46:92 + 0x00, 0x60, 0x05, 0x03, // 47:96 + 0x00, 0x65, 0x0A, 0x06, // 48:101 + 0x00, 0x6F, 0x08, 0x06, // 49:111 + 0x00, 0x77, 0x0A, 0x06, // 50:119 + 0x00, 0x81, 0x0A, 0x06, // 51:129 + 0x00, 0x8B, 0x0B, 0x06, // 52:139 + 0x00, 0x96, 0x0A, 0x06, // 53:150 + 0x00, 0xA0, 0x0A, 0x06, // 54:160 + 0x00, 0xAA, 0x09, 0x06, // 55:170 + 0x00, 0xB3, 0x0A, 0x06, // 56:179 + 0x00, 0xBD, 0x0A, 0x06, // 57:189 + 0x00, 0xC7, 0x04, 0x03, // 58:199 + 0x00, 0xCB, 0x04, 0x03, // 59:203 + 0x00, 0xCF, 0x0A, 0x06, // 60:207 + 0x00, 0xD9, 0x09, 0x06, // 61:217 + 0x00, 0xE2, 0x09, 0x06, // 62:226 + 0x00, 0xEB, 0x0B, 0x06, // 63:235 + 0x00, 0xF6, 0x14, 0x0A, // 64:246 + 0x01, 0x0A, 0x0E, 0x07, // 65:266 + 0x01, 0x18, 0x0C, 0x07, // 66:280 + 0x01, 0x24, 0x0C, 0x07, // 67:292 + 0x01, 0x30, 0x0B, 0x07, // 68:304 + 0x01, 0x3B, 0x0C, 0x07, // 69:315 + 0x01, 0x47, 0x09, 0x06, // 70:327 + 0x01, 0x50, 0x0D, 0x08, // 71:336 + 0x01, 0x5D, 0x0C, 0x07, // 72:349 + 0x01, 0x69, 0x04, 0x03, // 73:361 + 0x01, 0x6D, 0x08, 0x05, // 74:365 + 0x01, 0x75, 0x0E, 0x07, // 75:373 + 0x01, 0x83, 0x0C, 0x06, // 76:387 + 0x01, 0x8F, 0x10, 0x08, // 77:399 + 0x01, 0x9F, 0x0C, 0x07, // 78:415 + 0x01, 0xAB, 0x0E, 0x08, // 79:427 + 0x01, 0xB9, 0x0B, 0x07, // 80:441 + 0x01, 0xC4, 0x0E, 0x08, // 81:452 + 0x01, 0xD2, 0x0C, 0x07, // 82:466 + 0x01, 0xDE, 0x0C, 0x07, // 83:478 + 0x01, 0xEA, 0x0B, 0x06, // 84:490 + 0x01, 0xF5, 0x0C, 0x07, // 85:501 + 0x02, 0x01, 0x0D, 0x07, // 86:513 + 0x02, 0x0E, 0x11, 0x09, // 87:526 + 0x02, 0x1F, 0x0E, 0x07, // 88:543 + 0x02, 0x2D, 0x0D, 0x07, // 89:557 + 0x02, 0x3A, 0x0C, 0x06, // 90:570 + 0x02, 0x46, 0x06, 0x03, // 91:582 + 0x02, 0x4C, 0x06, 0x03, // 92:588 + 0x02, 0x52, 0x04, 0x03, // 93:594 + 0x02, 0x56, 0x09, 0x05, // 94:598 + 0x02, 0x5F, 0x0C, 0x06, // 95:607 + 0x02, 0x6B, 0x03, 0x03, // 96:619 + 0x02, 0x6E, 0x0A, 0x06, // 97:622 + 0x02, 0x78, 0x0A, 0x06, // 98:632 + 0x02, 0x82, 0x0A, 0x05, // 99:642 + 0x02, 0x8C, 0x0A, 0x06, // 100:652 + 0x02, 0x96, 0x0A, 0x06, // 101:662 + 0x02, 0xA0, 0x05, 0x03, // 102:672 + 0x02, 0xA5, 0x0A, 0x06, // 103:677 + 0x02, 0xAF, 0x0A, 0x06, // 104:687 + 0x02, 0xB9, 0x04, 0x02, // 105:697 + 0x02, 0xBD, 0x04, 0x02, // 106:701 + 0x02, 0xC1, 0x08, 0x05, // 107:705 + 0x02, 0xC9, 0x04, 0x02, // 108:713 + 0x02, 0xCD, 0x10, 0x08, // 109:717 + 0x02, 0xDD, 0x0A, 0x06, // 110:733 + 0x02, 0xE7, 0x0A, 0x06, // 111:743 + 0x02, 0xF1, 0x0A, 0x06, // 112:753 + 0x02, 0xFB, 0x0A, 0x06, // 113:763 + 0x03, 0x05, 0x05, 0x03, // 114:773 + 0x03, 0x0A, 0x08, 0x05, // 115:778 + 0x03, 0x12, 0x06, 0x03, // 116:786 + 0x03, 0x18, 0x0A, 0x06, // 117:792 + 0x03, 0x22, 0x09, 0x05, // 118:802 + 0x03, 0x2B, 0x0E, 0x07, // 119:811 + 0x03, 0x39, 0x0A, 0x05, // 120:825 + 0x03, 0x43, 0x09, 0x05, // 121:835 + 0x03, 0x4C, 0x0A, 0x05, // 122:844 + 0x03, 0x56, 0x06, 0x03, // 123:854 + 0x03, 0x5C, 0x04, 0x03, // 124:860 + 0x03, 0x60, 0x05, 0x03, // 125:864 + 0x03, 0x65, 0x09, 0x06, // 126:869 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 128:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 129:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 130:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 131:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 132:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 133:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 134:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 135:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 136:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 137:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 138:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 139:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 140:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 141:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 142:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 143:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 144:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 145:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 146:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 147:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 148:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 149:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 150:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 151:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 152:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 153:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 154:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 155:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 156:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 157:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 158:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 159:65535 + 0xFF, 0xFF, 0x00, 0x03, // 160:65535 + 0x03, 0x6E, 0x04, 0x03, // 161:878 + 0x03, 0x72, 0x0A, 0x06, // 162:882 + 0x03, 0x7C, 0x0C, 0x06, // 163:892 + 0x03, 0x88, 0x0A, 0x06, // 164:904 + 0x03, 0x92, 0x0A, 0x06, // 165:914 + 0x03, 0x9C, 0x04, 0x03, // 166:924 + 0x03, 0xA0, 0x0A, 0x06, // 167:928 + 0x03, 0xAA, 0x05, 0x03, // 168:938 + 0x03, 0xAF, 0x0D, 0x07, // 169:943 + 0x03, 0xBC, 0x07, 0x04, // 170:956 + 0x03, 0xC3, 0x0A, 0x06, // 171:963 + 0x03, 0xCD, 0x09, 0x06, // 172:973 + 0x03, 0xD6, 0x03, 0x03, // 173:982 + 0x03, 0xD9, 0x0D, 0x07, // 174:985 + 0x03, 0xE6, 0x0B, 0x06, // 175:998 + 0x03, 0xF1, 0x07, 0x04, // 176:1009 + 0x03, 0xF8, 0x0A, 0x05, // 177:1016 + 0x04, 0x02, 0x05, 0x03, // 178:1026 + 0x04, 0x07, 0x05, 0x03, // 179:1031 + 0x04, 0x0C, 0x05, 0x03, // 180:1036 + 0x04, 0x11, 0x0A, 0x06, // 181:1041 + 0x04, 0x1B, 0x09, 0x05, // 182:1051 + 0x04, 0x24, 0x03, 0x03, // 183:1060 + 0x04, 0x27, 0x06, 0x03, // 184:1063 + 0x04, 0x2D, 0x05, 0x03, // 185:1069 + 0x04, 0x32, 0x07, 0x04, // 186:1074 + 0x04, 0x39, 0x0A, 0x06, // 187:1081 + 0x04, 0x43, 0x10, 0x08, // 188:1091 + 0x04, 0x53, 0x10, 0x08, // 189:1107 + 0x04, 0x63, 0x10, 0x08, // 190:1123 + 0x04, 0x73, 0x0A, 0x06, // 191:1139 + 0x04, 0x7D, 0x0E, 0x07, // 192:1149 + 0x04, 0x8B, 0x0E, 0x07, // 193:1163 + 0x04, 0x99, 0x0E, 0x07, // 194:1177 + 0x04, 0xA7, 0x0E, 0x07, // 195:1191 + 0x04, 0xB5, 0x0E, 0x07, // 196:1205 + 0x04, 0xC3, 0x0E, 0x07, // 197:1219 + 0x04, 0xD1, 0x12, 0x0A, // 198:1233 + 0x04, 0xE3, 0x0C, 0x07, // 199:1251 + 0x04, 0xEF, 0x0C, 0x07, // 200:1263 + 0x04, 0xFB, 0x0C, 0x07, // 201:1275 + 0x05, 0x07, 0x0C, 0x07, // 202:1287 + 0x05, 0x13, 0x0C, 0x07, // 203:1299 + 0x05, 0x1F, 0x05, 0x03, // 204:1311 + 0x05, 0x24, 0x04, 0x03, // 205:1316 + 0x05, 0x28, 0x04, 0x03, // 206:1320 + 0x05, 0x2C, 0x05, 0x03, // 207:1324 + 0x05, 0x31, 0x0B, 0x07, // 208:1329 + 0x05, 0x3C, 0x0C, 0x07, // 209:1340 + 0x05, 0x48, 0x0E, 0x08, // 210:1352 + 0x05, 0x56, 0x0E, 0x08, // 211:1366 + 0x05, 0x64, 0x0E, 0x08, // 212:1380 + 0x05, 0x72, 0x0E, 0x08, // 213:1394 + 0x05, 0x80, 0x0E, 0x08, // 214:1408 + 0x05, 0x8E, 0x0A, 0x06, // 215:1422 + 0x05, 0x98, 0x0D, 0x08, // 216:1432 + 0x05, 0xA5, 0x0C, 0x07, // 217:1445 + 0x05, 0xB1, 0x0C, 0x07, // 218:1457 + 0x05, 0xBD, 0x0C, 0x07, // 219:1469 + 0x05, 0xC9, 0x0C, 0x07, // 220:1481 + 0x05, 0xD5, 0x0D, 0x07, // 221:1493 + 0x05, 0xE2, 0x0B, 0x07, // 222:1506 + 0x05, 0xED, 0x0C, 0x06, // 223:1517 + 0x05, 0xF9, 0x0A, 0x06, // 224:1529 + 0x06, 0x03, 0x0A, 0x06, // 225:1539 + 0x06, 0x0D, 0x0A, 0x06, // 226:1549 + 0x06, 0x17, 0x0A, 0x06, // 227:1559 + 0x06, 0x21, 0x0A, 0x06, // 228:1569 + 0x06, 0x2B, 0x0A, 0x06, // 229:1579 + 0x06, 0x35, 0x10, 0x09, // 230:1589 + 0x06, 0x45, 0x0A, 0x05, // 231:1605 + 0x06, 0x4F, 0x0A, 0x06, // 232:1615 + 0x06, 0x59, 0x0A, 0x06, // 233:1625 + 0x06, 0x63, 0x0A, 0x06, // 234:1635 + 0x06, 0x6D, 0x0A, 0x06, // 235:1645 + 0x06, 0x77, 0x05, 0x03, // 236:1655 + 0x06, 0x7C, 0x04, 0x03, // 237:1660 + 0x06, 0x80, 0x05, 0x03, // 238:1664 + 0x06, 0x85, 0x05, 0x03, // 239:1669 + 0x06, 0x8A, 0x0A, 0x06, // 240:1674 + 0x06, 0x94, 0x0A, 0x06, // 241:1684 + 0x06, 0x9E, 0x0A, 0x06, // 242:1694 + 0x06, 0xA8, 0x0A, 0x06, // 243:1704 + 0x06, 0xB2, 0x0A, 0x06, // 244:1714 + 0x06, 0xBC, 0x0A, 0x06, // 245:1724 + 0x06, 0xC6, 0x0A, 0x06, // 246:1734 + 0x06, 0xD0, 0x09, 0x05, // 247:1744 + 0x06, 0xD9, 0x0A, 0x06, // 248:1753 + 0x06, 0xE3, 0x0A, 0x06, // 249:1763 + 0x06, 0xED, 0x0A, 0x06, // 250:1773 + 0x06, 0xF7, 0x0A, 0x06, // 251:1783 + 0x07, 0x01, 0x0A, 0x06, // 252:1793 + 0x07, 0x0B, 0x09, 0x05, // 253:1803 + 0x07, 0x14, 0x0A, 0x06, // 254:1812 + 0x07, 0x1E, 0x09, 0x05, // 255:1822 + + // Font Data: + 0x00,0x00,0xF8,0x02, // 33 + 0x38,0x00,0x00,0x00,0x38, // 34 + 0xA0,0x03,0xE0,0x00,0xB8,0x03,0xE0,0x00,0xB8, // 35 + 0x30,0x01,0x28,0x02,0xF8,0x07,0x48,0x02,0x90,0x01, // 36 + 0x00,0x00,0x30,0x00,0x48,0x00,0x30,0x03,0xC0,0x00,0xB0,0x01,0x48,0x02,0x80,0x01, // 37 + 0x80,0x01,0x50,0x02,0x68,0x02,0xA8,0x02,0x18,0x01,0x80,0x03,0x80,0x02, // 38 + 0x38, // 39 + 0xE0,0x03,0x10,0x04,0x08,0x08, // 40 + 0x08,0x08,0x10,0x04,0xE0,0x03, // 41 + 0x28,0x00,0x18,0x00,0x28, // 42 + 0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43 + 0x00,0x00,0x00,0x06, // 44 + 0x80,0x00,0x80, // 45 + 0x00,0x00,0x00,0x02, // 46 + 0x00,0x03,0xE0,0x00,0x18, // 47 + 0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 48 + 0x00,0x00,0x20,0x00,0x10,0x00,0xF8,0x03, // 49 + 0x10,0x02,0x08,0x03,0x88,0x02,0x48,0x02,0x30,0x02, // 50 + 0x10,0x01,0x08,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 51 + 0xC0,0x00,0xA0,0x00,0x90,0x00,0x88,0x00,0xF8,0x03,0x80, // 52 + 0x60,0x01,0x38,0x02,0x28,0x02,0x28,0x02,0xC8,0x01, // 53 + 0xF0,0x01,0x28,0x02,0x28,0x02,0x28,0x02,0xD0,0x01, // 54 + 0x08,0x00,0x08,0x03,0xC8,0x00,0x38,0x00,0x08, // 55 + 0xB0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 56 + 0x70,0x01,0x88,0x02,0x88,0x02,0x88,0x02,0xF0,0x01, // 57 + 0x00,0x00,0x20,0x02, // 58 + 0x00,0x00,0x20,0x06, // 59 + 0x00,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01, // 60 + 0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61 + 0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0x40, // 62 + 0x10,0x00,0x08,0x00,0x08,0x00,0xC8,0x02,0x48,0x00,0x30, // 63 + 0x00,0x00,0xC0,0x03,0x30,0x04,0xD0,0x09,0x28,0x0A,0x28,0x0A,0xC8,0x0B,0x68,0x0A,0x10,0x05,0xE0,0x04, // 64 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x88,0x00,0xB0,0x00,0xC0,0x01,0x00,0x02, // 65 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0xF0,0x01, // 66 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x10,0x01, // 67 + 0x00,0x00,0xF8,0x03,0x08,0x02,0x08,0x02,0x10,0x01,0xE0, // 68 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0x48,0x02, // 69 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x08, // 70 + 0x00,0x00,0xE0,0x00,0x10,0x01,0x08,0x02,0x48,0x02,0x50,0x01,0xC0, // 71 + 0x00,0x00,0xF8,0x03,0x40,0x00,0x40,0x00,0x40,0x00,0xF8,0x03, // 72 + 0x00,0x00,0xF8,0x03, // 73 + 0x00,0x03,0x00,0x02,0x00,0x02,0xF8,0x01, // 74 + 0x00,0x00,0xF8,0x03,0x80,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 75 + 0x00,0x00,0xF8,0x03,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 76 + 0x00,0x00,0xF8,0x03,0x30,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x30,0x00,0xF8,0x03, // 77 + 0x00,0x00,0xF8,0x03,0x30,0x00,0x40,0x00,0x80,0x01,0xF8,0x03, // 78 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 79 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x48,0x00,0x30, // 80 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x03,0x08,0x03,0xF0,0x02, // 81 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0xC8,0x00,0x30,0x03, // 82 + 0x00,0x00,0x30,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0x90,0x01, // 83 + 0x00,0x00,0x08,0x00,0x08,0x00,0xF8,0x03,0x08,0x00,0x08, // 84 + 0x00,0x00,0xF8,0x01,0x00,0x02,0x00,0x02,0x00,0x02,0xF8,0x01, // 85 + 0x08,0x00,0x70,0x00,0x80,0x01,0x00,0x02,0x80,0x01,0x70,0x00,0x08, // 86 + 0x18,0x00,0xE0,0x01,0x00,0x02,0xF0,0x01,0x08,0x00,0xF0,0x01,0x00,0x02,0xE0,0x01,0x18, // 87 + 0x00,0x02,0x08,0x01,0x90,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 88 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC0,0x03,0x20,0x00,0x10,0x00,0x08, // 89 + 0x08,0x03,0x88,0x02,0xC8,0x02,0x68,0x02,0x38,0x02,0x18,0x02, // 90 + 0x00,0x00,0xF8,0x0F,0x08,0x08, // 91 + 0x18,0x00,0xE0,0x00,0x00,0x03, // 92 + 0x08,0x08,0xF8,0x0F, // 93 + 0x40,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x40, // 94 + 0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08, // 95 + 0x08,0x00,0x10, // 96 + 0x00,0x00,0x00,0x03,0xA0,0x02,0xA0,0x02,0xE0,0x03, // 97 + 0x00,0x00,0xF8,0x03,0x20,0x02,0x20,0x02,0xC0,0x01, // 98 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0x40,0x01, // 99 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xF8,0x03, // 100 + 0x00,0x00,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 101 + 0x20,0x00,0xF0,0x03,0x28, // 102 + 0x00,0x00,0xC0,0x05,0x20,0x0A,0x20,0x0A,0xE0,0x07, // 103 + 0x00,0x00,0xF8,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 104 + 0x00,0x00,0xE8,0x03, // 105 + 0x00,0x08,0xE8,0x07, // 106 + 0xF8,0x03,0x80,0x00,0xC0,0x01,0x20,0x02, // 107 + 0x00,0x00,0xF8,0x03, // 108 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 109 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 110 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xC0,0x01, // 111 + 0x00,0x00,0xE0,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 112 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xE0,0x0F, // 113 + 0x00,0x00,0xE0,0x03,0x20, // 114 + 0x40,0x02,0xA0,0x02,0xA0,0x02,0x20,0x01, // 115 + 0x20,0x00,0xF8,0x03,0x20,0x02, // 116 + 0x00,0x00,0xE0,0x01,0x00,0x02,0x00,0x02,0xE0,0x03, // 117 + 0x20,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x20, // 118 + 0xE0,0x01,0x00,0x02,0xC0,0x01,0x20,0x00,0xC0,0x01,0x00,0x02,0xE0,0x01, // 119 + 0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x20,0x02, // 120 + 0x20,0x00,0xC0,0x09,0x00,0x06,0xC0,0x01,0x20, // 121 + 0x20,0x02,0x20,0x03,0xA0,0x02,0x60,0x02,0x20,0x02, // 122 + 0x80,0x00,0x78,0x0F,0x08,0x08, // 123 + 0x00,0x00,0xF8,0x0F, // 124 + 0x08,0x08,0x78,0x0F,0x80, // 125 + 0xC0,0x00,0x40,0x00,0xC0,0x00,0x80,0x00,0xC0, // 126 + 0x00,0x00,0xA0,0x0F, // 161 + 0x00,0x00,0xC0,0x01,0xA0,0x0F,0x78,0x02,0x40,0x01, // 162 + 0x40,0x02,0x70,0x03,0xC8,0x02,0x48,0x02,0x08,0x02,0x10,0x02, // 163 + 0x00,0x00,0xE0,0x01,0x20,0x01,0x20,0x01,0xE0,0x01, // 164 + 0x48,0x01,0x70,0x01,0xC0,0x03,0x70,0x01,0x48,0x01, // 165 + 0x00,0x00,0x38,0x0F, // 166 + 0xD0,0x04,0x28,0x09,0x48,0x09,0x48,0x0A,0x90,0x05, // 167 + 0x08,0x00,0x00,0x00,0x08, // 168 + 0xE0,0x00,0x10,0x01,0x48,0x02,0xA8,0x02,0xA8,0x02,0x10,0x01,0xE0, // 169 + 0x68,0x00,0x68,0x00,0x68,0x00,0x78, // 170 + 0x00,0x00,0x80,0x01,0x40,0x02,0x80,0x01,0x40,0x02, // 171 + 0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172 + 0x80,0x00,0x80, // 173 + 0xE0,0x00,0x10,0x01,0xE8,0x02,0x68,0x02,0xC8,0x02,0x10,0x01,0xE0, // 174 + 0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 175 + 0x00,0x00,0x38,0x00,0x28,0x00,0x38, // 176 + 0x40,0x02,0x40,0x02,0xF0,0x03,0x40,0x02,0x40,0x02, // 177 + 0x48,0x00,0x68,0x00,0x58, // 178 + 0x48,0x00,0x58,0x00,0x68, // 179 + 0x00,0x00,0x10,0x00,0x08, // 180 + 0x00,0x00,0xE0,0x0F,0x00,0x02,0x00,0x02,0xE0,0x03, // 181 + 0x70,0x00,0xF8,0x0F,0x08,0x00,0xF8,0x0F,0x08, // 182 + 0x00,0x00,0x40, // 183 + 0x00,0x00,0x00,0x14,0x00,0x18, // 184 + 0x00,0x00,0x10,0x00,0x78, // 185 + 0x30,0x00,0x48,0x00,0x48,0x00,0x30, // 186 + 0x00,0x00,0x40,0x02,0x80,0x01,0x40,0x02,0x80,0x01, // 187 + 0x00,0x00,0x10,0x02,0x78,0x01,0xC0,0x00,0x20,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 188 + 0x00,0x00,0x10,0x02,0x78,0x01,0x80,0x00,0x60,0x00,0x50,0x02,0x48,0x03,0xC0,0x02, // 189 + 0x48,0x00,0x58,0x00,0x68,0x03,0x80,0x00,0x60,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 190 + 0x00,0x00,0x00,0x06,0x00,0x09,0xA0,0x09,0x00,0x04, // 191 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 192 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 193 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 194 + 0x00,0x02,0xC2,0x01,0xB1,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 195 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x88,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 196 + 0x00,0x02,0xC0,0x01,0xBE,0x00,0x8A,0x00,0xBE,0x00,0xC0,0x01,0x00,0x02, // 197 + 0x00,0x03,0xC0,0x00,0xE0,0x00,0x98,0x00,0x88,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02, // 198 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x16,0x08,0x1A,0x10,0x01, // 199 + 0x00,0x00,0xF8,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 200 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x4A,0x02,0x49,0x02,0x48,0x02, // 201 + 0x00,0x00,0xFA,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 202 + 0x00,0x00,0xF8,0x03,0x4A,0x02,0x48,0x02,0x4A,0x02,0x48,0x02, // 203 + 0x00,0x00,0xF9,0x03,0x02, // 204 + 0x02,0x00,0xF9,0x03, // 205 + 0x01,0x00,0xFA,0x03, // 206 + 0x02,0x00,0xF8,0x03,0x02, // 207 + 0x40,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x10,0x01,0xE0, // 208 + 0x00,0x00,0xFA,0x03,0x31,0x00,0x42,0x00,0x81,0x01,0xF8,0x03, // 209 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x09,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 210 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x08,0x02,0xF0,0x01, // 211 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x0A,0x02,0xF0,0x01, // 212 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x09,0x02,0x0A,0x02,0x09,0x02,0xF0,0x01, // 213 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x08,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 214 + 0x10,0x01,0xA0,0x00,0xE0,0x00,0xA0,0x00,0x10,0x01, // 215 + 0x00,0x00,0xF0,0x02,0x08,0x03,0xC8,0x02,0x28,0x02,0x18,0x03,0xE8, // 216 + 0x00,0x00,0xF8,0x01,0x01,0x02,0x02,0x02,0x00,0x02,0xF8,0x01, // 217 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x00,0x02,0xF8,0x01, // 218 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x02,0x02,0xF8,0x01, // 219 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x00,0x02,0x02,0x02,0xF8,0x01, // 220 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC2,0x03,0x21,0x00,0x10,0x00,0x08, // 221 + 0x00,0x00,0xF8,0x03,0x10,0x01,0x10,0x01,0x10,0x01,0xE0, // 222 + 0x00,0x00,0xF0,0x03,0x08,0x01,0x48,0x02,0xB0,0x02,0x80,0x01, // 223 + 0x00,0x00,0x00,0x03,0xA4,0x02,0xA8,0x02,0xE0,0x03, // 224 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE0,0x03, // 225 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE8,0x03, // 226 + 0x00,0x00,0x08,0x03,0xA4,0x02,0xA8,0x02,0xE4,0x03, // 227 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA0,0x02,0xE8,0x03, // 228 + 0x00,0x00,0x00,0x03,0xAE,0x02,0xAA,0x02,0xEE,0x03, // 229 + 0x00,0x00,0x40,0x03,0xA0,0x02,0xA0,0x02,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 230 + 0x00,0x00,0xC0,0x01,0x20,0x16,0x20,0x1A,0x40,0x01, // 231 + 0x00,0x00,0xC0,0x01,0xA4,0x02,0xA8,0x02,0xC0,0x02, // 232 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC0,0x02, // 233 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC8,0x02, // 234 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA0,0x02,0xC8,0x02, // 235 + 0x00,0x00,0xE4,0x03,0x08, // 236 + 0x08,0x00,0xE4,0x03, // 237 + 0x08,0x00,0xE4,0x03,0x08, // 238 + 0x08,0x00,0xE0,0x03,0x08, // 239 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x38,0x02,0xE0,0x01, // 240 + 0x00,0x00,0xE8,0x03,0x24,0x00,0x28,0x00,0xC4,0x03, // 241 + 0x00,0x00,0xC0,0x01,0x24,0x02,0x28,0x02,0xC0,0x01, // 242 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC0,0x01, // 243 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC8,0x01, // 244 + 0x00,0x00,0xC8,0x01,0x24,0x02,0x28,0x02,0xC4,0x01, // 245 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x20,0x02,0xC8,0x01, // 246 + 0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247 + 0x00,0x00,0xC0,0x02,0xA0,0x03,0x60,0x02,0xA0,0x01, // 248 + 0x00,0x00,0xE0,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 249 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x04,0x02,0xE0,0x03, // 250 + 0x00,0x00,0xE8,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 251 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x00,0x02,0xE8,0x03, // 252 + 0x20,0x00,0xC0,0x09,0x08,0x06,0xC4,0x01,0x20, // 253 + 0x00,0x00,0xF8,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 254 + 0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20 // 255 +}; + +const uint8_t ArialMT_Plain_16[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32:65535 + 0x00, 0x00, 0x08, 0x04, // 33:0 + 0x00, 0x08, 0x0D, 0x06, // 34:8 + 0x00, 0x15, 0x1A, 0x09, // 35:21 + 0x00, 0x2F, 0x17, 0x09, // 36:47 + 0x00, 0x46, 0x26, 0x0E, // 37:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38:108 + 0x00, 0x89, 0x04, 0x03, // 39:137 + 0x00, 0x8D, 0x0C, 0x05, // 40:141 + 0x00, 0x99, 0x0B, 0x05, // 41:153 + 0x00, 0xA4, 0x0D, 0x06, // 42:164 + 0x00, 0xB1, 0x17, 0x09, // 43:177 + 0x00, 0xC8, 0x09, 0x04, // 44:200 + 0x00, 0xD1, 0x0B, 0x05, // 45:209 + 0x00, 0xDC, 0x08, 0x04, // 46:220 + 0x00, 0xE4, 0x0A, 0x04, // 47:228 + 0x00, 0xEE, 0x17, 0x09, // 48:238 + 0x01, 0x05, 0x11, 0x09, // 49:261 + 0x01, 0x16, 0x17, 0x09, // 50:278 + 0x01, 0x2D, 0x17, 0x09, // 51:301 + 0x01, 0x44, 0x17, 0x09, // 52:324 + 0x01, 0x5B, 0x17, 0x09, // 53:347 + 0x01, 0x72, 0x17, 0x09, // 54:370 + 0x01, 0x89, 0x16, 0x09, // 55:393 + 0x01, 0x9F, 0x17, 0x09, // 56:415 + 0x01, 0xB6, 0x17, 0x09, // 57:438 + 0x01, 0xCD, 0x05, 0x04, // 58:461 + 0x01, 0xD2, 0x06, 0x04, // 59:466 + 0x01, 0xD8, 0x17, 0x09, // 60:472 + 0x01, 0xEF, 0x17, 0x09, // 61:495 + 0x02, 0x06, 0x17, 0x09, // 62:518 + 0x02, 0x1D, 0x16, 0x09, // 63:541 + 0x02, 0x33, 0x2F, 0x10, // 64:563 + 0x02, 0x62, 0x1D, 0x0B, // 65:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66:639 + 0x02, 0x9C, 0x20, 0x0C, // 67:668 + 0x02, 0xBC, 0x20, 0x0C, // 68:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69:732 + 0x02, 0xF9, 0x19, 0x0A, // 70:761 + 0x03, 0x12, 0x20, 0x0C, // 71:786 + 0x03, 0x32, 0x1D, 0x0C, // 72:818 + 0x03, 0x4F, 0x05, 0x04, // 73:847 + 0x03, 0x54, 0x14, 0x08, // 74:852 + 0x03, 0x68, 0x1D, 0x0B, // 75:872 + 0x03, 0x85, 0x17, 0x09, // 76:901 + 0x03, 0x9C, 0x23, 0x0D, // 77:924 + 0x03, 0xBF, 0x1D, 0x0C, // 78:959 + 0x03, 0xDC, 0x20, 0x0C, // 79:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80:1020 + 0x04, 0x18, 0x20, 0x0C, // 81:1048 + 0x04, 0x38, 0x1D, 0x0C, // 82:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83:1109 + 0x04, 0x72, 0x19, 0x0A, // 84:1138 + 0x04, 0x8B, 0x1D, 0x0C, // 85:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88:1263 + 0x05, 0x0F, 0x19, 0x0B, // 89:1295 + 0x05, 0x28, 0x1A, 0x0A, // 90:1320 + 0x05, 0x42, 0x0C, 0x04, // 91:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92:1358 + 0x05, 0x59, 0x09, 0x04, // 93:1369 + 0x05, 0x62, 0x14, 0x08, // 94:1378 + 0x05, 0x76, 0x1B, 0x09, // 95:1398 + 0x05, 0x91, 0x07, 0x05, // 96:1425 + 0x05, 0x98, 0x17, 0x09, // 97:1432 + 0x05, 0xAF, 0x17, 0x09, // 98:1455 + 0x05, 0xC6, 0x14, 0x08, // 99:1478 + 0x05, 0xDA, 0x17, 0x09, // 100:1498 + 0x05, 0xF1, 0x17, 0x09, // 101:1521 + 0x06, 0x08, 0x0A, 0x04, // 102:1544 + 0x06, 0x12, 0x17, 0x09, // 103:1554 + 0x06, 0x29, 0x14, 0x09, // 104:1577 + 0x06, 0x3D, 0x05, 0x04, // 105:1597 + 0x06, 0x42, 0x06, 0x04, // 106:1602 + 0x06, 0x48, 0x17, 0x08, // 107:1608 + 0x06, 0x5F, 0x05, 0x04, // 108:1631 + 0x06, 0x64, 0x23, 0x0D, // 109:1636 + 0x06, 0x87, 0x14, 0x09, // 110:1671 + 0x06, 0x9B, 0x17, 0x09, // 111:1691 + 0x06, 0xB2, 0x17, 0x09, // 112:1714 + 0x06, 0xC9, 0x18, 0x09, // 113:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114:1761 + 0x06, 0xEE, 0x14, 0x08, // 115:1774 + 0x07, 0x02, 0x0B, 0x04, // 116:1794 + 0x07, 0x0D, 0x14, 0x09, // 117:1805 + 0x07, 0x21, 0x13, 0x08, // 118:1825 + 0x07, 0x34, 0x1F, 0x0C, // 119:1844 + 0x07, 0x53, 0x14, 0x08, // 120:1875 + 0x07, 0x67, 0x13, 0x08, // 121:1895 + 0x07, 0x7A, 0x14, 0x08, // 122:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123:1934 + 0x07, 0x9D, 0x06, 0x04, // 124:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125:1955 + 0x07, 0xB1, 0x17, 0x09, // 126:1969 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x10, // 128:65535 + 0xFF, 0xFF, 0x00, 0x10, // 129:65535 + 0xFF, 0xFF, 0x00, 0x10, // 130:65535 + 0xFF, 0xFF, 0x00, 0x10, // 131:65535 + 0xFF, 0xFF, 0x00, 0x10, // 132:65535 + 0xFF, 0xFF, 0x00, 0x10, // 133:65535 + 0xFF, 0xFF, 0x00, 0x10, // 134:65535 + 0xFF, 0xFF, 0x00, 0x10, // 135:65535 + 0xFF, 0xFF, 0x00, 0x10, // 136:65535 + 0xFF, 0xFF, 0x00, 0x10, // 137:65535 + 0xFF, 0xFF, 0x00, 0x10, // 138:65535 + 0xFF, 0xFF, 0x00, 0x10, // 139:65535 + 0xFF, 0xFF, 0x00, 0x10, // 140:65535 + 0xFF, 0xFF, 0x00, 0x10, // 141:65535 + 0xFF, 0xFF, 0x00, 0x10, // 142:65535 + 0xFF, 0xFF, 0x00, 0x10, // 143:65535 + 0xFF, 0xFF, 0x00, 0x10, // 144:65535 + 0xFF, 0xFF, 0x00, 0x10, // 145:65535 + 0xFF, 0xFF, 0x00, 0x10, // 146:65535 + 0xFF, 0xFF, 0x00, 0x10, // 147:65535 + 0xFF, 0xFF, 0x00, 0x10, // 148:65535 + 0xFF, 0xFF, 0x00, 0x10, // 149:65535 + 0xFF, 0xFF, 0x00, 0x10, // 150:65535 + 0xFF, 0xFF, 0x00, 0x10, // 151:65535 + 0xFF, 0xFF, 0x00, 0x10, // 152:65535 + 0xFF, 0xFF, 0x00, 0x10, // 153:65535 + 0xFF, 0xFF, 0x00, 0x10, // 154:65535 + 0xFF, 0xFF, 0x00, 0x10, // 155:65535 + 0xFF, 0xFF, 0x00, 0x10, // 156:65535 + 0xFF, 0xFF, 0x00, 0x10, // 157:65535 + 0xFF, 0xFF, 0x00, 0x10, // 158:65535 + 0xFF, 0xFF, 0x00, 0x10, // 159:65535 + 0xFF, 0xFF, 0x00, 0x04, // 160:65535 + 0x07, 0xC8, 0x09, 0x05, // 161:1992 + 0x07, 0xD1, 0x17, 0x09, // 162:2001 + 0x07, 0xE8, 0x17, 0x09, // 163:2024 + 0x07, 0xFF, 0x14, 0x09, // 164:2047 + 0x08, 0x13, 0x1A, 0x09, // 165:2067 + 0x08, 0x2D, 0x06, 0x04, // 166:2093 + 0x08, 0x33, 0x17, 0x09, // 167:2099 + 0x08, 0x4A, 0x07, 0x05, // 168:2122 + 0x08, 0x51, 0x23, 0x0C, // 169:2129 + 0x08, 0x74, 0x0E, 0x06, // 170:2164 + 0x08, 0x82, 0x14, 0x09, // 171:2178 + 0x08, 0x96, 0x17, 0x09, // 172:2198 + 0x08, 0xAD, 0x0B, 0x05, // 173:2221 + 0x08, 0xB8, 0x23, 0x0C, // 174:2232 + 0x08, 0xDB, 0x19, 0x09, // 175:2267 + 0x08, 0xF4, 0x0D, 0x06, // 176:2292 + 0x09, 0x01, 0x17, 0x09, // 177:2305 + 0x09, 0x18, 0x0E, 0x05, // 178:2328 + 0x09, 0x26, 0x0D, 0x05, // 179:2342 + 0x09, 0x33, 0x0A, 0x05, // 180:2355 + 0x09, 0x3D, 0x17, 0x09, // 181:2365 + 0x09, 0x54, 0x19, 0x09, // 182:2388 + 0x09, 0x6D, 0x08, 0x05, // 183:2413 + 0x09, 0x75, 0x0C, 0x05, // 184:2421 + 0x09, 0x81, 0x0B, 0x05, // 185:2433 + 0x09, 0x8C, 0x0D, 0x06, // 186:2444 + 0x09, 0x99, 0x17, 0x09, // 187:2457 + 0x09, 0xB0, 0x26, 0x0D, // 188:2480 + 0x09, 0xD6, 0x26, 0x0D, // 189:2518 + 0x09, 0xFC, 0x26, 0x0D, // 190:2556 + 0x0A, 0x22, 0x1A, 0x0A, // 191:2594 + 0x0A, 0x3C, 0x1D, 0x0B, // 192:2620 + 0x0A, 0x59, 0x1D, 0x0B, // 193:2649 + 0x0A, 0x76, 0x1D, 0x0B, // 194:2678 + 0x0A, 0x93, 0x1D, 0x0B, // 195:2707 + 0x0A, 0xB0, 0x1D, 0x0B, // 196:2736 + 0x0A, 0xCD, 0x1D, 0x0B, // 197:2765 + 0x0A, 0xEA, 0x2C, 0x10, // 198:2794 + 0x0B, 0x16, 0x20, 0x0C, // 199:2838 + 0x0B, 0x36, 0x1D, 0x0B, // 200:2870 + 0x0B, 0x53, 0x1D, 0x0B, // 201:2899 + 0x0B, 0x70, 0x1D, 0x0B, // 202:2928 + 0x0B, 0x8D, 0x1D, 0x0B, // 203:2957 + 0x0B, 0xAA, 0x05, 0x04, // 204:2986 + 0x0B, 0xAF, 0x07, 0x04, // 205:2991 + 0x0B, 0xB6, 0x0A, 0x04, // 206:2998 + 0x0B, 0xC0, 0x07, 0x04, // 207:3008 + 0x0B, 0xC7, 0x20, 0x0C, // 208:3015 + 0x0B, 0xE7, 0x1D, 0x0C, // 209:3047 + 0x0C, 0x04, 0x20, 0x0C, // 210:3076 + 0x0C, 0x24, 0x20, 0x0C, // 211:3108 + 0x0C, 0x44, 0x20, 0x0C, // 212:3140 + 0x0C, 0x64, 0x20, 0x0C, // 213:3172 + 0x0C, 0x84, 0x20, 0x0C, // 214:3204 + 0x0C, 0xA4, 0x17, 0x09, // 215:3236 + 0x0C, 0xBB, 0x20, 0x0C, // 216:3259 + 0x0C, 0xDB, 0x1D, 0x0C, // 217:3291 + 0x0C, 0xF8, 0x1D, 0x0C, // 218:3320 + 0x0D, 0x15, 0x1D, 0x0C, // 219:3349 + 0x0D, 0x32, 0x1D, 0x0C, // 220:3378 + 0x0D, 0x4F, 0x19, 0x0B, // 221:3407 + 0x0D, 0x68, 0x1D, 0x0B, // 222:3432 + 0x0D, 0x85, 0x17, 0x0A, // 223:3461 + 0x0D, 0x9C, 0x17, 0x09, // 224:3484 + 0x0D, 0xB3, 0x17, 0x09, // 225:3507 + 0x0D, 0xCA, 0x17, 0x09, // 226:3530 + 0x0D, 0xE1, 0x17, 0x09, // 227:3553 + 0x0D, 0xF8, 0x17, 0x09, // 228:3576 + 0x0E, 0x0F, 0x17, 0x09, // 229:3599 + 0x0E, 0x26, 0x29, 0x0E, // 230:3622 + 0x0E, 0x4F, 0x14, 0x08, // 231:3663 + 0x0E, 0x63, 0x17, 0x09, // 232:3683 + 0x0E, 0x7A, 0x17, 0x09, // 233:3706 + 0x0E, 0x91, 0x17, 0x09, // 234:3729 + 0x0E, 0xA8, 0x17, 0x09, // 235:3752 + 0x0E, 0xBF, 0x05, 0x04, // 236:3775 + 0x0E, 0xC4, 0x07, 0x04, // 237:3780 + 0x0E, 0xCB, 0x0A, 0x04, // 238:3787 + 0x0E, 0xD5, 0x07, 0x04, // 239:3797 + 0x0E, 0xDC, 0x17, 0x09, // 240:3804 + 0x0E, 0xF3, 0x14, 0x09, // 241:3827 + 0x0F, 0x07, 0x17, 0x09, // 242:3847 + 0x0F, 0x1E, 0x17, 0x09, // 243:3870 + 0x0F, 0x35, 0x17, 0x09, // 244:3893 + 0x0F, 0x4C, 0x17, 0x09, // 245:3916 + 0x0F, 0x63, 0x17, 0x09, // 246:3939 + 0x0F, 0x7A, 0x17, 0x09, // 247:3962 + 0x0F, 0x91, 0x17, 0x0A, // 248:3985 + 0x0F, 0xA8, 0x14, 0x09, // 249:4008 + 0x0F, 0xBC, 0x14, 0x09, // 250:4028 + 0x0F, 0xD0, 0x14, 0x09, // 251:4048 + 0x0F, 0xE4, 0x14, 0x09, // 252:4068 + 0x0F, 0xF8, 0x13, 0x08, // 253:4088 + 0x10, 0x0B, 0x17, 0x09, // 254:4107 + 0x10, 0x22, 0x13, 0x08, // 255:4130 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x5F, // 33 + 0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, // 34 + 0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08, // 35 + 0x00,0x00,0x00,0xE0,0x10,0x00,0x10,0x21,0x00,0x08,0x41,0x00,0xFC,0xFF,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 36 + 0x00,0x00,0x00,0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x61,0x00,0xF0,0x18,0x00,0x00,0x06,0x00,0xC0,0x01,0x00,0x30,0x3C,0x00,0x08,0x42,0x00,0x00,0x42,0x00,0x00,0x42,0x00,0x00,0x3C, // 37 + 0x00,0x00,0x00,0x00,0x1C,0x00,0x70,0x22,0x00,0x88,0x41,0x00,0x08,0x43,0x00,0x88,0x44,0x00,0x70,0x28,0x00,0x00,0x10,0x00,0x00,0x28,0x00,0x00,0x44, // 38 + 0x00,0x00,0x00,0x78, // 39 + 0x00,0x00,0x00,0x80,0x3F,0x00,0x70,0xC0,0x01,0x08,0x00,0x02, // 40 + 0x00,0x00,0x00,0x08,0x00,0x02,0x70,0xC0,0x01,0x80,0x3F, // 41 + 0x10,0x00,0x00,0xD0,0x00,0x00,0x38,0x00,0x00,0xD0,0x00,0x00,0x10, // 42 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0xC0,0x1F,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01, // 44 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40, // 46 + 0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 47 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xE0,0x1F, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0xF8,0x7F, // 49 + 0x00,0x00,0x00,0x20,0x40,0x00,0x10,0x60,0x00,0x08,0x50,0x00,0x08,0x48,0x00,0x08,0x44,0x00,0x10,0x43,0x00,0xE0,0x40, // 50 + 0x00,0x00,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x88,0x41,0x00,0xF0,0x22,0x00,0x00,0x1C, // 51 + 0x00,0x0C,0x00,0x00,0x0A,0x00,0x00,0x09,0x00,0xC0,0x08,0x00,0x20,0x08,0x00,0x10,0x08,0x00,0xF8,0x7F,0x00,0x00,0x08, // 52 + 0x00,0x00,0x00,0xC0,0x11,0x00,0xB8,0x20,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x08,0x21,0x00,0x08,0x1E, // 53 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x21,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x10,0x21,0x00,0x20,0x1E, // 54 + 0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x78,0x00,0x08,0x07,0x00,0xC8,0x00,0x00,0x28,0x00,0x00,0x18, // 55 + 0x00,0x00,0x00,0x60,0x1C,0x00,0x90,0x22,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 56 + 0x00,0x00,0x00,0xE0,0x11,0x00,0x10,0x22,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x10,0x22,0x00,0xE0,0x1F, // 57 + 0x00,0x00,0x00,0x40,0x40, // 58 + 0x00,0x00,0x00,0x40,0xC0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x40,0x10, // 60 + 0x00,0x00,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08, // 61 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x02, // 62 + 0x00,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x08,0x5C,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 63 + 0x00,0x00,0x00,0x00,0x3F,0x00,0xC0,0x40,0x00,0x20,0x80,0x00,0x10,0x1E,0x01,0x10,0x21,0x01,0x88,0x40,0x02,0x48,0x40,0x02,0x48,0x40,0x02,0x48,0x20,0x02,0x88,0x7C,0x02,0xC8,0x43,0x02,0x10,0x40,0x02,0x10,0x20,0x01,0x60,0x10,0x01,0x80,0x8F, // 64 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x08,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 65 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 66 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 67 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 68 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 69 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08, // 70 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x12,0x00,0x00,0x0E, // 71 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0xF8,0x7F, // 72 + 0x00,0x00,0x00,0xF8,0x7F, // 73 + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0xF8,0x3F, // 74 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x80,0x03,0x00,0x40,0x04,0x00,0x20,0x18,0x00,0x10,0x20,0x00,0x08,0x40, // 75 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40, // 76 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0xF8,0x7F, // 77 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 78 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 79 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 80 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x50,0x00,0x08,0x50,0x00,0x10,0x20,0x00,0x20,0x70,0x00,0xC0,0x4F, // 81 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x06,0x00,0x08,0x1A,0x00,0x10,0x21,0x00,0xE0,0x40, // 82 + 0x00,0x00,0x00,0x60,0x10,0x00,0x90,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 83 + 0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0x7F,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 84 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 85 + 0x00,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x07,0x00,0xE0,0x00,0x00,0x18, // 86 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x03,0x00,0x70,0x00,0x00,0x08,0x00,0x00,0x70,0x00,0x00,0x80,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 87 + 0x00,0x40,0x00,0x08,0x20,0x00,0x10,0x10,0x00,0x60,0x0C,0x00,0x80,0x02,0x00,0x00,0x01,0x00,0x80,0x02,0x00,0x60,0x0C,0x00,0x10,0x10,0x00,0x08,0x20,0x00,0x00,0x40, // 88 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x00,0x7E,0x00,0x80,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 89 + 0x00,0x40,0x00,0x08,0x60,0x00,0x08,0x58,0x00,0x08,0x44,0x00,0x08,0x43,0x00,0x88,0x40,0x00,0x68,0x40,0x00,0x18,0x40,0x00,0x08,0x40, // 90 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x08,0x00,0x02,0x08,0x00,0x02, // 91 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60, // 92 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF8,0xFF,0x03, // 93 + 0x00,0x01,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0x08,0x00,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x01, // 94 + 0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 95 + 0x00,0x00,0x00,0x08,0x00,0x00,0x10, // 96 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 97 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 98 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20, // 99 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xF8,0x7F, // 100 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 101 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x48,0x00,0x00,0x48, // 102 + 0x00,0x00,0x00,0x00,0x1F,0x01,0x80,0x20,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x80,0x20,0x01,0xC0,0xFF, // 103 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 104 + 0x00,0x00,0x00,0xC8,0x7F, // 105 + 0x00,0x00,0x02,0xC8,0xFF,0x01, // 106 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x19,0x00,0x80,0x20,0x00,0x40,0x40, // 107 + 0x00,0x00,0x00,0xF8,0x7F, // 108 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 109 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 110 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 111 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 112 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xC0,0xFF,0x03, // 113 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40, // 114 + 0x00,0x00,0x00,0x80,0x23,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x38, // 115 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x40,0x40,0x00,0x40,0x40, // 116 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 117 + 0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0, // 118 + 0xC0,0x00,0x00,0x00,0x1F,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1F,0x00,0xC0, // 119 + 0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x40,0x40, // 120 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x00,0xE0,0x01,0x00,0x38,0x00,0x00,0x07,0x00,0xC0, // 121 + 0x40,0x40,0x00,0x40,0x60,0x00,0x40,0x58,0x00,0x40,0x44,0x00,0x40,0x43,0x00,0xC0,0x40,0x00,0x40,0x40, // 122 + 0x00,0x04,0x00,0x00,0x04,0x00,0xF0,0xFB,0x01,0x08,0x00,0x02,0x08,0x00,0x02, // 123 + 0x00,0x00,0x00,0xF8,0xFF,0x03, // 124 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF0,0xFB,0x01,0x00,0x04,0x00,0x00,0x04, // 125 + 0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xFF,0x03, // 161 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x03,0x40,0xF0,0x00,0x40,0x4E,0x00,0xC0,0x41,0x00,0xB8,0x20,0x00,0x00,0x11, // 162 + 0x00,0x41,0x00,0xE0,0x31,0x00,0x10,0x2F,0x00,0x08,0x21,0x00,0x08,0x21,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x20,0x20, // 163 + 0x00,0x00,0x00,0x40,0x0B,0x00,0x80,0x04,0x00,0x40,0x08,0x00,0x40,0x08,0x00,0x80,0x04,0x00,0x40,0x0B, // 164 + 0x08,0x0A,0x00,0x10,0x0A,0x00,0x60,0x0A,0x00,0x80,0x0B,0x00,0x00,0x7E,0x00,0x80,0x0B,0x00,0x60,0x0A,0x00,0x10,0x0A,0x00,0x08,0x0A, // 165 + 0x00,0x00,0x00,0xF8,0xF1,0x03, // 166 + 0x00,0x86,0x00,0x70,0x09,0x01,0xC8,0x10,0x02,0x88,0x10,0x02,0x08,0x21,0x02,0x08,0x61,0x02,0x30,0xD2,0x01,0x00,0x0C, // 167 + 0x08,0x00,0x00,0x00,0x00,0x00,0x08, // 168 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xC8,0x47,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x48,0x44,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 169 + 0xD0,0x00,0x00,0x48,0x01,0x00,0x28,0x01,0x00,0x28,0x01,0x00,0xF0,0x01, // 170 + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20, // 171 + 0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x0F, // 172 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 173 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xE8,0x4F,0x00,0x28,0x41,0x00,0x28,0x41,0x00,0x28,0x43,0x00,0x28,0x45,0x00,0xC8,0x48,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 174 + 0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04, // 175 + 0x00,0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x48,0x00,0x00,0x30, // 176 + 0x00,0x00,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0xE0,0x4F,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41, // 177 + 0x10,0x01,0x00,0x88,0x01,0x00,0x48,0x01,0x00,0x48,0x01,0x00,0x30,0x01, // 178 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x28,0x01,0x00,0xD8, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x08, // 180 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 181 + 0xF0,0x00,0x00,0xF8,0x00,0x00,0xF8,0x01,0x00,0xF8,0x01,0x00,0xF8,0xFF,0x03,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0xFF,0x03,0x08, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, // 183 + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x02,0x00,0x00,0x03, // 184 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0xF8,0x01, // 185 + 0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0xF0, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04, // 187 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x21,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x80,0x01,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 188 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x31,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x60,0x44,0x00,0x10,0x62,0x00,0x08,0x52,0x00,0x00,0x52,0x00,0x00,0x4C, // 189 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x41,0x00,0x28,0x21,0x00,0xD8,0x18,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 190 + 0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,0x01,0x00,0x08,0x02,0x40,0x07,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0xC0, // 191 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x71,0x04,0x00,0x0A,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 192 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x0A,0x04,0x00,0x71,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 193 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x71,0x04,0x00,0x82,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 194 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x72,0x04,0x00,0x81,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 195 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x08,0x04,0x00,0x72,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 196 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x7E,0x04,0x00,0x0A,0x04,0x00,0x7E,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 197 + 0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x06,0x00,0x80,0x05,0x00,0x60,0x04,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41, // 198 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x02,0x08,0xC0,0x02,0x08,0x40,0x03,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 199 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 200 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 201 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 202 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 203 + 0x01,0x00,0x00,0xFA,0x7F, // 204 + 0x00,0x00,0x00,0xFA,0x7F,0x00,0x01, // 205 + 0x02,0x00,0x00,0xF9,0x7F,0x00,0x01,0x00,0x00,0x02, // 206 + 0x02,0x00,0x00,0xF8,0x7F,0x00,0x02, // 207 + 0x00,0x02,0x00,0xF8,0x7F,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 208 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x82,0x00,0x00,0x01,0x03,0x00,0x02,0x04,0x00,0x01,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 209 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 210 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 211 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 212 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 213 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 214 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x40,0x10, // 215 + 0x00,0x00,0x00,0xC0,0x4F,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x08,0x4C,0x00,0x08,0x42,0x00,0x08,0x41,0x00,0xC8,0x40,0x00,0x30,0x20,0x00,0x30,0x10,0x00,0xC8,0x0F, // 216 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 217 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 218 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 219 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 220 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x02,0x7E,0x00,0x81,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 221 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x40,0x08,0x00,0x80,0x07, // 222 + 0x00,0x00,0x00,0xE0,0x7F,0x00,0x10,0x00,0x00,0x08,0x20,0x00,0x88,0x43,0x00,0x70,0x42,0x00,0x00,0x44,0x00,0x00,0x38, // 223 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 224 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 225 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x42,0x00,0x50,0x22,0x00,0x80,0x7F, // 226 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x48,0x22,0x00,0x80,0x7F, // 227 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 228 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x5C,0x44,0x00,0x54,0x44,0x00,0x5C,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 229 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x3F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 230 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x02,0x40,0xC0,0x02,0x40,0x40,0x03,0x80,0x20, // 231 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x48,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 232 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 233 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x44,0x00,0x90,0x24,0x00,0x00,0x17, // 234 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 235 + 0x08,0x00,0x00,0xD0,0x7F, // 236 + 0x00,0x00,0x00,0xD0,0x7F,0x00,0x08, // 237 + 0x10,0x00,0x00,0xC8,0x7F,0x00,0x08,0x00,0x00,0x10, // 238 + 0x10,0x00,0x00,0xC0,0x7F,0x00,0x10, // 239 + 0x00,0x00,0x00,0x00,0x1F,0x00,0xA0,0x20,0x00,0x68,0x40,0x00,0x58,0x40,0x00,0x70,0x40,0x00,0xE8,0x20,0x00,0x00,0x1F, // 240 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x90,0x00,0x00,0x48,0x00,0x00,0x50,0x00,0x00,0x48,0x00,0x00,0x80,0x7F, // 241 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 242 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 243 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x48,0x40,0x00,0x90,0x20,0x00,0x00,0x1F, // 244 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x88,0x20,0x00,0x00,0x1F, // 245 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 246 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 247 + 0x00,0x00,0x00,0x00,0x5F,0x00,0x80,0x30,0x00,0x40,0x48,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x80,0x21,0x00,0x40,0x1F, // 248 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 249 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x08,0x20,0x00,0xC0,0x7F, // 250 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x10,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xC0,0x7F, // 251 + 0x00,0x00,0x00,0xD0,0x3F,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 252 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x10,0xE0,0x01,0x08,0x38,0x00,0x00,0x07,0x00,0xC0, // 253 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0 // 255 +}; +const uint8_t ArialMT_Plain_24[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32:65535 + 0x00, 0x00, 0x13, 0x07, // 33:0 + 0x00, 0x13, 0x1A, 0x09, // 34:19 + 0x00, 0x2D, 0x33, 0x0D, // 35:45 + 0x00, 0x60, 0x2F, 0x0D, // 36:96 + 0x00, 0x8F, 0x4F, 0x15, // 37:143 + 0x00, 0xDE, 0x3B, 0x10, // 38:222 + 0x01, 0x19, 0x0A, 0x05, // 39:281 + 0x01, 0x23, 0x1C, 0x08, // 40:291 + 0x01, 0x3F, 0x1B, 0x08, // 41:319 + 0x01, 0x5A, 0x21, 0x09, // 42:346 + 0x01, 0x7B, 0x32, 0x0E, // 43:379 + 0x01, 0xAD, 0x10, 0x07, // 44:429 + 0x01, 0xBD, 0x1B, 0x08, // 45:445 + 0x01, 0xD8, 0x0F, 0x07, // 46:472 + 0x01, 0xE7, 0x19, 0x07, // 47:487 + 0x02, 0x00, 0x2F, 0x0D, // 48:512 + 0x02, 0x2F, 0x23, 0x0D, // 49:559 + 0x02, 0x52, 0x2F, 0x0D, // 50:594 + 0x02, 0x81, 0x2F, 0x0D, // 51:641 + 0x02, 0xB0, 0x2F, 0x0D, // 52:688 + 0x02, 0xDF, 0x2F, 0x0D, // 53:735 + 0x03, 0x0E, 0x2F, 0x0D, // 54:782 + 0x03, 0x3D, 0x2D, 0x0D, // 55:829 + 0x03, 0x6A, 0x2F, 0x0D, // 56:874 + 0x03, 0x99, 0x2F, 0x0D, // 57:921 + 0x03, 0xC8, 0x0F, 0x07, // 58:968 + 0x03, 0xD7, 0x10, 0x07, // 59:983 + 0x03, 0xE7, 0x2F, 0x0E, // 60:999 + 0x04, 0x16, 0x2F, 0x0E, // 61:1046 + 0x04, 0x45, 0x2E, 0x0E, // 62:1093 + 0x04, 0x73, 0x2E, 0x0D, // 63:1139 + 0x04, 0xA1, 0x5B, 0x18, // 64:1185 + 0x04, 0xFC, 0x3B, 0x10, // 65:1276 + 0x05, 0x37, 0x3B, 0x10, // 66:1335 + 0x05, 0x72, 0x3F, 0x11, // 67:1394 + 0x05, 0xB1, 0x3F, 0x11, // 68:1457 + 0x05, 0xF0, 0x3B, 0x10, // 69:1520 + 0x06, 0x2B, 0x35, 0x0F, // 70:1579 + 0x06, 0x60, 0x43, 0x13, // 71:1632 + 0x06, 0xA3, 0x3B, 0x11, // 72:1699 + 0x06, 0xDE, 0x0F, 0x07, // 73:1758 + 0x06, 0xED, 0x27, 0x0C, // 74:1773 + 0x07, 0x14, 0x3F, 0x10, // 75:1812 + 0x07, 0x53, 0x2F, 0x0D, // 76:1875 + 0x07, 0x82, 0x43, 0x14, // 77:1922 + 0x07, 0xC5, 0x3B, 0x11, // 78:1989 + 0x08, 0x00, 0x47, 0x13, // 79:2048 + 0x08, 0x47, 0x3A, 0x10, // 80:2119 + 0x08, 0x81, 0x47, 0x13, // 81:2177 + 0x08, 0xC8, 0x3F, 0x11, // 82:2248 + 0x09, 0x07, 0x3B, 0x10, // 83:2311 + 0x09, 0x42, 0x35, 0x0F, // 84:2370 + 0x09, 0x77, 0x3B, 0x11, // 85:2423 + 0x09, 0xB2, 0x39, 0x10, // 86:2482 + 0x09, 0xEB, 0x59, 0x17, // 87:2539 + 0x0A, 0x44, 0x3B, 0x10, // 88:2628 + 0x0A, 0x7F, 0x3D, 0x10, // 89:2687 + 0x0A, 0xBC, 0x37, 0x0F, // 90:2748 + 0x0A, 0xF3, 0x14, 0x07, // 91:2803 + 0x0B, 0x07, 0x1B, 0x07, // 92:2823 + 0x0B, 0x22, 0x18, 0x07, // 93:2850 + 0x0B, 0x3A, 0x2A, 0x0B, // 94:2874 + 0x0B, 0x64, 0x34, 0x0D, // 95:2916 + 0x0B, 0x98, 0x11, 0x08, // 96:2968 + 0x0B, 0xA9, 0x2F, 0x0D, // 97:2985 + 0x0B, 0xD8, 0x33, 0x0D, // 98:3032 + 0x0C, 0x0B, 0x2B, 0x0C, // 99:3083 + 0x0C, 0x36, 0x2F, 0x0D, // 100:3126 + 0x0C, 0x65, 0x2F, 0x0D, // 101:3173 + 0x0C, 0x94, 0x1A, 0x07, // 102:3220 + 0x0C, 0xAE, 0x2F, 0x0D, // 103:3246 + 0x0C, 0xDD, 0x2F, 0x0D, // 104:3293 + 0x0D, 0x0C, 0x0F, 0x05, // 105:3340 + 0x0D, 0x1B, 0x10, 0x05, // 106:3355 + 0x0D, 0x2B, 0x2F, 0x0C, // 107:3371 + 0x0D, 0x5A, 0x0F, 0x05, // 108:3418 + 0x0D, 0x69, 0x47, 0x14, // 109:3433 + 0x0D, 0xB0, 0x2F, 0x0D, // 110:3504 + 0x0D, 0xDF, 0x2F, 0x0D, // 111:3551 + 0x0E, 0x0E, 0x33, 0x0D, // 112:3598 + 0x0E, 0x41, 0x30, 0x0D, // 113:3649 + 0x0E, 0x71, 0x1E, 0x08, // 114:3697 + 0x0E, 0x8F, 0x2B, 0x0C, // 115:3727 + 0x0E, 0xBA, 0x1B, 0x07, // 116:3770 + 0x0E, 0xD5, 0x2F, 0x0D, // 117:3797 + 0x0F, 0x04, 0x2A, 0x0C, // 118:3844 + 0x0F, 0x2E, 0x42, 0x11, // 119:3886 + 0x0F, 0x70, 0x2B, 0x0C, // 120:3952 + 0x0F, 0x9B, 0x2A, 0x0C, // 121:3995 + 0x0F, 0xC5, 0x2B, 0x0C, // 122:4037 + 0x0F, 0xF0, 0x1C, 0x08, // 123:4080 + 0x10, 0x0C, 0x10, 0x06, // 124:4108 + 0x10, 0x1C, 0x1B, 0x08, // 125:4124 + 0x10, 0x37, 0x32, 0x0E, // 126:4151 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x18, // 128:65535 + 0xFF, 0xFF, 0x00, 0x18, // 129:65535 + 0xFF, 0xFF, 0x00, 0x18, // 130:65535 + 0xFF, 0xFF, 0x00, 0x18, // 131:65535 + 0xFF, 0xFF, 0x00, 0x18, // 132:65535 + 0xFF, 0xFF, 0x00, 0x18, // 133:65535 + 0xFF, 0xFF, 0x00, 0x18, // 134:65535 + 0xFF, 0xFF, 0x00, 0x18, // 135:65535 + 0xFF, 0xFF, 0x00, 0x18, // 136:65535 + 0xFF, 0xFF, 0x00, 0x18, // 137:65535 + 0xFF, 0xFF, 0x00, 0x18, // 138:65535 + 0xFF, 0xFF, 0x00, 0x18, // 139:65535 + 0xFF, 0xFF, 0x00, 0x18, // 140:65535 + 0xFF, 0xFF, 0x00, 0x18, // 141:65535 + 0xFF, 0xFF, 0x00, 0x18, // 142:65535 + 0xFF, 0xFF, 0x00, 0x18, // 143:65535 + 0xFF, 0xFF, 0x00, 0x18, // 144:65535 + 0xFF, 0xFF, 0x00, 0x18, // 145:65535 + 0xFF, 0xFF, 0x00, 0x18, // 146:65535 + 0xFF, 0xFF, 0x00, 0x18, // 147:65535 + 0xFF, 0xFF, 0x00, 0x18, // 148:65535 + 0xFF, 0xFF, 0x00, 0x18, // 149:65535 + 0xFF, 0xFF, 0x00, 0x18, // 150:65535 + 0xFF, 0xFF, 0x00, 0x18, // 151:65535 + 0xFF, 0xFF, 0x00, 0x18, // 152:65535 + 0xFF, 0xFF, 0x00, 0x18, // 153:65535 + 0xFF, 0xFF, 0x00, 0x18, // 154:65535 + 0xFF, 0xFF, 0x00, 0x18, // 155:65535 + 0xFF, 0xFF, 0x00, 0x18, // 156:65535 + 0xFF, 0xFF, 0x00, 0x18, // 157:65535 + 0xFF, 0xFF, 0x00, 0x18, // 158:65535 + 0xFF, 0xFF, 0x00, 0x18, // 159:65535 + 0xFF, 0xFF, 0x00, 0x07, // 160:65535 + 0x10, 0x69, 0x14, 0x08, // 161:4201 + 0x10, 0x7D, 0x2B, 0x0D, // 162:4221 + 0x10, 0xA8, 0x2F, 0x0D, // 163:4264 + 0x10, 0xD7, 0x33, 0x0D, // 164:4311 + 0x11, 0x0A, 0x31, 0x0D, // 165:4362 + 0x11, 0x3B, 0x10, 0x06, // 166:4411 + 0x11, 0x4B, 0x2F, 0x0D, // 167:4427 + 0x11, 0x7A, 0x19, 0x08, // 168:4474 + 0x11, 0x93, 0x46, 0x12, // 169:4499 + 0x11, 0xD9, 0x1A, 0x09, // 170:4569 + 0x11, 0xF3, 0x27, 0x0D, // 171:4595 + 0x12, 0x1A, 0x2F, 0x0E, // 172:4634 + 0x12, 0x49, 0x1B, 0x08, // 173:4681 + 0x12, 0x64, 0x46, 0x12, // 174:4708 + 0x12, 0xAA, 0x31, 0x0D, // 175:4778 + 0x12, 0xDB, 0x1E, 0x0A, // 176:4827 + 0x12, 0xF9, 0x33, 0x0D, // 177:4857 + 0x13, 0x2C, 0x1A, 0x08, // 178:4908 + 0x13, 0x46, 0x1A, 0x08, // 179:4934 + 0x13, 0x60, 0x19, 0x08, // 180:4960 + 0x13, 0x79, 0x2F, 0x0E, // 181:4985 + 0x13, 0xA8, 0x31, 0x0D, // 182:5032 + 0x13, 0xD9, 0x12, 0x08, // 183:5081 + 0x13, 0xEB, 0x18, 0x08, // 184:5099 + 0x14, 0x03, 0x16, 0x08, // 185:5123 + 0x14, 0x19, 0x1E, 0x09, // 186:5145 + 0x14, 0x37, 0x2E, 0x0D, // 187:5175 + 0x14, 0x65, 0x4F, 0x14, // 188:5221 + 0x14, 0xB4, 0x4B, 0x14, // 189:5300 + 0x14, 0xFF, 0x4B, 0x14, // 190:5375 + 0x15, 0x4A, 0x33, 0x0F, // 191:5450 + 0x15, 0x7D, 0x3B, 0x10, // 192:5501 + 0x15, 0xB8, 0x3B, 0x10, // 193:5560 + 0x15, 0xF3, 0x3B, 0x10, // 194:5619 + 0x16, 0x2E, 0x3B, 0x10, // 195:5678 + 0x16, 0x69, 0x3B, 0x10, // 196:5737 + 0x16, 0xA4, 0x3B, 0x10, // 197:5796 + 0x16, 0xDF, 0x5B, 0x18, // 198:5855 + 0x17, 0x3A, 0x3F, 0x11, // 199:5946 + 0x17, 0x79, 0x3B, 0x10, // 200:6009 + 0x17, 0xB4, 0x3B, 0x10, // 201:6068 + 0x17, 0xEF, 0x3B, 0x10, // 202:6127 + 0x18, 0x2A, 0x3B, 0x10, // 203:6186 + 0x18, 0x65, 0x11, 0x07, // 204:6245 + 0x18, 0x76, 0x11, 0x07, // 205:6262 + 0x18, 0x87, 0x15, 0x07, // 206:6279 + 0x18, 0x9C, 0x15, 0x07, // 207:6300 + 0x18, 0xB1, 0x3F, 0x11, // 208:6321 + 0x18, 0xF0, 0x3B, 0x11, // 209:6384 + 0x19, 0x2B, 0x47, 0x13, // 210:6443 + 0x19, 0x72, 0x47, 0x13, // 211:6514 + 0x19, 0xB9, 0x47, 0x13, // 212:6585 + 0x1A, 0x00, 0x47, 0x13, // 213:6656 + 0x1A, 0x47, 0x47, 0x13, // 214:6727 + 0x1A, 0x8E, 0x2B, 0x0E, // 215:6798 + 0x1A, 0xB9, 0x47, 0x13, // 216:6841 + 0x1B, 0x00, 0x3B, 0x11, // 217:6912 + 0x1B, 0x3B, 0x3B, 0x11, // 218:6971 + 0x1B, 0x76, 0x3B, 0x11, // 219:7030 + 0x1B, 0xB1, 0x3B, 0x11, // 220:7089 + 0x1B, 0xEC, 0x3D, 0x10, // 221:7148 + 0x1C, 0x29, 0x3A, 0x10, // 222:7209 + 0x1C, 0x63, 0x37, 0x0F, // 223:7267 + 0x1C, 0x9A, 0x2F, 0x0D, // 224:7322 + 0x1C, 0xC9, 0x2F, 0x0D, // 225:7369 + 0x1C, 0xF8, 0x2F, 0x0D, // 226:7416 + 0x1D, 0x27, 0x2F, 0x0D, // 227:7463 + 0x1D, 0x56, 0x2F, 0x0D, // 228:7510 + 0x1D, 0x85, 0x2F, 0x0D, // 229:7557 + 0x1D, 0xB4, 0x53, 0x15, // 230:7604 + 0x1E, 0x07, 0x2B, 0x0C, // 231:7687 + 0x1E, 0x32, 0x2F, 0x0D, // 232:7730 + 0x1E, 0x61, 0x2F, 0x0D, // 233:7777 + 0x1E, 0x90, 0x2F, 0x0D, // 234:7824 + 0x1E, 0xBF, 0x2F, 0x0D, // 235:7871 + 0x1E, 0xEE, 0x11, 0x07, // 236:7918 + 0x1E, 0xFF, 0x11, 0x07, // 237:7935 + 0x1F, 0x10, 0x15, 0x07, // 238:7952 + 0x1F, 0x25, 0x15, 0x07, // 239:7973 + 0x1F, 0x3A, 0x2F, 0x0D, // 240:7994 + 0x1F, 0x69, 0x2F, 0x0D, // 241:8041 + 0x1F, 0x98, 0x2F, 0x0D, // 242:8088 + 0x1F, 0xC7, 0x2F, 0x0D, // 243:8135 + 0x1F, 0xF6, 0x2F, 0x0D, // 244:8182 + 0x20, 0x25, 0x2F, 0x0D, // 245:8229 + 0x20, 0x54, 0x2F, 0x0D, // 246:8276 + 0x20, 0x83, 0x32, 0x0D, // 247:8323 + 0x20, 0xB5, 0x33, 0x0F, // 248:8373 + 0x20, 0xE8, 0x2F, 0x0D, // 249:8424 + 0x21, 0x17, 0x2F, 0x0D, // 250:8471 + 0x21, 0x46, 0x2F, 0x0D, // 251:8518 + 0x21, 0x75, 0x2F, 0x0D, // 252:8565 + 0x21, 0xA4, 0x2A, 0x0C, // 253:8612 + 0x21, 0xCE, 0x2F, 0x0D, // 254:8654 + 0x21, 0xFD, 0x2A, 0x0C, // 255:8701 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x33,0x00,0xE0,0xFF,0x33, // 33 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 34 + 0x00,0x0C,0x03,0x00,0x00,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x03,0x00,0x00,0x0C,0x03, // 35 + 0x00,0x00,0x00,0x00,0x80,0x07,0x06,0x00,0xC0,0x0F,0x1E,0x00,0xC0,0x18,0x1C,0x00,0x60,0x18,0x38,0x00,0x60,0x30,0x30,0x00,0xF0,0xFF,0xFF,0x00,0x60,0x30,0x30,0x00,0x60,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0xC1,0x1F,0x00,0x00,0x81,0x07, // 36 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x60,0x30,0x38,0x00,0xC0,0x1F,0x1E,0x00,0x80,0x8F,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x8F,0x0F,0x00,0xC0,0xC3,0x1F,0x00,0xE0,0x60,0x30,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 37 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,0xC0,0x0F,0x00,0x80,0xE3,0x1C,0x00,0xC0,0x77,0x38,0x00,0xE0,0x3C,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x78,0x30,0x00,0xE0,0xEC,0x38,0x00,0xC0,0x8F,0x1B,0x00,0x80,0x03,0x1F,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0x10, // 38 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 39 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xFE,0x7F,0x00,0x80,0x0F,0xF0,0x01,0xC0,0x01,0x80,0x03,0x60,0x00,0x00,0x06,0x20,0x00,0x00,0x04, // 40 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x60,0x00,0x00,0x06,0xC0,0x01,0x80,0x03,0x80,0x0F,0xF0,0x01,0x00,0xFE,0x7F,0x00,0x00,0xF0,0x0F, // 41 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x04,0x00,0x00,0x80,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x80,0x04,0x00,0x00,0x80, // 42 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0xFF,0x0F,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0xF0,0x01, // 44 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 46 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x3F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60, // 47 + 0x00,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x01,0x1C,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x03, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 49 + 0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0xC0,0x03,0x38,0x00,0xC0,0x00,0x3C,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x33,0x00,0x60,0x80,0x31,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x30,0x00,0xC0,0x30,0x30,0x00,0xC0,0x1F,0x30,0x00,0x00,0x0F,0x30, // 50 + 0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0xC0,0x01,0x0E,0x00,0xC0,0x00,0x1C,0x00,0x60,0x00,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x0F,0x00,0x00,0x80,0x07, // 51 + 0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0x3C,0x03,0x00,0x00,0x0E,0x03,0x00,0x80,0x07,0x03,0x00,0xC0,0x01,0x03,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03, // 52 + 0x00,0x00,0x00,0x00,0x00,0x30,0x06,0x00,0x80,0x3F,0x0E,0x00,0xE0,0x1F,0x18,0x00,0x60,0x08,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x18,0x1C,0x00,0x60,0xF0,0x0F,0x00,0x00,0xE0,0x03, // 53 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x63,0x1C,0x00,0xC0,0x30,0x38,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0xE0,0x30,0x18,0x00,0xC0,0xF1,0x0F,0x00,0x80,0xC1,0x07, // 54 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x3C,0x00,0x60,0x80,0x3F,0x00,0x60,0xE0,0x03,0x00,0x60,0x78,0x00,0x00,0x60,0x0E,0x00,0x00,0x60,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60, // 55 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x80,0xC7,0x1F,0x00,0xC0,0x6F,0x18,0x00,0xE0,0x38,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xE0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 56 + 0x00,0x00,0x00,0x00,0x00,0x1F,0x0C,0x00,0x80,0x7F,0x1C,0x00,0xC0,0x61,0x38,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x18,0x00,0xC0,0x31,0x1E,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x01, // 57 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 58 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x03,0x00,0x06,0xF0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x03,0x06, // 60 + 0x00,0x00,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01, // 61 + 0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x04,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x20, // 62 + 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x33,0x00,0x60,0xE0,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x07, // 63 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x1E,0xF0,0x00,0x00,0x07,0xC0,0x01,0x80,0xC3,0x87,0x01,0xC0,0xF1,0x9F,0x03,0xC0,0x38,0x18,0x03,0xC0,0x0C,0x30,0x03,0x60,0x0E,0x30,0x06,0x60,0x06,0x30,0x06,0x60,0x06,0x18,0x06,0x60,0x06,0x0C,0x06,0x60,0x0C,0x1E,0x06,0x60,0xF8,0x3F,0x06,0xE0,0xFE,0x31,0x06,0xC0,0x0E,0x30,0x06,0xC0,0x01,0x18,0x03,0x80,0x03,0x1C,0x03,0x00,0x07,0x8F,0x01,0x00,0xFE,0x87,0x01,0x00,0xF8,0xC1,0x00,0x00,0x00,0x40, // 64 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE0,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 65 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x78,0x30,0x00,0xC0,0xFF,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 66 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 67 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 68 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 69 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60, // 70 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x60,0x30,0x00,0x60,0x60,0x30,0x00,0xE0,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0x61,0x18,0x00,0x80,0xE3,0x0F,0x00,0x00,0xE2,0x0F, // 71 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 72 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 73 + 0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0xE0,0xFF,0x1F,0x00,0xE0,0xFF,0x0F, // 74 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE7,0x01,0x00,0x80,0x83,0x07,0x00,0xC0,0x01,0x0F,0x00,0xE0,0x00,0x1E,0x00,0x60,0x00,0x38,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 75 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 76 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x07,0x00,0x00,0xFE,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 77 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 78 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 79 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0xC0,0x30,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x0F, // 80 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x0C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x36,0x00,0xE0,0x00,0x3C,0x00,0xC0,0x00,0x1C,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x3F,0x00,0x00,0xFF,0x77,0x00,0x00,0xFC,0x61, // 81 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x70,0x00,0x00,0x60,0xF0,0x00,0x00,0x60,0xF0,0x03,0x00,0x60,0xB0,0x07,0x00,0xE0,0x18,0x1F,0x00,0xC0,0x1F,0x3C,0x00,0x80,0x0F,0x30,0x00,0x00,0x00,0x20, // 82 + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x0F,0x00,0xC0,0x1F,0x1C,0x00,0xC0,0x18,0x18,0x00,0x60,0x38,0x38,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x70,0x30,0x00,0xC0,0x60,0x18,0x00,0xC0,0xE1,0x18,0x00,0x80,0xC3,0x0F,0x00,0x00,0x83,0x07, // 83 + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 84 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 85 + 0x20,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xF8,0x01,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0x20, // 86 + 0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0xFF,0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x80,0x3F,0x00,0x00,0xF8,0x0F,0x00,0x80,0xFF,0x00,0x00,0xE0,0x07,0x00,0x00,0x60, // 87 + 0x00,0x00,0x20,0x00,0x20,0x00,0x30,0x00,0x60,0x00,0x3C,0x00,0xE0,0x01,0x1E,0x00,0xC0,0x83,0x07,0x00,0x00,0xCF,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0xCF,0x03,0x00,0xC0,0x03,0x07,0x00,0xE0,0x01,0x1E,0x00,0x60,0x00,0x3C,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 88 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 89 + 0x00,0x00,0x30,0x00,0x60,0x00,0x38,0x00,0x60,0x00,0x3C,0x00,0x60,0x00,0x37,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0x60,0x0E,0x30,0x00,0x60,0x07,0x30,0x00,0xE0,0x01,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x30, // 90 + 0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 91 + 0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 92 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07, // 93 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20, // 94 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 95 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x80, // 96 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 97 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 98 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 99 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 100 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 101 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x06, // 102 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x83,0x01,0x00,0xF8,0x8F,0x03,0x00,0x1C,0x1C,0x07,0x00,0x0E,0x38,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x0C,0x18,0x07,0x00,0x18,0x8C,0x03,0x00,0xFE,0xFF,0x01,0x00,0xFE,0xFF, // 103 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 104 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F, // 105 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x60,0xFE,0xFF,0x07,0x60,0xFE,0xFF,0x03, // 106 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0x98,0x07,0x00,0x00,0x0C,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x02,0x30,0x00,0x00,0x00,0x20, // 107 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 108 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 109 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 110 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 111 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 112 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07, // 113 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 114 + 0x00,0x00,0x00,0x00,0x00,0x38,0x0C,0x00,0x00,0x7C,0x1C,0x00,0x00,0xEE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x31,0x00,0x00,0xC6,0x31,0x00,0x00,0x8E,0x39,0x00,0x00,0x9C,0x1F,0x00,0x00,0x18,0x0F, // 115 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 116 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 117 + 0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xF8,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 118 + 0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x7C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xF0,0x03,0x00,0x00,0x7E,0x00,0x00,0x00,0x0E, // 119 + 0x00,0x02,0x20,0x00,0x00,0x06,0x30,0x00,0x00,0x1E,0x3C,0x00,0x00,0x38,0x0E,0x00,0x00,0xF0,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x07,0x00,0x00,0x38,0x0E,0x00,0x00,0x1C,0x3C,0x00,0x00,0x0E,0x30,0x00,0x00,0x02,0x20, // 120 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0x00,0xC0,0x1F,0x00,0x00,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 121 + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x3E,0x00,0x00,0x06,0x37,0x00,0x00,0xC6,0x33,0x00,0x00,0xE6,0x30,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x30,0x00,0x00,0x1E,0x30,0x00,0x00,0x06,0x30, // 122 + 0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x03,0x00,0xC0,0x7F,0xFE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 123 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x0F,0xE0,0xFF,0xFF,0x0F, // 124 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0x3F,0xFC,0x07,0xC0,0x7F,0xFF,0x03,0x00,0xC0,0x03,0x00,0x00,0x80,0x01, // 125 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0xFF,0x07,0x00,0xE6,0xFF,0x07, // 161 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x9C,0x07,0x00,0x0E,0x78,0x00,0x00,0x06,0x3F,0x00,0x00,0xF6,0x30,0x00,0x00,0x0E,0x30,0x00,0xE0,0x0D,0x1C,0x00,0x00,0x1C,0x0E,0x00,0x00,0x10,0x06, // 162 + 0x00,0x60,0x10,0x00,0x00,0x60,0x38,0x00,0x00,0x7F,0x1C,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xE0,0x19,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x30,0x00,0xE0,0x00,0x30,0x00,0xC0,0x01,0x30,0x00,0x80,0x01,0x38,0x00,0x00,0x00,0x10, // 163 + 0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x00,0xF7,0x0E,0x00,0x00,0xFE,0x07,0x00,0x00,0x0C,0x03,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x0C,0x03,0x00,0x00,0xFE,0x07,0x00,0x00,0xF7,0x0E,0x00,0x00,0x02,0x04, // 164 + 0xE0,0x60,0x06,0x00,0xC0,0x61,0x06,0x00,0x80,0x67,0x06,0x00,0x00,0x7E,0x06,0x00,0x00,0x7C,0x06,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x7C,0x06,0x00,0x00,0x7E,0x06,0x00,0x80,0x67,0x06,0x00,0xC0,0x61,0x06,0x00,0xE0,0x60,0x06,0x00,0x20, // 165 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xF8,0x0F,0xE0,0x7F,0xF8,0x0F, // 166 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x80,0xF3,0xC1,0x00,0xC0,0x1F,0xC3,0x03,0xE0,0x0C,0x07,0x03,0x60,0x1C,0x06,0x06,0x60,0x18,0x0C,0x06,0x60,0x30,0x1C,0x06,0xE0,0x70,0x38,0x07,0xC0,0xE1,0xF4,0x03,0x80,0xC1,0xE7,0x01,0x00,0x80,0x03, // 167 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 168 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x79,0x1C,0x00,0xC0,0xFE,0x19,0x00,0x60,0x86,0x31,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x87,0x33,0x00,0xC0,0x86,0x19,0x00,0xC0,0x85,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 169 + 0x00,0x00,0x00,0x00,0xC0,0x1C,0x00,0x00,0xE0,0x3E,0x00,0x00,0x60,0x32,0x00,0x00,0x60,0x32,0x00,0x00,0xE0,0x3F,0x00,0x00,0xC0,0x3F, // 170 + 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x84,0x10,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x04,0x10, // 171 + 0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFC,0x01, // 172 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 173 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x01,0x1C,0x00,0xC0,0xFE,0x1B,0x00,0x60,0xFE,0x33,0x00,0x60,0x66,0x30,0x00,0x60,0x66,0x30,0x00,0x60,0xE6,0x30,0x00,0x60,0xFE,0x31,0x00,0x60,0x3C,0x33,0x00,0xC0,0x00,0x1A,0x00,0xC0,0x01,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 174 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 175 + 0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x04,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x40,0x04,0x00,0x00,0x80,0x03, // 176 + 0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0xFF,0x3F,0x00,0x00,0xFF,0x3F,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30, // 177 + 0x40,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x38,0x00,0x00,0x20,0x2C,0x00,0x00,0x20,0x26,0x00,0x00,0xE0,0x23,0x00,0x00,0xC0,0x21, // 178 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x00,0x00,0x20,0x22,0x00,0x00,0xE0,0x3D,0x00,0x00,0xC0,0x1D, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 180 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x00,0x1C,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 181 + 0x00,0x0F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 183 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x01, // 184 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F, // 185 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xE0,0x38,0x00,0x00,0x60,0x30,0x00,0x00,0xE0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x84,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x80, // 187 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x38,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x0C,0x00,0xC0,0x01,0x0E,0x00,0xE0,0x80,0x0B,0x00,0x60,0xC0,0x08,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x08, // 188 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x30,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x4E,0x20,0x00,0x00,0x67,0x30,0x00,0xC0,0x21,0x38,0x00,0xE0,0x20,0x2C,0x00,0x60,0x20,0x26,0x00,0x00,0xE0,0x27,0x00,0x00,0xC0,0x21, // 189 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x20,0x00,0x20,0x22,0x30,0x00,0xE0,0x3D,0x38,0x00,0xC0,0x1D,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x0E,0x0C,0x00,0x00,0x07,0x0E,0x00,0x80,0x83,0x0B,0x00,0xE0,0xC0,0x08,0x00,0x60,0xE0,0x3F,0x00,0x20,0xE0,0x3F,0x00,0x00,0x00,0x08, // 190 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x07,0x07,0x00,0xE6,0x03,0x06,0x00,0xE6,0x01,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xC0, // 191 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x82,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE8,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 192 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE8,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x82,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 193 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x88,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x08,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 194 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x0C,0xFE,0x01,0x00,0x8E,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xEC,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0E,0xFE,0x01,0x00,0x06,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 195 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x8C,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0C,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 196 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x9C,0x8F,0x01,0x00,0xE2,0x83,0x01,0x00,0x62,0x80,0x01,0x00,0xE2,0x83,0x01,0x00,0x9C,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 197 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x01,0x00,0x00,0xBC,0x01,0x00,0x00,0x8F,0x01,0x00,0xC0,0x83,0x01,0x00,0xE0,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 198 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x02,0x60,0x00,0x30,0x02,0x60,0x00,0xF0,0x02,0x60,0x00,0xB0,0x03,0x60,0x00,0x30,0x01,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 199 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 200 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 201 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 202 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 203 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xEE,0xFF,0x3F,0x00,0x08, // 204 + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xEE,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x02, // 205 + 0x08,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x08, // 206 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x0C, // 207 + 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 208 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x8C,0x03,0x00,0x00,0x0E,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x70,0x00,0x00,0x0C,0xE0,0x01,0x00,0x0C,0x80,0x03,0x00,0x0E,0x00,0x0F,0x00,0x06,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 209 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x62,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 210 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x62,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 211 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x68,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xE8,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 212 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xCC,0x00,0x18,0x00,0xEE,0x00,0x38,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0xE6,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 213 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xEC,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 214 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0xF8,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0x06,0x03, // 215 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x21,0x00,0x00,0xFF,0x77,0x00,0x80,0x07,0x3F,0x00,0xC0,0x01,0x1E,0x00,0xC0,0x00,0x1F,0x00,0xE0,0x80,0x3B,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x70,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0xE0,0x0E,0x38,0x00,0xC0,0x07,0x18,0x00,0xC0,0x03,0x1C,0x00,0xE0,0x07,0x0F,0x00,0x70,0xFF,0x07,0x00,0x20,0xFC,0x01, // 216 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x02,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 217 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 218 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x08,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x08,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 219 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 220 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x08,0xF0,0x3F,0x00,0x0E,0xF0,0x3F,0x00,0x06,0x3C,0x00,0x00,0x02,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 221 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x07,0x00,0x00,0x86,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0xF8, // 222 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x00,0xC0,0xFF,0x3F,0x00,0xC0,0x00,0x00,0x00,0x60,0x00,0x08,0x00,0x60,0x00,0x1C,0x00,0x60,0x00,0x38,0x00,0xE0,0x78,0x30,0x00,0xC0,0x7F,0x30,0x00,0x80,0xC7,0x30,0x00,0x00,0x80,0x39,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x0F, // 223 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x20,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 224 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x80,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x18,0x00,0x20,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 225 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x80,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0x60,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0x80,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 226 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0xC0,0x1C,0x1F,0x00,0xE0,0x8C,0x39,0x00,0x60,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xC0,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xE0,0xCE,0x0C,0x00,0x60,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 227 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0xC0,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xC0,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 228 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x70,0x86,0x31,0x00,0x88,0x86,0x31,0x00,0x88,0xC6,0x30,0x00,0x88,0xC6,0x18,0x00,0x70,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 229 + 0x00,0x00,0x00,0x00,0x00,0x10,0x0F,0x00,0x00,0x9C,0x1F,0x00,0x00,0xCC,0x39,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0x66,0x18,0x00,0x00,0x6E,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xCC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xE0,0x04, // 230 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x02,0x00,0x06,0x30,0x02,0x00,0x06,0xF0,0x02,0x00,0x06,0xB0,0x03,0x00,0x0E,0x38,0x01,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 231 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x20,0xCE,0x38,0x00,0x60,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 232 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x80,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x20,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 233 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x80,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0x80,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 234 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 235 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0xE0,0xFE,0x3F,0x00,0x80, // 236 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0x20, // 237 + 0x80,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0x80, // 238 + 0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0xC0, // 239 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1D,0x1C,0x00,0xA0,0x0F,0x38,0x00,0xA0,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0F,0x38,0x00,0x20,0x1F,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xE0,0x07, // 240 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0xC0,0xFE,0x3F,0x00,0xE0,0x18,0x00,0x00,0x60,0x0C,0x00,0x00,0x60,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xE0,0x0E,0x00,0x00,0x60,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 241 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x20,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x80,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 242 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x80,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x20,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 243 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x80,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0x80,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 244 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0xC0,0x1C,0x1C,0x00,0xE0,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xE0,0x1C,0x1C,0x00,0x60,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 245 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 246 + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB6,0x01,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 247 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x67,0x00,0x00,0xF8,0x7F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x3F,0x00,0x00,0x86,0x33,0x00,0x00,0xE6,0x31,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xFF,0x0F,0x00,0x00,0xF3,0x07, // 248 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x20,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 249 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x18,0x00,0x20,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 250 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x80,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0x80,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 251 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0xC0,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 252 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x80,0x00,0xFE,0x03,0xE0,0x00,0xFC,0x00,0x60,0xC0,0x1F,0x00,0x20,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 253 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255 +}; +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayUi.cpp b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayUi.cpp new file mode 100644 index 0000000..cd371a0 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayUi.cpp @@ -0,0 +1,422 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include "OLEDDisplayUi.h" + +OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { + this->display = display; +} + +void OLEDDisplayUi::init() { + this->display->init(); +} + +void OLEDDisplayUi::setTargetFPS(uint8_t fps){ + float oldInterval = this->updateInterval; + this->updateInterval = ((float) 1.0 / (float) fps) * 1000; + + // Calculate new ticksPerFrame + float changeRatio = oldInterval / (float) this->updateInterval; + this->ticksPerFrame *= changeRatio; + this->ticksPerTransition *= changeRatio; +} + +// -/------ Automatic controll ------\- + +void OLEDDisplayUi::enableAutoTransition(){ + this->autoTransition = true; +} +void OLEDDisplayUi::disableAutoTransition(){ + this->autoTransition = false; +} +void OLEDDisplayUi::setAutoTransitionForwards(){ + this->state.frameTransitionDirection = 1; + this->lastTransitionDirection = 1; +} +void OLEDDisplayUi::setAutoTransitionBackwards(){ + this->state.frameTransitionDirection = -1; + this->lastTransitionDirection = -1; +} +void OLEDDisplayUi::setTimePerFrame(uint16_t time){ + this->ticksPerFrame = (uint16_t) ( (float) time / (float) updateInterval); +} +void OLEDDisplayUi::setTimePerTransition(uint16_t time){ + this->ticksPerTransition = (uint16_t) ( (float) time / (float) updateInterval); +} + +// -/------ Customize indicator position and style -------\- +void OLEDDisplayUi::enableIndicator(){ + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::disableIndicator(){ + this->state.isIndicatorDrawen = false; +} + +void OLEDDisplayUi::enableAllIndicators(){ + this->shouldDrawIndicators = true; +} + +void OLEDDisplayUi::disableAllIndicators(){ + this->shouldDrawIndicators = false; +} + +void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { + this->indicatorPosition = pos; +} +void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { + this->indicatorDirection = dir; +} +void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) { + this->activeSymbol = symbol; +} +void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) { + this->inactiveSymbol = symbol; +} + + +// -/----- Frame settings -----\- +void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { + this->frameAnimationDirection = dir; +} +void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { + this->frameFunctions = frameFunctions; + this->frameCount = frameCount; + this->resetState(); +} + +// -/----- Overlays ------\- +void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ + this->overlayFunctions = overlayFunctions; + this->overlayCount = overlayCount; +} + +// -/----- Loading Process -----\- + +void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { + this->loadingDrawFunction = loadingDrawFunction; +} + +void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { + uint8_t progress = 0; + uint8_t increment = 100 / stagesCount; + + for (uint8_t i = 0; i < stagesCount; i++) { + display->clear(); + this->loadingDrawFunction(this->display, &stages[i], progress); + display->display(); + + stages[i].callback(); + + progress += increment; + yield(); + } + + display->clear(); + this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); + display->display(); + + delay(150); +} + +// -/----- Manuel control -----\- +void OLEDDisplayUi::nextFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = 1; + } +} +void OLEDDisplayUi::previousFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = -1; + } +} + +void OLEDDisplayUi::switchToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->state.frameState = FIXED; + this->state.currentFrame = frame; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::transitionToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->nextFrameNumber = frame; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; +} + + +// -/----- State information -----\- +OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ + return &this->state; +} + + +int8_t OLEDDisplayUi::update(){ + unsigned long frameStart = millis(); + int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); + if ( timeBudget <= 0) { + // Implement frame skipping to ensure time budget is keept + if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil(-timeBudget / this->updateInterval); + + this->state.lastUpdate = frameStart; + this->tick(); + } + return this->updateInterval - (millis() - frameStart); +} + + +void OLEDDisplayUi::tick() { + this->state.ticksSinceLastStateSwitch++; + + switch (this->state.frameState) { + case IN_TRANSITION: + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ + this->state.frameState = FIXED; + this->state.currentFrame = getNextFrameNumber(); + this->state.ticksSinceLastStateSwitch = 0; + this->nextFrameNumber = -1; + } + break; + case FIXED: + // Revert manuelControll + if (this->state.manuelControll) { + this->state.frameTransitionDirection = this->lastTransitionDirection; + this->state.manuelControll = false; + } + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ + if (this->autoTransition){ + this->state.frameState = IN_TRANSITION; + } + this->state.ticksSinceLastStateSwitch = 0; + } + break; + } + + this->display->clear(); + this->drawFrame(); + if (shouldDrawIndicators) { + this->drawIndicator(); + } + this->drawOverlays(); + this->display->display(); +} + +void OLEDDisplayUi::resetState() { + this->state.lastUpdate = 0; + this->state.ticksSinceLastStateSwitch = 0; + this->state.frameState = FIXED; + this->state.currentFrame = 0; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::drawFrame(){ + switch (this->state.frameState){ + case IN_TRANSITION: { + float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; + int16_t x = 0, y = 0, x1 = 0, y1 = 0; + switch(this->frameAnimationDirection){ + case SLIDE_LEFT: + x = -this->display->width() * progress; + y = 0; + x1 = x + this->display->width(); + y1 = 0; + break; + case SLIDE_RIGHT: + x = this->display->width() * progress; + y = 0; + x1 = x - this->display->width(); + y1 = 0; + break; + case SLIDE_UP: + x = 0; + y = -this->display->height() * progress; + x1 = 0; + y1 = y + this->display->height(); + break; + case SLIDE_DOWN: + default: + x = 0; + y = this->display->height() * progress; + x1 = 0; + y1 = y - this->display->height(); + break; + } + + // Invert animation if direction is reversed. + int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; + x *= dir; y *= dir; x1 *= dir; y1 *= dir; + + bool drawenCurrentFrame; + + + // Prope each frameFunction for the indicator Drawen state + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); + drawenCurrentFrame = this->state.isIndicatorDrawen; + + this->enableIndicator(); + (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); + + // Build up the indicatorDrawState + if (drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Drawen now but not next + this->indicatorDrawState = 2; + } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) { + // Not drawen now but next + this->indicatorDrawState = 1; + } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Not drawen in both frames + this->indicatorDrawState = 3; + } + + // If the indicator isn't draw in the current frame + // reflect it in state.isIndicatorDrawen + if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false; + + break; + } + case FIXED: + // Always assume that the indicator is drawn! + // And set indicatorDrawState to "not known yet" + this->indicatorDrawState = 0; + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); + break; + } +} + +void OLEDDisplayUi::drawIndicator() { + + // Only draw if the indicator is invisible + // for both frames or + // the indiactor is shown and we are IN_TRANSITION + if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) { + return; + } + + uint8_t posOfHighlightFrame = 0; + float indicatorFadeProgress = 0; + + // if the indicator needs to be slided in we want to + // highlight the next frame in the transition + uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; + + // Calculate the frame that needs to be highlighted + // based on the Direction the indiactor is drawn + switch (this->indicatorDirection){ + case LEFT_RIGHT: + posOfHighlightFrame = frameToHighlight; + break; + case RIGHT_LEFT: + default: + posOfHighlightFrame = this->frameCount - frameToHighlight; + break; + } + + switch (this->indicatorDrawState) { + case 1: // Indicator was not drawn in this frame but will be in next + // Slide IN + indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + case 2: // Indicator was drawn in this frame but not in next + // Slide OUT + indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + } + + //Space between indicators - reduce for small screen sizes + uint16_t indicatorSpacing = 12; + if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) { + indicatorSpacing = 6; + } + + uint16_t frameStartPos = (indicatorSpacing * frameCount / 2); + const uint8_t *image; + + uint16_t x = 0,y = 0; + + + for (byte i = 0; i < this->frameCount; i++) { + + switch (this->indicatorPosition){ + case TOP: + y = 0 - (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case BOTTOM: + y = (this->display->height() - 8) + (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case RIGHT: + x = (this->display->width() - 8) + (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i; + break; + case LEFT: + default: + x = 0 - (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i; + break; + } + + if (posOfHighlightFrame == i) { + image = this->activeSymbol; + } else { + image = this->inactiveSymbol; + } + + this->display->drawFastImage(x, y, 8, 8, image); + } +} + +void OLEDDisplayUi::drawOverlays() { + for (uint8_t i=0;ioverlayCount;i++){ + (this->overlayFunctions[i])(this->display, &this->state); + } +} + +uint8_t OLEDDisplayUi::getNextFrameNumber(){ + if (this->nextFrameNumber != -1) return this->nextFrameNumber; + return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; +} diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayUi.h b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayUi.h new file mode 100644 index 0000000..2cabf30 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/OLEDDisplayUi.h @@ -0,0 +1,309 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAYUI_h +#define OLEDDISPLAYUI_h + +#include +#include "OLEDDisplay.h" + +//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAYUI +#define DEBUG_OLEDDISPLAYUI(...) +#endif + +enum AnimationDirection { + SLIDE_UP, + SLIDE_DOWN, + SLIDE_LEFT, + SLIDE_RIGHT +}; + +enum IndicatorPosition { + TOP, + RIGHT, + BOTTOM, + LEFT +}; + +enum IndicatorDirection { + LEFT_RIGHT, + RIGHT_LEFT +}; + +enum FrameState { + IN_TRANSITION, + FIXED +}; + + +const uint8_t ANIMATION_activeSymbol[] PROGMEM = { + 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 +}; + +const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = { + 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 +}; + + +// Structure of the UiState +struct OLEDDisplayUiState { + uint64_t lastUpdate = 0; + uint16_t ticksSinceLastStateSwitch = 0; + + FrameState frameState = FIXED; + uint8_t currentFrame = 0; + + bool isIndicatorDrawen = true; + + // Normal = 1, Inverse = -1; + int8_t frameTransitionDirection = 1; + + bool manuelControll = false; + + // Custom data that can be used by the user + void* userData = NULL; +}; + +struct LoadingStage { + const char* process; + void (*callback)(); +}; + +typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state); +typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress); + +class OLEDDisplayUi { + private: + OLEDDisplay *display; + + // Symbols for the Indicator + IndicatorPosition indicatorPosition = BOTTOM; + IndicatorDirection indicatorDirection = LEFT_RIGHT; + + const uint8_t* activeSymbol = ANIMATION_activeSymbol; + const uint8_t* inactiveSymbol = ANIMATION_inactiveSymbol; + + bool shouldDrawIndicators = true; + + // Values for the Frames + AnimationDirection frameAnimationDirection = SLIDE_RIGHT; + + int8_t lastTransitionDirection = 1; + + uint16_t ticksPerFrame = 151; // ~ 5000ms at 30 FPS + uint16_t ticksPerTransition = 15; // ~ 500ms at 30 FPS + + bool autoTransition = true; + + FrameCallback* frameFunctions; + uint8_t frameCount = 0; + + // Internally used to transition to a specific frame + int8_t nextFrameNumber = -1; + + // Values for Overlays + OverlayCallback* overlayFunctions; + uint8_t overlayCount = 0; + + // Will the Indicator be drawen + // 3 Not drawn in both frames + // 2 Drawn this frame but not next + // 1 Not drown this frame but next + // 0 Not known yet + uint8_t indicatorDrawState = 1; + + // Loading screen + LoadingDrawFunction loadingDrawFunction = [](OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 18, stage->process); + display->drawProgressBar(4, 32, 120, 8, progress); + }; + + // UI State + OLEDDisplayUiState state; + + // Bookeeping for update + uint8_t updateInterval = 33; + + uint8_t getNextFrameNumber(); + void drawIndicator(); + void drawFrame(); + void drawOverlays(); + void tick(); + void resetState(); + + public: + + OLEDDisplayUi(OLEDDisplay *display); + + /** + * Initialise the display + */ + void init(); + + /** + * Configure the internal used target FPS + */ + void setTargetFPS(uint8_t fps); + + // Automatic Controll + /** + * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. + */ + void enableAutoTransition(); + + /** + * Disable automatic transition to next frame. + */ + void disableAutoTransition(); + + /** + * Set the direction if the automatic transitioning + */ + void setAutoTransitionForwards(); + void setAutoTransitionBackwards(); + + /** + * Set the approx. time a frame is displayed + */ + void setTimePerFrame(uint16_t time); + + /** + * Set the approx. time a transition will take + */ + void setTimePerTransition(uint16_t time); + + // Customize indicator position and style + + /** + * Draw the indicator. + * This is the defaut state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ + void enableIndicator(); + + /** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ + void disableIndicator(); + + /** + * Enable drawing of indicators + */ + void enableAllIndicators(); + + /** + * Disable draw of indicators. + */ + void disableAllIndicators(); + + /** + * Set the position of the indicator bar. + */ + void setIndicatorPosition(IndicatorPosition pos); + + /** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ + void setIndicatorDirection(IndicatorDirection dir); + + /** + * Set the symbol to indicate an active frame in the indicator bar. + */ + void setActiveSymbol(const uint8_t* symbol); + + /** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ + void setInactiveSymbol(const uint8_t* symbol); + + + // Frame settings + + /** + * Configure what animation is used to transition from one frame to another + */ + void setFrameAnimation(AnimationDirection dir); + + /** + * Add frame drawing functions + */ + void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + + // Overlay + + /** + * Add overlays drawing functions that are draw independent of the Frames + */ + void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + + + // Loading animation + /** + * Set the function that will draw each step + * in the loading animation + */ + void setLoadingDrawFunction(LoadingDrawFunction loadingFunction); + + + /** + * Run the loading process + */ + void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + + + // Manual Control + void nextFrame(); + void previousFrame(); + + /** + * Switch without transition to frame `frame`. + */ + void switchToFrame(uint8_t frame); + + /** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ + void transitionToFrame(uint8_t frame); + + // State Info + OLEDDisplayUiState* getUiState(); + + int8_t update(); +}; +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/SSD1306.h b/libraries/Heltec_ESP32_Dev-Boards/src/oled/SSD1306.h new file mode 100644 index 0000000..f6bd554 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/SSD1306.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306_h +#define SSD1306_h +#include "SSD1306Wire.h" + +// For legacy support make SSD1306 an alias for SSD1306 +typedef SSD1306Wire SSD1306; + + +#endif diff --git a/libraries/Heltec_ESP32_Dev-Boards/src/oled/SSD1306Wire.h b/libraries/Heltec_ESP32_Dev-Boards/src/oled/SSD1306Wire.h new file mode 100644 index 0000000..f121375 --- /dev/null +++ b/libraries/Heltec_ESP32_Dev-Boards/src/oled/SSD1306Wire.h @@ -0,0 +1,184 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Wire_h +#define SSD1306Wire_h + +#include "OLEDDisplay.h" +#include + + +class SSD1306Wire : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + uint8_t _rst; + bool _doI2cAutoInit = false; + + public: + SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl, uint8_t _rst, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + this->_rst = _rst; + } + + + bool connect() { + pinMode(_rst,OUTPUT); + digitalWrite(_rst, LOW); + delay(50); + digitalWrite(_rst, HIGH); + + Wire.begin(this->_sda, this->_scl); + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + Wire.setClock(700000); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + const int x_offset = (128 - this->width()) / 2; + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + Wire.beginTransmission(_address); + Wire.write(0x40); + } + + Wire.write(buffer[x + y * this->width()]); + k++; + if (k == 16) { + Wire.endTransmission(); + k = 0; + } + } + yield(); + } + + if (k != 0) { + Wire.endTransmission(); + } + #else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand((this->height() / 8) - 1); + + if (geometry == GEOMETRY_128_64) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + for (uint16_t i=0; i < displayBufferSize; i++) { + Wire.beginTransmission(this->_address); + Wire.write(0x40); + for (uint8_t x = 0; x < 16; x++) { + Wire.write(buffer[i]); + i++; + } + i--; + Wire.endTransmission(); + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + initI2cIfNeccesary(); + Wire.beginTransmission(_address); + Wire.write(0x80); + Wire.write(command); + Wire.endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { + Wire.begin(this->_sda, this->_scl); + } + } + +}; + +//SSD1306Wire display; + +#endif diff --git a/libraries/LoRa/API.md b/libraries/LoRa/API.md new file mode 100644 index 0000000..78f054d --- /dev/null +++ b/libraries/LoRa/API.md @@ -0,0 +1,377 @@ +# LoRa API + +## Include Library + +```arduino +#include +``` + +## Setup + +### Begin + +Initialize the library with the specified frequency. + +```arduino +LoRa.begin(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +Returns `1` on success, `0` on failure. + +### Set pins + +Override the default `NSS`, `NRESET`, and `DIO0` pins used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setPins(ss, reset, dio0); +``` + * `ss` - new slave select pin to use, defaults to `10` + * `reset` - new reset pin to use, defaults to `9` + * `dio0` - new DIO0 pin to use, defaults to `2`. **Must** be interrupt capable via [attachInterrupt(...)](https://www.arduino.cc/en/Reference/AttachInterrupt). + +This call is optional and only needs to be used if you need to change the default pins used. + +#### No MCU controlled reset pin + +To save further pins one could connect the reset pin of the MCU with reset pin of the radio thus resetting only during startup. + +* `reset` - set to `-1` to omit this pin + +#### Pin dio0 interrupt callbacks + +The dio0 pin can be used for transmission finish callback and/or receiving callback, check `onTxDone` and `onReceive`. + +### Set SPI interface + +Override the default SPI interface used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPI(spi); +``` + * `spi` - new SPI interface to use, defaults to `SPI` + +This call is optional and only needs to be used if you need to change the default SPI interface used, in the case your Arduino (or compatible) board has more than one SPI interface present. + +### Set SPI Frequency + +Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPIFrequency(frequency); +``` + * `frequency` - new SPI frequency to use, defaults to `8E6` + +This call is optional and only needs to be used if you need to change the default SPI frequency used. Some logic level converters cannot support high speeds such as 8 MHz, so a lower SPI frequency can be selected with `LoRa.setSPIFrequency(frequency)`. + +### End + +Stop the library + +```arduino +LoRa.end() +``` + +## Sending data + +### Begin packet + +Start the sequence of sending a packet. + +```arduino +LoRa.beginPacket(); + +LoRa.beginPacket(implicitHeader); +``` + + * `implicitHeader` - (optional) `true` enables implicit header mode, `false` enables explicit header mode (default) + +Returns `1` if radio is ready to transmit, `0` if busy or on failure. + +### Writing + +Write data to the packet. Each packet can contain up to 255 bytes. + +```arduino +LoRa.write(byte); + +LoRa.write(buffer, length); +``` +* `byte` - single byte to write to packet + +or + +* `buffer` - data to write to packet +* `length` - size of data to write + +Returns the number of bytes written. + +**Note:** Other Arduino `Print` API's can also be used to write data into the packet + +### End packet + +End the sequence of sending a packet. + +```arduino +LoRa.endPacket(); + +LoRa.endPacket(async); +``` + * `async` - (optional) `true` enables non-blocking mode, `false` waits for transmission to be completed (default) + +Returns `1` on success, `0` on failure. + +### Tx Done + +**WARNING**: TxDone callback uses the interrupt pin on the `dio0` check `setPins` function! + +### Register callback + +Register a callback function for when a packet transmission finish. + +```arduino +LoRa.onTxDone(onTxDone); + +void onTxDone() { + // ... +} +``` + + * `onTxDone` - function to call when a packet transmission finish. + +## Receiving data + +### Parsing packet + +Check if a packet has been received. + +```arduino +int packetSize = LoRa.parsePacket(); + +int packetSize = LoRa.parsePacket(size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + + +Returns the packet size in bytes or `0` if no packet was received. + +### Continuous receive mode + +**WARNING**: Receive callback uses the interrupt pin on the `dio0`, check `setPins` function! + +#### Register callback + +Register a callback function for when a packet is received. + +```arduino +LoRa.onReceive(onReceive); + +void onReceive(int packetSize) { + // ... +} +``` + + * `onReceive` - function to call when a packet is received. + +#### Receive mode + +Puts the radio in continuous receive mode. + +```arduino +LoRa.receive(); + +LoRa.receive(int size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + +The `onReceive` callback will be called when a packet is received. + +### Packet RSSI + +```arduino +int rssi = LoRa.packetRssi(); +``` + +Returns the RSSI of the received packet. + +### Packet SNR + +```arduino +float snr = LoRa.packetSnr(); +``` + +Returns the estimated SNR of the received packet in dB. + +### Packet Frequency Error + +```arduino +long freqErr = LoRa.packetFrequencyError(); +``` + +Returns the frequency error of the received packet in Hz. The frequency error is the frequency offset between the receiver centre frequency and that of an incoming LoRa signal. + +### Available + +```arduino +int availableBytes = LoRa.available() +``` + +Returns number of bytes available for reading. + +### Peeking + +Peek at the next byte in the packet. + +```arduino +byte b = LoRa.peek(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +### Reading + +Read the next byte from the packet. + +```arduino +byte b = LoRa.read(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet + +## Other radio modes + +### Idle mode + +Put the radio in idle (standby) mode. + +```arduino +LoRa.idle(); +``` + +### Sleep mode + +Put the radio in sleep mode. + +```arduino +LoRa.sleep(); +``` + +## Radio parameters + +### TX Power + +Change the TX power of the radio. + +```arduino +LoRa.setTxPower(txPower); + +LoRa.setTxPower(txPower, outputPin); +``` + * `txPower` - TX power in dB, defaults to `17` + * `outputPin` - (optional) PA output pin, supported values are `PA_OUTPUT_RFO_PIN` and `PA_OUTPUT_PA_BOOST_PIN`, defaults to `PA_OUTPUT_PA_BOOST_PIN`. + +Supported values are `2` to `20` for `PA_OUTPUT_PA_BOOST_PIN`, and `0` to `14` for `PA_OUTPUT_RFO_PIN`. + +Most modules have the PA output pin connected to PA BOOST, + +### Frequency + +Change the frequency of the radio. + +```arduino +LoRa.setFrequency(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +### Spreading Factor + +Change the spreading factor of the radio. + +```arduino +LoRa.setSpreadingFactor(spreadingFactor); +``` + * `spreadingFactor` - spreading factor, defaults to `7` + +Supported values are between `6` and `12`. If a spreading factor of `6` is set, implicit header mode must be used to transmit and receive packets. + +### Signal Bandwidth + +Change the signal bandwidth of the radio. + +```arduino +LoRa.setSignalBandwidth(signalBandwidth); +``` + + * `signalBandwidth` - signal bandwidth in Hz, defaults to `125E3`. + +Supported values are `7.8E3`, `10.4E3`, `15.6E3`, `20.8E3`, `31.25E3`, `41.7E3`, `62.5E3`, `125E3`, and `250E3`. + +### Coding Rate + +Change the coding rate of the radio. + +```arduino +LoRa.setCodingRate4(codingRateDenominator); +``` + + * `codingRateDenominator` - denominator of the coding rate, defaults to `5` + +Supported values are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. The coding rate numerator is fixed at `4`. + +### Preamble Length + +Change the preamble length of the radio. + +```arduino +LoRa.setPreambleLength(preambleLength); +``` + + * `preambleLength` - preamble length in symbols, defaults to `8` + +Supported values are between `6` and `65535`. + +### Sync Word + +Change the sync word of the radio. + +```arduino +LoRa.setSyncWord(syncWord); +``` + + * `syncWord` - byte value to use as the sync word, defaults to `0x12` + +### CRC + +Enable or disable CRC usage, by default a CRC is not used. + +```arduino +LoRa.enableCrc(); + +LoRa.disableCrc(); +``` + +### Invert IQ Signals + +Enable or disable Invert the LoRa I and Q signals, by default a invertIQ is not used. + +```arduino +LoRa.enableInvertIQ(); + +LoRa.disableInvertIQ(); +``` + +## Other functions + +### Random + +Generate a random byte, based on the Wideband RSSI measurement. + +``` +byte b = LoRa.random(); +``` + +Returns random byte. diff --git a/libraries/LoRa/LICENSE b/libraries/LoRa/LICENSE new file mode 100644 index 0000000..1e85b95 --- /dev/null +++ b/libraries/LoRa/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Sandeep Mistry + +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. diff --git a/libraries/LoRa/README.md b/libraries/LoRa/README.md new file mode 100644 index 0000000..c89956a --- /dev/null +++ b/libraries/LoRa/README.md @@ -0,0 +1,91 @@ +# Arduino LoRa + +[![Build Status](https://travis-ci.org/sandeepmistry/arduino-LoRa.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-LoRa) + +An [Arduino](https://arduino.cc/) library for sending and receiving data using [LoRa](https://www.lora-alliance.org/) radios. + +## Compatible Hardware + + * [Semtech SX1276/77/78/79](http://www.semtech.com/apps/product.php?pn=SX1276) based boards including: + * [Dragino Lora Shield](http://www.dragino.com/products/module/item/102-lora-shield.html) + * [HopeRF](http://www.hoperf.com/rf_transceiver/lora/) [RFM95W](http://www.hoperf.com/rf_transceiver/lora/RFM95W.html), [RFM96W](http://www.hoperf.com/rf_transceiver/lora/RFM96W.html), and [RFM98W](http://www.hoperf.com/rf_transceiver/lora/RFM98W.html) + * [Modtronix](http://modtronix.com/) [inAir4](http://modtronix.com/inair4.html), [inAir9](http://modtronix.com/inair9.html), and [inAir9B](http://modtronix.com/inair9b.html) + * [Arduino MKR WAN 1300](https://store.arduino.cc/usa/mkr-wan-1300) + * **NOTE:** Requires firmware v1.1.6 or later on the on-board Murata module. Please use the [MKRWANFWUpdate_standalone example](https://github.com/arduino-libraries/MKRWAN/blob/master/examples/MKRWANFWUpdate_standalone/MKRWANFWUpdate_standalone.ino) from latest [MKRWAN library](https://github.com/arduino-libraries/MKRWAN) release to update the firmware. + * **WARNING**: [LoRa.onReceive(...)](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#register-callback) and [LoRa.recieve()](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#receive-mode) is not compatible with this board! + +### Semtech SX1276/77/78/79 wiring + +| Semtech SX1276/77/78/79 | Arduino | +| :---------------------: | :------:| +| VCC | 3.3V | +| GND | GND | +| SCK | SCK | +| MISO | MISO | +| MOSI | MOSI | +| NSS | 10 | +| NRESET | 9 | +| DIO0 | 2 | + + +`NSS`, `NRESET`, and `DIO0` pins can be changed by using `LoRa.setPins(ss, reset, dio0)`. `DIO0` pin is optional, it is only needed for receive callback mode. If `DIO0` pin is used, it **must** be interrupt capable via [`attachInterrupt(...)`](https://www.arduino.cc/en/Reference/AttachInterrupt). + +**NOTES**: + * Some boards (like the Arduino Nano), cannot supply enough current for the SX127x in TX mode. This will cause lockups when sending, be sure to use an external 3.3V supply that can provide at least 120mA's when using these boards. + * If your Arduino board operates at 5V, like the Arduino Uno, Leonardo or Mega, you will need to use a level converter for the wiring to the Semtech SX127x module. Most Semtech SX127x breakout boards do not have logic level converters built-in. + +## Installation + +### Using the Arduino IDE Library Manager + +1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...` +2. Type `LoRa` into the search box. +3. Click the row to select the library. +4. Click the `Install` button to install the library. + +### Using Git + +```sh +cd ~/Documents/Arduino/libraries/ +git clone https://github.com/sandeepmistry/arduino-LoRa LoRa +``` + +## API + +See [API.md](API.md). + +## Examples + +See [examples](examples) folder. + +## FAQ + +**1) Initilizating the LoRa radio is failing** + +Please check the wiring you are using matches what's listed in [Semtech SX1276/77/78/79 wiring](#semtech-sx1276777879-wiring). You can also use `LoRa.setPins(ss, reset, dio0)` to change the default pins used. Some logic level converters cannot operate at 8 MHz, you can call `LoRa.setSPIFrequency(frequency)` to lower the SPI frequency used by the library. Both API's must be called before `LoRa.begin(...)`. + +**2) Can other radios see the packets I'm sending?** + +Yes, any LoRa radio that are configured with the same radio parameters and in range can see the packets you send. + +**3) Is the data I'm sending encrypted?** + +No, all data is sent unencrypted. If want your packet data to be encrypted, you must encrypt it before passing it into this library, followed by decrypting on the receiving end. + +**4) How does this library differ from LoRaWAN libraries?** + +This library exposes the LoRa radio directly, and allows you to send data to any radios in range with same radio parameters. All data is broadcasted and there is no addressing. LoRaWAN builds on top of LoRA, but adds addressing, encryption, and additional layers. It also requires a LoRaWAN gateway and LoRaWAN network and application server. + +**5) Does this library honor duty cycles?** + +No, you have to manage it by your self. + +**6) Which frequencies can I use?** + +You can use [this table](https://www.thethingsnetwork.org/wiki/LoRaWAN/Frequencies/By-Country) to lookup the available frequencies by your country. The selectable frequency also depends on your hardware. You can lookup the data sheet or ask your supplier. + +Please also notice the frequency dependent duty cycles for legal reasons! + +## License + +This libary is [licensed](LICENSE) under the [MIT Licence](https://en.wikipedia.org/wiki/MIT_License). diff --git a/libraries/LoRa/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino b/libraries/LoRa/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino new file mode 100644 index 0000000..5a23d2e --- /dev/null +++ b/libraries/LoRa/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino @@ -0,0 +1,30 @@ +/* + LoRa register dump + + This examples shows how to inspect and output the LoRa radio's + registers on the Serial interface +*/ +#include // include libraries +#include + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Dump Registers"); + + // override the default CS, reset, and IRQ pins (optional) + // LoRa.setPins(7, 6, 1); // set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.dumpRegisters(Serial); +} + + +void loop() { +} + diff --git a/libraries/LoRa/examples/LoRaDuplex/LoRaDuplex.ino b/libraries/LoRa/examples/LoRaDuplex/LoRaDuplex.ino new file mode 100644 index 0000000..c914254 --- /dev/null +++ b/libraries/LoRa/examples/LoRaDuplex/LoRaDuplex.ino @@ -0,0 +1,106 @@ +/* + LoRa Duplex communication + + Sends a message every half second, and polls continually + for new incoming messages. Implements a one-byte addressing scheme, + with 0xFF as the broadcast address. + + Uses readString() from Stream class to read payload. The Stream class' + timeout may affect other functuons, like the radio's callback. For an + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +String outgoing; // outgoing message + +byte msgCount = 0; // count of outgoing messages +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + if (incomingLength != incoming.length()) { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/LoRa/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino b/libraries/LoRa/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino new file mode 100644 index 0000000..0511f3e --- /dev/null +++ b/libraries/LoRa/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino @@ -0,0 +1,110 @@ +/* + LoRa Duplex communication wth callback + + Sends a message every half second, and uses callback + for new incoming messages. Implements a one-byte addressing scheme, + with 0xFF as the broadcast address. + + Note: while sending, LoRa radio is not listening for incoming messages. + Note2: when using the callback method, you can't use any of the Stream + functions that rely on the timeout, such as readString, parseInt(), etc. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#error "This example is not compatible with the Arduino MKR WAN 1300 board!" +#endif + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +String outgoing; // outgoing message +byte msgCount = 0; // count of outgoing messages +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex with callback"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.onReceive(onReceive); + LoRa.receive(); + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + LoRa.receive(); // go back into receive mode + } +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; // payload of packet + + while (LoRa.available()) { // can't use readString() in callback, so + incoming += (char)LoRa.read(); // add bytes one by one + } + + if (incomingLength != incoming.length()) { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/LoRa/examples/LoRaReceiver/LoRaReceiver.ino b/libraries/LoRa/examples/LoRaReceiver/LoRaReceiver.ino new file mode 100644 index 0000000..ccbb453 --- /dev/null +++ b/libraries/LoRa/examples/LoRaReceiver/LoRaReceiver.ino @@ -0,0 +1,32 @@ +#include +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + // try to parse packet + int packetSize = LoRa.parsePacket(); + if (packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + while (LoRa.available()) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); + } +} diff --git a/libraries/LoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino b/libraries/LoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino new file mode 100644 index 0000000..e227e23 --- /dev/null +++ b/libraries/LoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino @@ -0,0 +1,43 @@ +#include +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#error "This example is not compatible with the Arduino MKR WAN 1300 board!" +#endif + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver Callback"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + + // register the receive callback + LoRa.onReceive(onReceive); + + // put the radio into receive mode + LoRa.receive(); +} + +void loop() { + // do nothing +} + +void onReceive(int packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + for (int i = 0; i < packetSize; i++) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); +} + diff --git a/libraries/LoRa/examples/LoRaSender/LoRaSender.ino b/libraries/LoRa/examples/LoRaSender/LoRaSender.ino new file mode 100644 index 0000000..a252ee5 --- /dev/null +++ b/libraries/LoRa/examples/LoRaSender/LoRaSender.ino @@ -0,0 +1,31 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + Serial.print("Sending packet: "); + Serial.println(counter); + + // send packet + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + counter++; + + delay(5000); +} diff --git a/libraries/LoRa/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino b/libraries/LoRa/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino new file mode 100644 index 0000000..0f39077 --- /dev/null +++ b/libraries/LoRa/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino @@ -0,0 +1,35 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender non-blocking"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + // wait until the radio is ready to send a packet + while (LoRa.beginPacket() == 0) { + Serial.print("waiting for radio ... "); + delay(100); + } + + Serial.print("Sending packet non-blocking: "); + Serial.println(counter); + + // send in async / non-blocking mode + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(true); // true = async / non-blocking mode + + counter++; +} diff --git a/libraries/LoRa/examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino b/libraries/LoRa/examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino new file mode 100644 index 0000000..aa79499 --- /dev/null +++ b/libraries/LoRa/examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino @@ -0,0 +1,50 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender non-blocking Callback"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + + LoRa.onTxDone(onTxDone); +} + +void loop() { + if (runEvery(5000)) { // repeat every 5000 millis + + Serial.print("Sending packet non-blocking: "); + Serial.println(counter); + + // send in async / non-blocking mode + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(true); // true = async / non-blocking mode + + counter++; + } +} + +void onTxDone() { + Serial.println("TxDone"); +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} diff --git a/libraries/LoRa/examples/LoRaSetSpread/LoRaSetSpread.ino b/libraries/LoRa/examples/LoRaSetSpread/LoRaSetSpread.ino new file mode 100644 index 0000000..99d0e8d --- /dev/null +++ b/libraries/LoRa/examples/LoRaSetSpread/LoRaSetSpread.ino @@ -0,0 +1,87 @@ +/* + LoRa Duplex communication with Spreading Factor + + Sends a message every half second, and polls continually + for new incoming messages. Sets the LoRa radio's spreading factor. + + Spreading factor affects how far apart the radio's transmissions + are, across the available bandwidth. Radios with different spreading + factors will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + Spreading factor affects reliability of transmission at high rates, however, + so avoid a hugh spreading factor when you're sending continually. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Spreading Factor. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex - Set spreading factor"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin); // set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.setSpreadingFactor(8); // ranges from 6-12,default 7 see API docs + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/LoRa/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino b/libraries/LoRa/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino new file mode 100644 index 0000000..69af87d --- /dev/null +++ b/libraries/LoRa/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino @@ -0,0 +1,82 @@ +/* + LoRa Duplex communication with Sync Word + + Sends a message every half second, and polls continually + for new incoming messages. Sets the LoRa radio's Sync Word. + + Spreading factor is basically the radio's network ID. Radios with different + Sync Words will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Sync Word. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex - Set sync word"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.setSyncWord(0xF3); // ranges from 0-0xFF, default 0x34, see API docs + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/LoRa/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino b/libraries/LoRa/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino new file mode 100644 index 0000000..95d84a7 --- /dev/null +++ b/libraries/LoRa/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino @@ -0,0 +1,117 @@ +/* + LoRa Simple Gateway/Node Exemple + + This code uses InvertIQ function to create a simple Gateway/Node logic. + + Gateway - Sends messages with enableInvertIQ() + - Receives messages with disableInvertIQ() + + Node - Sends messages with disableInvertIQ() + - Receives messages with enableInvertIQ() + + With this arrangement a Gateway never receive messages from another Gateway + and a Node never receive message from another Node. + Only Gateway to Node and vice versa. + + This code receives messages and sends a message every second. + + InvertIQ function basically invert the LoRa I and Q signals. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on InvertIQ register 0x33. + + created 05 August 2018 + by Luiz H. Cassettari +*/ + +#include // include libraries +#include + +const long frequency = 915E6; // LoRa Frequency + +const int csPin = 10; // LoRa radio chip select +const int resetPin = 9; // LoRa radio reset +const int irqPin = 2; // change for your board; must be a hardware interrupt pin + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + LoRa.setPins(csPin, resetPin, irqPin); + + if (!LoRa.begin(frequency)) { + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + Serial.println("LoRa Simple Gateway"); + Serial.println("Only receive messages from nodes"); + Serial.println("Tx: invertIQ enable"); + Serial.println("Rx: invertIQ disable"); + Serial.println(); + + LoRa.onReceive(onReceive); + LoRa.onTxDone(onTxDone); + LoRa_rxMode(); +} + +void loop() { + if (runEvery(5000)) { // repeat every 5000 millis + + String message = "HeLoRa World! "; + message += "I'm a Gateway! "; + message += millis(); + + LoRa_sendMessage(message); // send a message + + Serial.println("Send Message!"); + } +} + +void LoRa_rxMode(){ + LoRa.disableInvertIQ(); // normal mode + LoRa.receive(); // set receive mode +} + +void LoRa_txMode(){ + LoRa.idle(); // set standby mode + LoRa.enableInvertIQ(); // active invert I and Q signals +} + +void LoRa_sendMessage(String message) { + LoRa_txMode(); // set tx mode + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(true); // finish packet and send it +} + +void onReceive(int packetSize) { + String message = ""; + + while (LoRa.available()) { + message += (char)LoRa.read(); + } + + Serial.print("Gateway Receive: "); + Serial.println(message); +} + +void onTxDone() { + Serial.println("TxDone"); + LoRa_rxMode(); +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} + diff --git a/libraries/LoRa/examples/LoRaSimpleNode/LoRaSimpleNode.ino b/libraries/LoRa/examples/LoRaSimpleNode/LoRaSimpleNode.ino new file mode 100644 index 0000000..db8c6fa --- /dev/null +++ b/libraries/LoRa/examples/LoRaSimpleNode/LoRaSimpleNode.ino @@ -0,0 +1,117 @@ +/* + LoRa Simple Gateway/Node Exemple + + This code uses InvertIQ function to create a simple Gateway/Node logic. + + Gateway - Sends messages with enableInvertIQ() + - Receives messages with disableInvertIQ() + + Node - Sends messages with disableInvertIQ() + - Receives messages with enableInvertIQ() + + With this arrangement a Gateway never receive messages from another Gateway + and a Node never receive message from another Node. + Only Gateway to Node and vice versa. + + This code receives messages and sends a message every second. + + InvertIQ function basically invert the LoRa I and Q signals. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on InvertIQ register 0x33. + + created 05 August 2018 + by Luiz H. Cassettari +*/ + +#include // include libraries +#include + +const long frequency = 915E6; // LoRa Frequency + +const int csPin = 10; // LoRa radio chip select +const int resetPin = 9; // LoRa radio reset +const int irqPin = 2; // change for your board; must be a hardware interrupt pin + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + LoRa.setPins(csPin, resetPin, irqPin); + + if (!LoRa.begin(frequency)) { + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + Serial.println("LoRa Simple Node"); + Serial.println("Only receive messages from gateways"); + Serial.println("Tx: invertIQ disable"); + Serial.println("Rx: invertIQ enable"); + Serial.println(); + + LoRa.onReceive(onReceive); + LoRa.onTxDone(onTxDone); + LoRa_rxMode(); +} + +void loop() { + if (runEvery(1000)) { // repeat every 1000 millis + + String message = "HeLoRa World! "; + message += "I'm a Node! "; + message += millis(); + + LoRa_sendMessage(message); // send a message + + Serial.println("Send Message!"); + } +} + +void LoRa_rxMode(){ + LoRa.enableInvertIQ(); // active invert I and Q signals + LoRa.receive(); // set receive mode +} + +void LoRa_txMode(){ + LoRa.idle(); // set standby mode + LoRa.disableInvertIQ(); // normal mode +} + +void LoRa_sendMessage(String message) { + LoRa_txMode(); // set tx mode + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(true); // finish packet and send it +} + +void onReceive(int packetSize) { + String message = ""; + + while (LoRa.available()) { + message += (char)LoRa.read(); + } + + Serial.print("Node Receive: "); + Serial.println(message); +} + +void onTxDone() { + Serial.println("TxDone"); + LoRa_rxMode(); +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} + diff --git a/libraries/LoRa/issue_template.md b/libraries/LoRa/issue_template.md new file mode 100644 index 0000000..3fa6d74 --- /dev/null +++ b/libraries/LoRa/issue_template.md @@ -0,0 +1,3 @@ +Are you receiving `Starting LoRa failed` while using the demo code? + +PLEASE see the [FAQ #1](https://github.com/sandeepmistry/arduino-LoRa#faq) about using [setPins](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#set-pins) **BEFORE** submitting an issue. diff --git a/libraries/LoRa/keywords.txt b/libraries/LoRa/keywords.txt new file mode 100644 index 0000000..63e0e9a --- /dev/null +++ b/libraries/LoRa/keywords.txt @@ -0,0 +1,61 @@ +####################################### +# Syntax Coloring Map For LoRa +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LoRa KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 + +beginPacket KEYWORD2 +endPacket KEYWORD2 + +parsePacket KEYWORD2 +packetRssi KEYWORD2 +packetSnr KEYWORD2 +packetFrequencyError KEYWORD2 + +write KEYWORD2 + +available KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 + +onReceive KEYWORD2 +onTxDone KEYWORD2 +receive KEYWORD2 +idle KEYWORD2 +sleep KEYWORD2 + +setTxPower KEYWORD2 +setFrequency KEYWORD2 +setSpreadingFactor KEYWORD2 +setSignalBandwidth KEYWORD2 +setCodingRate4 KEYWORD2 +setPreambleLength KEYWORD2 +setSyncWord KEYWORD2 +enableCrc KEYWORD2 +disableCrc KEYWORD2 +enableInvertIQ KEYWORD2 +disableInvertIQ KEYWORD2 + +random KEYWORD2 +setPins KEYWORD2 +setSPIFrequency KEYWORD2 +dumpRegisters KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +PA_OUTPUT_RFO_PIN LITERAL1 +PA_OUTPUT_PA_BOOST_PIN LITERAL1 diff --git a/libraries/LoRa/library.properties b/libraries/LoRa/library.properties new file mode 100644 index 0000000..ea93ffc --- /dev/null +++ b/libraries/LoRa/library.properties @@ -0,0 +1,10 @@ +name=LoRa +version=0.7.2 +author=Sandeep Mistry +maintainer=Sandeep Mistry +sentence=An Arduino library for sending and receiving data using LoRa radios. +paragraph=Supports Semtech SX1276/77/78/79 based boards/shields. +category=Communication +url=https://github.com/sandeepmistry/arduino-LoRa +architectures=* +includes=LoRa.h diff --git a/libraries/LoRa/src/LoRa.cpp b/libraries/LoRa/src/LoRa.cpp new file mode 100644 index 0000000..4396518 --- /dev/null +++ b/libraries/LoRa/src/LoRa.cpp @@ -0,0 +1,718 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +// registers +#define REG_FIFO 0x00 +#define REG_OP_MODE 0x01 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define REG_OCP 0x0b +#define REG_LNA 0x0c +#define REG_FIFO_ADDR_PTR 0x0d +#define REG_FIFO_TX_BASE_ADDR 0x0e +#define REG_FIFO_RX_BASE_ADDR 0x0f +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_RX_NB_BYTES 0x13 +#define REG_PKT_SNR_VALUE 0x19 +#define REG_PKT_RSSI_VALUE 0x1a +#define REG_MODEM_CONFIG_1 0x1d +#define REG_MODEM_CONFIG_2 0x1e +#define REG_PREAMBLE_MSB 0x20 +#define REG_PREAMBLE_LSB 0x21 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_MODEM_CONFIG_3 0x26 +#define REG_FREQ_ERROR_MSB 0x28 +#define REG_FREQ_ERROR_MID 0x29 +#define REG_FREQ_ERROR_LSB 0x2a +#define REG_RSSI_WIDEBAND 0x2c +#define REG_DETECTION_OPTIMIZE 0x31 +#define REG_INVERTIQ 0x33 +#define REG_DETECTION_THRESHOLD 0x37 +#define REG_SYNC_WORD 0x39 +#define REG_INVERTIQ2 0x3b +#define REG_DIO_MAPPING_1 0x40 +#define REG_VERSION 0x42 +#define REG_PA_DAC 0x4d + +// modes +#define MODE_LONG_RANGE_MODE 0x80 +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x01 +#define MODE_TX 0x03 +#define MODE_RX_CONTINUOUS 0x05 +#define MODE_RX_SINGLE 0x06 + +// PA config +#define PA_BOOST 0x80 + +// IRQ masks +#define IRQ_TX_DONE_MASK 0x08 +#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 +#define IRQ_RX_DONE_MASK 0x40 + +#define MAX_PKT_LENGTH 255 + +#if (ESP8266 || ESP32) + #define ISR_PREFIX ICACHE_RAM_ATTR +#else + #define ISR_PREFIX +#endif + +LoRaClass::LoRaClass() : + _spiSettings(LORA_DEFAULT_SPI_FREQUENCY, MSBFIRST, SPI_MODE0), + _spi(&LORA_DEFAULT_SPI), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), + _frequency(0), + _packetIndex(0), + _implicitHeaderMode(0), + _onReceive(NULL), + _onTxDone(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +int LoRaClass::begin(long frequency) +{ +#if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) + pinMode(LORA_IRQ_DUMB, OUTPUT); + digitalWrite(LORA_IRQ_DUMB, LOW); + + // Hardware reset + pinMode(LORA_BOOT0, OUTPUT); + digitalWrite(LORA_BOOT0, LOW); + + pinMode(LORA_RESET, OUTPUT); + digitalWrite(LORA_RESET, HIGH); + delay(200); + digitalWrite(LORA_RESET, LOW); + delay(200); + digitalWrite(LORA_RESET, HIGH); + delay(50); +#endif + + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + if (_reset != -1) { + pinMode(_reset, OUTPUT); + + // perform reset + digitalWrite(_reset, LOW); + delay(10); + digitalWrite(_reset, HIGH); + delay(10); + } + + // start SPI + _spi->begin(); + + // check version + uint8_t version = readRegister(REG_VERSION); + if (version != 0x12) { + return 0; + } + + // put in sleep mode + sleep(); + + // set frequency + setFrequency(frequency); + + // set base addresses + writeRegister(REG_FIFO_TX_BASE_ADDR, 0); + writeRegister(REG_FIFO_RX_BASE_ADDR, 0); + + // set LNA boost + writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); + + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + + // set output power to 17 dBm + setTxPower(17); + + // put in standby mode + idle(); + + return 1; +} + +void LoRaClass::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + _spi->end(); +} + +int LoRaClass::beginPacket(int implicitHeader) +{ + if (isTransmitting()) { + return 0; + } + + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + // reset FIFO address and paload length + writeRegister(REG_FIFO_ADDR_PTR, 0); + writeRegister(REG_PAYLOAD_LENGTH, 0); + + return 1; +} + +int LoRaClass::endPacket(bool async) +{ + + if ((async) && (_onTxDone)) + writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE + + // put in TX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); + + if (!async) { + // wait for TX done + while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { + yield(); + } + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return 1; +} + +bool LoRaClass::isTransmitting() +{ + if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) { + return true; + } + + if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) { + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return false; +} + +int LoRaClass::parsePacket(int size) +{ + int packetLength = 0; + int irqFlags = readRegister(REG_IRQ_FLAGS); + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + + // read packet length + if (_implicitHeaderMode) { + packetLength = readRegister(REG_PAYLOAD_LENGTH); + } else { + packetLength = readRegister(REG_RX_NB_BYTES); + } + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + // put in standby mode + idle(); + } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { + // not currently in RX mode + + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + + // put in single RX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); + } + + return packetLength; +} + +int LoRaClass::packetRssi() +{ + return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157)); +} + +float LoRaClass::packetSnr() +{ + return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; +} + +long LoRaClass::packetFrequencyError() +{ + int32_t freqError = 0; + freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); + + if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on + freqError -= 524288; // B1000'0000'0000'0000'0000 + } + + const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) + const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 + + return static_cast(fError); +} + +size_t LoRaClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t LoRaClass::write(const uint8_t *buffer, size_t size) +{ + int currentLength = readRegister(REG_PAYLOAD_LENGTH); + + // check size + if ((currentLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - currentLength; + } + + // write data + for (size_t i = 0; i < size; i++) { + writeRegister(REG_FIFO, buffer[i]); + } + + // update length + writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); + + return size; +} + +int LoRaClass::available() +{ + return (readRegister(REG_RX_NB_BYTES) - _packetIndex); +} + +int LoRaClass::read() +{ + if (!available()) { + return -1; + } + + _packetIndex++; + + return readRegister(REG_FIFO); +} + +int LoRaClass::peek() +{ + if (!available()) { + return -1; + } + + // store current FIFO address + int currentAddress = readRegister(REG_FIFO_ADDR_PTR); + + // read + uint8_t b = readRegister(REG_FIFO); + + // restore FIFO address + writeRegister(REG_FIFO_ADDR_PTR, currentAddress); + + return b; +} + +void LoRaClass::flush() +{ +} + +#ifndef ARDUINO_SAMD_MKRWAN1300 +void LoRaClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::onTxDone(void(*callback)()) +{ + _onTxDone = callback; + + if (callback) { + pinMode(_dio0, INPUT); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::receive(int size) +{ + + writeRegister(REG_DIO_MAPPING_1, 0x00); // DIO0 => RXDONE + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); +} +#endif + +void LoRaClass::idle() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); +} + +void LoRaClass::sleep() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); +} + +void LoRaClass::setTxPower(int level, int outputPin) +{ + if (PA_OUTPUT_RFO_PIN == outputPin) { + // RFO + if (level < 0) { + level = 0; + } else if (level > 14) { + level = 14; + } + + writeRegister(REG_PA_CONFIG, 0x70 | level); + } else { + // PA BOOST + if (level > 17) { + if (level > 20) { + level = 20; + } + + // subtract 3 from level, so 18 - 20 maps to 15 - 17 + level -= 3; + + // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) + writeRegister(REG_PA_DAC, 0x87); + setOCP(140); + } else { + if (level < 2) { + level = 2; + } + //Default value PA_HF/LF or +17dBm + writeRegister(REG_PA_DAC, 0x84); + setOCP(100); + } + + writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); + } +} + +void LoRaClass::setFrequency(long frequency) +{ + _frequency = frequency; + + uint64_t frf = ((uint64_t)frequency << 19) / 32000000; + + writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); + writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); + writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); +} + +int LoRaClass::getSpreadingFactor() +{ + return readRegister(REG_MODEM_CONFIG_2) >> 4; +} + +void LoRaClass::setSpreadingFactor(int sf) +{ + if (sf < 6) { + sf = 6; + } else if (sf > 12) { + sf = 12; + } + + if (sf == 6) { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); + writeRegister(REG_DETECTION_THRESHOLD, 0x0c); + } else { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); + writeRegister(REG_DETECTION_THRESHOLD, 0x0a); + } + + writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); + setLdoFlag(); +} + +long LoRaClass::getSignalBandwidth() +{ + byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); + + switch (bw) { + case 0: return 7.8E3; + case 1: return 10.4E3; + case 2: return 15.6E3; + case 3: return 20.8E3; + case 4: return 31.25E3; + case 5: return 41.7E3; + case 6: return 62.5E3; + case 7: return 125E3; + case 8: return 250E3; + case 9: return 500E3; + } + + return -1; +} + +void LoRaClass::setSignalBandwidth(long sbw) +{ + int bw; + + if (sbw <= 7.8E3) { + bw = 0; + } else if (sbw <= 10.4E3) { + bw = 1; + } else if (sbw <= 15.6E3) { + bw = 2; + } else if (sbw <= 20.8E3) { + bw = 3; + } else if (sbw <= 31.25E3) { + bw = 4; + } else if (sbw <= 41.7E3) { + bw = 5; + } else if (sbw <= 62.5E3) { + bw = 6; + } else if (sbw <= 125E3) { + bw = 7; + } else if (sbw <= 250E3) { + bw = 8; + } else /*if (sbw <= 250E3)*/ { + bw = 9; + } + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); + setLdoFlag(); +} + +void LoRaClass::setLdoFlag() +{ + // Section 4.1.1.5 + long symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ; + + // Section 4.1.1.6 + boolean ldoOn = symbolDuration > 16; + + uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); + bitWrite(config3, 3, ldoOn); + writeRegister(REG_MODEM_CONFIG_3, config3); +} + +void LoRaClass::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + int cr = denominator - 4; + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); +} + +void LoRaClass::setPreambleLength(long length) +{ + writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); + writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); +} + +void LoRaClass::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD, sw); +} + +void LoRaClass::enableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); +} + +void LoRaClass::disableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); +} + +void LoRaClass::enableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x66); + writeRegister(REG_INVERTIQ2, 0x19); +} + +void LoRaClass::disableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x27); + writeRegister(REG_INVERTIQ2, 0x1d); +} + +void LoRaClass::setOCP(uint8_t mA) +{ + uint8_t ocpTrim = 27; + + if (mA <= 120) { + ocpTrim = (mA - 45) / 5; + } else if (mA <=240) { + ocpTrim = (mA + 30) / 10; + } + + writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim)); +} + +byte LoRaClass::random() +{ + return readRegister(REG_RSSI_WIDEBAND); +} + +void LoRaClass::setPins(int ss, int reset, int dio0) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; +} + +void LoRaClass::setSPI(SPIClass& spi) +{ + _spi = &spi; +} + +void LoRaClass::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void LoRaClass::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 128; i++) { + out.print("0x"); + out.print(i, HEX); + out.print(": 0x"); + out.println(readRegister(i), HEX); + } +} + +void LoRaClass::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); +} + +void LoRaClass::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); +} + +void LoRaClass::handleDio0Rise() +{ + int irqFlags = readRegister(REG_IRQ_FLAGS); + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + + if ((irqFlags & IRQ_RX_DONE_MASK) != 0) { + // received a packet + _packetIndex = 0; + + // read packet length + int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + if (_onReceive) { + _onReceive(packetLength); + } + } + else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) { + if (_onTxDone) { + _onTxDone(); + } + } + } +} + +uint8_t LoRaClass::readRegister(uint8_t address) +{ + return singleTransfer(address & 0x7f, 0x00); +} + +void LoRaClass::writeRegister(uint8_t address, uint8_t value) +{ + singleTransfer(address | 0x80, value); +} + +uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) +{ + uint8_t response; + + digitalWrite(_ss, LOW); + + _spi->beginTransaction(_spiSettings); + _spi->transfer(address); + response = _spi->transfer(value); + _spi->endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} + +ISR_PREFIX void LoRaClass::onDio0Rise() +{ + LoRa.handleDio0Rise(); +} + +LoRaClass LoRa; diff --git a/libraries/LoRa/src/LoRa.h b/libraries/LoRa/src/LoRa.h new file mode 100644 index 0000000..c1671c1 --- /dev/null +++ b/libraries/LoRa/src/LoRa.h @@ -0,0 +1,126 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LORA_H +#define LORA_H + +#include +#include + +#if defined(ARDUINO_SAMD_MKRWAN1300) +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 200000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN -1 +#elif defined(ARDUINO_SAMD_MKRWAN1310) +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 200000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN LORA_IRQ +#else +#define LORA_DEFAULT_SPI SPI +#define LORA_DEFAULT_SPI_FREQUENCY 8E6 +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 +#endif + +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +class LoRaClass : public Stream { +public: + LoRaClass(); + + int begin(long frequency); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(bool async = false); + + int parsePacket(int size = 0); + int packetRssi(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + +#ifndef ARDUINO_SAMD_MKRWAN1300 + void onReceive(void(*callback)(int)); + void onTxDone(void(*callback)()); + + void receive(int size = 0); +#endif + void idle(); + void sleep(); + + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + void setFrequency(long frequency); + void setSpreadingFactor(int sf); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + void enableCrc(); + void disableCrc(); + void enableInvertIQ(); + void disableInvertIQ(); + + void setOCP(uint8_t mA); // Over Current Protection control + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); + void setSPI(SPIClass& spi); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + bool isTransmitting(); + + int getSpreadingFactor(); + long getSignalBandwidth(); + + void setLdoFlag(); + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + +private: + SPISettings _spiSettings; + SPIClass* _spi; + int _ss; + int _reset; + int _dio0; + long _frequency; + int _packetIndex; + int _implicitHeaderMode; + void (*_onReceive)(int); + void (*_onTxDone)(); +}; + +extern LoRaClass LoRa; + +#endif diff --git a/libraries/LoRa_Node/README.adoc b/libraries/LoRa_Node/README.adoc new file mode 100644 index 0000000..80a6c7d --- /dev/null +++ b/libraries/LoRa_Node/README.adoc @@ -0,0 +1,38 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +--- Revised BSD License --- +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/libraries/LoRa_Node/examples/TinkerkitExample/TinkerkitExample.ino b/libraries/LoRa_Node/examples/TinkerkitExample/TinkerkitExample.ino new file mode 100644 index 0000000..81cd2da --- /dev/null +++ b/libraries/LoRa_Node/examples/TinkerkitExample/TinkerkitExample.ino @@ -0,0 +1,112 @@ +/* + * TinkerkitExample.ino + * + * This example shows how to use the connectors on the shield + * to interact with the LoRaWan network. + * Connect a tinkerkit button to IN2, a tinkerkit LDR sensor + * to IN3, a tinkerkit buzzer to OUT5 and a tinkerkit led to + * OUT6. + * Every time the button is pressed a packet cointaining the + * value read from the LDR sensor and a count of the number + * of button pressure will be sent (note that the packet is + * sent in the Cayenne format). If the node receive a packet + * with value 'a' the buzzer will sound. Any other received + * packet will change the led's status. + + * Change the keys below to fit the application in your server. + * + * Note : If you're using this example with an Arduino Primo download + * the ArduinoLowPower library from the library manager. + * + * This example code is in the public domain. + * + * created April 2017 + * by chiara@arduino.org + * + *********************************************************************** + * NOTE: + * Frequency is defined in config.h file inside the src folder of this + * library. Edit this file to select the frequency that fit your region. + *********************************************************************** + * + */ + +#include "LoRaNode.h" + +const char * appEui = "00250C0000010001"; +const char * appKey = "0E708C34BBDA282AA8691318BCD06BBB"; +const char * devEui = "00250C010000062F"; + +volatile int ledState = LOW; +int lastButtonState = LOW; +int buttonState; +char buttonCnt = 0; +long lastDebounceTime = 0; +long debounceDelay = 50; + +const int led = 6; +const int button = A2; +const int buzzer = 5; +const int ldrSensor = A3; + +// | cayenne button | illuminance senor | +char frame[7] = {0x00, 0x01, 0x00, 0x01, 0x65, 0x00, 0x00}; + +void setup() { + pinMode(31, OUTPUT); + digitalWrite(31, HIGH); + + pinMode(button, INPUT); + pinMode(led, OUTPUT); + pinMode(ldrSensor, INPUT); + digitalWrite(ldrSensor, ledState); + + Serial.begin(9600); + + node.joinOTAA(appEui, appKey, devEui); + //register callback for incoming messages + node.onReceive(readMsg); + //begin initialization + node.begin(); + + node.showStatus(); +} + +void loop() { + //debounce button to send the frame just once at pressure + int reading = digitalRead(button); + if (reading != lastButtonState) { + lastDebounceTime = millis(); + } + + if ((millis() - lastDebounceTime) > debounceDelay) { + + if (reading != buttonState) { + buttonState = reading; + if (buttonState == HIGH) { + // read a value from the LDR sensor and send it + int light = analogRead(ldrSensor); + // illuminance value has to be 2 byte long (MSB) + frame[5] = (light & 0xFF00) >> 8; + frame[6] = light & 0x00FF; + // send button count also + frame[2] = ++buttonCnt; + + node.sendFrame(frame, sizeof(frame), 2); + } + } + } + lastButtonState = reading; +} + +void readMsg(unsigned char * rcvData, int dim, int port){ + // make a sound if the desidered value is received + if(rcvData[0] == 'a'){ + tone(buzzer, 262, 500); + } + // toggle the led if anything else was received + else{ + ledState = !ledState; + digitalWrite(led, ledState); + } +} diff --git a/libraries/LoRa_Node/examples/loraABP/loraABP.ino b/libraries/LoRa_Node/examples/loraABP/loraABP.ino new file mode 100644 index 0000000..f81f7bf --- /dev/null +++ b/libraries/LoRa_Node/examples/loraABP/loraABP.ino @@ -0,0 +1,69 @@ +/* + * loraABP.ino + * + * A simple example to use the Arduino LoRa Node shield. + * This example join the LoRaWan network and send periodically + * packets to the server. The activation method used is + * Activation By Personalization (ABP). + * If you register an application on a server with the same + * keys used in this example you will be able to see the + * packets sent from the node. + * Change the keys below to fit the application in your server. + * + * Note : If you're using this example with an Arduino Primo download + * the ArduinoLowPower library from the library manager. + * + * This example code is in the public domain. + * + * created April 2017 + * by chiara@arduino.org + * + *********************************************************************** + * NOTE: + * Frequency is defined in config.h file inside the src folder of this + * library. Edit this file to select the frequency that fit your region. + *********************************************************************** + * + */ + +// Application's keys +const char * deviceAddress = "26011AD0"; +const char * networkSessionKey = "F60F30CF0900CE09E07301104E02B0D3"; +const char * applicationSessionKey = "0CE04D0B30C80970D30A00A101D01001"; + +#include "LoRaNode.h" + +void setup() { + pinMode(31, OUTPUT); + digitalWrite(31, HIGH); + + Serial.begin(9600); + + //register the keys for this application + node.joinABP(deviceAddress, networkSessionKey, applicationSessionKey); + //register callback for incoming messages + node.onReceive(readMsg); + //begin initialization + node.begin(); + //show the node's main parameters + node.showStatus(); +} + +void loop() { + //send a frame every 10 seconds + char frame[] = {0x00, 0x00, 0x00, 0xA0}; + + // data , dataLength , port + node.sendFrame(frame, sizeof(frame), 2); + + //wait 10 seconds sleeping to save power + node.sleep(10000); + } + +void readMsg(unsigned char * rcvData, int dim, int port){ + Serial.print("data = "); + for(int i=0; i +#include + +Adafruit_MLX90614 mlx = Adafruit_MLX90614(); + +const char * appEui = "00250C0000010001"; +const char * appKey = "0E708C34BBDA282AA8691318BCD06BBB"; +const char * devEui = "00250C010000062F"; + + +int ledState = LOW; +int lastButtonState = LOW; +int buttonState; +char buttonCnt = 0; +long lastDebounceTime = 0; +long debounceDelay = 50; + +const int led = 6; +const int button = A2; + +// | cayenne button | cayenne temp sensor | +char frame[7] = {0x00, 0x01, 0x00, 0x01, 0x67, 0x00, 0x00}; + +void setup() { + pinMode(31, OUTPUT); + digitalWrite(31, HIGH); + + pinMode(button, INPUT); + pinMode(led, OUTPUT); + + Serial.begin(9600); + + + //node.joinABP(devAddr, nwkSessionKey, appSessionKey); + node.joinOTAA(appEui, appKey, devEui); + //register callback for incoming messages + node.onReceive(readMsg); + //begin initialization + node.begin(); + + node.showStatus(); + + + mlx.begin(); +} + +void loop() { + //debounce button to send the frame just once at pressure + int reading = digitalRead(button); + if (reading != lastButtonState) { + lastDebounceTime = millis(); + } + + if ((millis() - lastDebounceTime) > debounceDelay) { + + if (reading != buttonState) { + buttonState = reading; + if (buttonState == HIGH) { + + // read a value from the temperature sensor and send it + float temp = mlx.readAmbientTempC(); + // temperature value has to be 2 byte long (MSB) + // example: 27.25°C => 2725 + int temperature = temp * 100; + frame[5] = (temperature & 0xFF00) >> 8; + frame[6] = temperature & 0x00FF; + // send button count also + frame[2] = ++buttonCnt; + node.sendFrame(frame, sizeof(frame), 2); + Serial.println("data sent"); + } + } + } + lastButtonState = reading; +} + +void readMsg(unsigned char * rcvData, int dim, int port){ + // toggle the led if the desidered value is received + if(rcvData[0] == 'a'){ + ledState = !ledState; + digitalWrite(led, ledState); + } +} \ No newline at end of file diff --git a/libraries/LoRa_Node/keywords.txt b/libraries/LoRa_Node/keywords.txt new file mode 100644 index 0000000..c2892cc --- /dev/null +++ b/libraries/LoRa_Node/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map LoRaNode +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +node KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +onReceive KEYWORD2 +begin KEYWORD2 +joinOTAA KEYWORD2 +joinABP KEYWORD2 +sendFrame KEYWORD2 +poll KEYWORD2 +showStatus KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/LoRa_Node/library.properties b/libraries/LoRa_Node/library.properties new file mode 100644 index 0000000..7017fa2 --- /dev/null +++ b/libraries/LoRa_Node/library.properties @@ -0,0 +1,9 @@ +name=LoRa Node +version=1.0.1 +author=Arduino +maintainer=Arduino +sentence=LoRa Node library for Arduino LoRa Node Shield. +paragraph=This library allows you to send and receive LoRaWan packets +category=Communication +url=http://www.arduino.org/learning/reference/LoRaNode +architectures=nrf52 diff --git a/libraries/LoRa_Node/src/LoRaNode.cpp b/libraries/LoRa_Node/src/LoRaNode.cpp new file mode 100644 index 0000000..b4c9ca5 --- /dev/null +++ b/libraries/LoRa_Node/src/LoRaNode.cpp @@ -0,0 +1,852 @@ +/* + Copyright (c) 2016 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 "LoRaNode.h" + +#ifdef ARDUINO_ARCH_NRF52 + #include "ArduinoLowPower.h" +#endif + +#define NR_OF_TRIALS 48 + +struct ComplianceTest_s +{ + bool Running; + uint8_t State; + bool IsTxConfirmed; + uint8_t AppPort; + uint8_t AppDataSize; + uint8_t *AppDataBuffer; + uint16_t DownLinkCounter; + bool LinkCheck; + uint8_t DemodMargin; + uint8_t NbGateways; +}ComplianceTest; + +static uint8_t IsTxConfirmed = false; +static uint8_t AppPort = 1; +static uint8_t AppDataSize = 16; + +void convertAddress(uint32_t& DevAddr, const char * devAddr){ + // address is MSB + uint8_t first; + uint8_t second; + uint8_t third; + uint8_t fourth; + first = (devAddr[7] >= 'A' && devAddr[7] <= 'F') ? (devAddr[7] - 'A' + 10) : (devAddr[7] >= 'a' && devAddr[7] <= 'f') ? (devAddr[7] - 'a' + 10) : (devAddr[7] - '0'); + first += (devAddr[6] >= 'A' && devAddr[6] <= 'F') ? ((devAddr[6] - 'A' + 10) << 4) : (devAddr[6] >= 'a' && devAddr[6] <= 'f') ? ((devAddr[6] - 'a' + 10) << 4) : ((devAddr[6] - '0') << 4); + second = (devAddr[5] >= 'A' && devAddr[5] <= 'F') ? (devAddr[5] - 'A' + 10) : (devAddr[5] >= 'a' && devAddr[5] <= 'f') ? (devAddr[5] - 'a' + 10) : (devAddr[5] - '0'); + second += (devAddr[4] >= 'A' && devAddr[4] <= 'F') ? ((devAddr[4] - 'A' + 10) << 4) : (devAddr[4] >= 'a' && devAddr[4] <= 'f') ? ((devAddr[4] - 'a' + 10) << 4) : ((devAddr[4] - '0') << 4); + third = (devAddr[3] >= 'A' && devAddr[3] <= 'F') ? (devAddr[3] - 'A' + 10) : (devAddr[3] >= 'a' && devAddr[3] <= 'f') ? (devAddr[3] - 'a' + 10) : (devAddr[3] - '0'); + third += (devAddr[2] >= 'A' && devAddr[2] <= 'F') ? ((devAddr[2] - 'A' + 10) << 4) : (devAddr[2] >= 'a' && devAddr[2] <= 'f') ? ((devAddr[2] - 'a' + 10) << 4) : ((devAddr[2] - '0') << 4); + fourth = (devAddr[1] >= 'A' && devAddr[1] <= 'F') ? (devAddr[1] - 'A' + 10) : (devAddr[1] >= 'a' && devAddr[1] <= 'f') ? (devAddr[1] - 'a' + 10) : (devAddr[1] - '0'); + fourth += (devAddr[0] >= 'A' && devAddr[0] <= 'F') ? ((devAddr[0] - 'A' + 10) << 4) : (devAddr[0] >= 'a' && devAddr[0] <= 'f') ? ((devAddr[0] - 'a' + 10) << 4) : ((devAddr[0] - '0') << 4); + DevAddr = first | (second << 8) | (third << 16) | (fourth << 24); +} + +void convertKey(uint8_t* skey, const char * _skey, uint8_t len){ + for(int i = 0; i < len; i++){ + skey[i] = (_skey[(i*2) + 1] >= 'A' && _skey[(i*2) + 1] <= 'F') ? (_skey[(i*2) + 1] - 'A' + 10) : (_skey[(i*2) + 1] >= 'a' && _skey[(i*2) + 1] <= 'f') ? (_skey[(i*2) + 1] - 'a' + 10) : (_skey[(i*2) + 1] - '0'); + skey[i] += (_skey[(i*2)] >= 'A' && _skey[(i*2)] <= 'F') ? ((_skey[(i*2)] - 'A' + 10) << 4) : (_skey[(i*2)] >= 'a' && _skey[(i*2)] <= 'f') ? ((_skey[(i*2)] - 'a' + 10) << 4) : ((_skey[(i*2)] - '0') << 4); + } +} + +static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) +{ + if( mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + switch( mcpsConfirm->McpsRequest ) + { + case MCPS_UNCONFIRMED: + { + // Check Datarate + // Check TxPower + break; + } + case MCPS_CONFIRMED: + { + // Check Datarate + // Check TxPower + // Check AckReceived + // Check NbTrials + break; + } + case MCPS_PROPRIETARY: + { + break; + } + default: + break; + } + } + node.nextTx = true; +} + +/*! + * \brief MCPS-Indication event function + * + * \param [IN] mcpsIndication - Pointer to the indication structure, + * containing indication attributes. + */ +static void McpsIndication( McpsIndication_t *mcpsIndication ) +{ + if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK ) + { + return; + } + + switch( mcpsIndication->McpsIndication ) + { + case MCPS_UNCONFIRMED: + { + break; + } + case MCPS_CONFIRMED: + { + break; + } + case MCPS_PROPRIETARY: + { + break; + } + case MCPS_MULTICAST: + { + break; + } + default: + break; + } + + // Check Multicast + // Check Port + // Check Datarate + // Check FramePending + // Check Buffer + // Check BufferSize + // Check Rssi + // Check Snr + // Check RxSlot + + if( mcpsIndication->RxData == true ) + { + if(node.onReceiveCallback) + node.onReceiveCallback(mcpsIndication->Buffer, mcpsIndication->BufferSize, mcpsIndication->Port); + switch( mcpsIndication->Port ) + { + case 1: // The application LED can be controlled on port 1 or 2 + case 2: + if( mcpsIndication->BufferSize == 1 ) + { + // AppLedStateOn = mcpsIndication->Buffer[0] & 0x01; + // GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 0 : 1 ); + } + break; + case 224: + if( ComplianceTest.Running == false ) + { + // Check compliance test enable command (i) + if( ( mcpsIndication->BufferSize == 4 ) && + ( mcpsIndication->Buffer[0] == 0x01 ) && + ( mcpsIndication->Buffer[1] == 0x01 ) && + ( mcpsIndication->Buffer[2] == 0x01 ) && + ( mcpsIndication->Buffer[3] == 0x01 ) ) + { + IsTxConfirmed = false; + AppPort = 224; + AppDataSize = 2; + ComplianceTest.DownLinkCounter = 0; + ComplianceTest.LinkCheck = false; + ComplianceTest.DemodMargin = 0; + ComplianceTest.NbGateways = 0; + ComplianceTest.Running = true; + ComplianceTest.State = 1; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = true; + LoRaMacMibSetRequestConfirm( &mibReq ); + +#if defined( REGION_EU868 ) + LoRaMacTestSetDutyCycleOn( false ); +#endif + } + } + else + { + ComplianceTest.State = mcpsIndication->Buffer[0]; + switch( ComplianceTest.State ) + { + case 0: // Check compliance test disable command (ii) + IsTxConfirmed = node._confirmed;//LORAWAN_CONFIRMED_MSG_ON; + AppPort = node._appPort;//LORAWAN_APP_PORT; + AppDataSize = node._appDataSize;//LORAWAN_APP_DATA_SIZE; + ComplianceTest.DownLinkCounter = 0; + ComplianceTest.Running = false; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = 1;//LORAWAN_ADR_ON; + LoRaMacMibSetRequestConfirm( &mibReq ); +#if defined( REGION_EU868 ) + LoRaMacTestSetDutyCycleOn(true /*LORAWAN_DUTYCYCLE_ON*/); +#endif + break; + case 1: // (iii, iv) + AppDataSize = 2; + break; + case 2: // Enable confirmed messages (v) + IsTxConfirmed = true; + ComplianceTest.State = 1; + break; + case 3: // Disable confirmed messages (vi) + IsTxConfirmed = false; + ComplianceTest.State = 1; + break; + case 4: // (vii) + AppDataSize = mcpsIndication->BufferSize; + + node._appData[0] = 4; + for( uint8_t i = 1; i < AppDataSize; i++ ) + { + node._appData[i] = mcpsIndication->Buffer[i] + 1; + } + break; + case 5: // (viii) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_LINK_CHECK; + LoRaMacMlmeRequest( &mlmeReq ); + } + break; + case 6: // (ix) + { + MlmeReq_t mlmeReq; + + // Disable TestMode and revert back to normal operation + IsTxConfirmed = node._confirmed;//LORAWAN_CONFIRMED_MSG_ON; + AppPort = node._appPort;//LORAWAN_APP_PORT; + AppDataSize = node._appDataSize;//LORAWAN_APP_DATA_SIZE; + ComplianceTest.DownLinkCounter = 0; + ComplianceTest.Running = false; + + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = true;//LORAWAN_ADR_ON; + LoRaMacMibSetRequestConfirm( &mibReq ); +#if defined( REGION_EU868 ) + LoRaMacTestSetDutyCycleOn(true /*LORAWAN_DUTYCYCLE_ON */); +#endif + + mlmeReq.Type = MLME_JOIN; + + uint8_t DevEui[8]; + if(node._devEui == NULL) + BoardGetUniqueId( DevEui ); + else + convertKey(DevEui, node._devEui, 8); + + mlmeReq.Req.Join.DevEui = DevEui; + mlmeReq.Req.Join.AppEui = (unsigned char*)node._appEui; + mlmeReq.Req.Join.AppKey = (unsigned char*)node._appKey; + mlmeReq.Req.Join.NbTrials = NR_OF_TRIALS; + + LoRaMacMlmeRequest( &mlmeReq ); + // DeviceState = DEVICE_STATE_SLEEP; + + } + break; + case 7: // (x) + { + if( mcpsIndication->BufferSize == 3 ) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_TXCW; + mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); + LoRaMacMlmeRequest( &mlmeReq ); + } + else if( mcpsIndication->BufferSize == 7 ) + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_TXCW_1; + mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); + mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100; + mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6]; + LoRaMacMlmeRequest( &mlmeReq ); + } + ComplianceTest.State = 1; + } + break; + default: + break; + } + } + break; + default: + break; + } + } +} + +/*! + * \brief MLME-Confirm event function + * + * \param [IN] mlmeConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) +{ + switch( mlmeConfirm->MlmeRequest ) + { + case MLME_JOIN: + { + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + // Status is OK, node has joined the network + node.joined = true; + } + else + { + // Join was not successful. Try to join again + MlmeReq_t mlmeReq; + + uint8_t DevEui[8]; + uint8_t AppEui[8]; + uint8_t AppKey[16]; + if(node._devEui == NULL) + BoardGetUniqueId( DevEui ); + else + convertKey(DevEui, node._devEui, 8); + convertKey(AppEui, node._appEui, 8); + convertKey(AppKey, node._appKey, 16); + + mlmeReq.Type = MLME_JOIN; + + mlmeReq.Req.Join.DevEui = DevEui; + mlmeReq.Req.Join.AppEui = AppEui; + mlmeReq.Req.Join.AppKey = AppKey; + mlmeReq.Req.Join.NbTrials = NR_OF_TRIALS; + if( node.nextTx == true ) + { + LoRaMacMlmeRequest( &mlmeReq ); + } + + } + break; + } + case MLME_LINK_CHECK: + { + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + // Check DemodMargin + // Check NbGateways + if( ComplianceTest.Running == true ) + { + ComplianceTest.LinkCheck = true; + ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin; + ComplianceTest.NbGateways = mlmeConfirm->NbGateways; + } + } + break; + } + default: + break; + } + // } + node.nextTx = true; +} + + +LoRaNode::LoRaNode() : + nextTx(true), + joined(false), + onReceiveCallback(NULL), + _appDataSize(16), + _appEui(NULL), + _appKey(NULL), + _devEui(NULL), + _devAddr(NULL), + _nwkSKey(NULL), + _appSKey(NULL), + _initialized(false), + _dr(DR_0) +{ +#ifdef REGION_EU868 + _power = 1; +#elif defined (REGION_US915) | defined (REGION_US915_HYBRID) + _power = 5; +#endif +} + +void LoRaNode::onReceive(LoRaNodeEventHandler handler){ + onReceiveCallback = handler; +} + +void LoRaNode::joinOTAA(const char *appEui, const char *appKey){ + _otaa = true; + _appEui = appEui; + _appKey = appKey; + _devEui = NULL; +} + +void LoRaNode::joinOTAA(const char *appEui,const char *appKey, const char *devEui){ + _otaa = true; + _appEui = appEui; + _appKey = appKey; + _devEui = devEui; +} + +void LoRaNode::joinABP(const char * devAddr, const char * nwkSKey, const char * appSKey){ + _otaa = false; + _devAddr = devAddr; + _nwkSKey = nwkSKey; + _appSKey = appSKey; +} + +void LoRaNode::begin(){ + BoardInitMcu( ); + BoardInitPeriph( ); + _initialized = true; + + _LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; + _LoRaMacPrimitives.MacMcpsIndication = McpsIndication; + _LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; +#if defined( REGION_EU868 ) + LoRaMacInitialization( &_LoRaMacPrimitives, &_LoRaMacCallbacks, LORAMAC_REGION_EU868, _dr, _power ); +#elif defined( REGION_US915 ) + LoRaMacInitialization( &_LoRaMacPrimitives, &_LoRaMacCallbacks, LORAMAC_REGION_US915, _dr, _power ); +#elif defined( REGION_US915_HYBRID ) + LoRaMacInitialization( &_LoRaMacPrimitives, &_LoRaMacCallbacks, LORAMAC_REGION_US915_HYBRID, _dr, _power ); +#else + #error "Please define a region in the compiler options." +#endif + + _mibReq.Type = MIB_ADR; + _mibReq.Param.AdrEnable = 1;//LORAWAN_ADR_ON; + LoRaMacMibSetRequestConfirm( &_mibReq ); + + _mibReq.Type = MIB_PUBLIC_NETWORK; + _mibReq.Param.EnablePublicNetwork = true;//LORAWAN_PUBLIC_NETWORK; + LoRaMacMibSetRequestConfirm( &_mibReq ); + +#if defined( REGION_EU868 ) + LoRaMacTestSetDutyCycleOn( true/*LORAWAN_DUTYCYCLE_ON*/ ); + + LoRaMacChannelAdd( 3, ( ChannelParams_t )LC4 ); + LoRaMacChannelAdd( 4, ( ChannelParams_t )LC5 ); + LoRaMacChannelAdd( 5, ( ChannelParams_t )LC6 ); + LoRaMacChannelAdd( 6, ( ChannelParams_t )LC7 ); + LoRaMacChannelAdd( 7, ( ChannelParams_t )LC8 ); + LoRaMacChannelAdd( 8, ( ChannelParams_t )LC9 ); + LoRaMacChannelAdd( 9, ( ChannelParams_t )LC10 ); + + _mibReq.Type = MIB_RX2_CHANNEL; + _mibReq.Param.Rx2Channel = ( Rx2ChannelParams_t ){ 869525000, DR_3 }; + LoRaMacMibSetRequestConfirm( &_mibReq ); +#endif + + + if(_otaa){ //OTAA + MlmeReq_t mlmeReq; + + uint8_t DevEui[8]; + uint8_t AppEui[8]; + uint8_t AppKey[16]; + if(_devEui == NULL) + BoardGetUniqueId( DevEui ); + else + convertKey(DevEui, _devEui, 8); + convertKey(AppEui, _appEui, 8); + convertKey(AppKey, _appKey, 16); + + + mlmeReq.Type = MLME_JOIN; + + mlmeReq.Req.Join.DevEui = DevEui; + mlmeReq.Req.Join.AppEui = AppEui; + mlmeReq.Req.Join.AppKey = AppKey; + mlmeReq.Req.Join.NbTrials = NR_OF_TRIALS; + do{ + LoRaMacMlmeRequest( &mlmeReq ); + delay(7000); + + } + + while(!joined); + } + else{ //ABP + uint32_t DevAddr; + // Choose a random device address if not already defined in Comissioning.h + if( _devAddr == NULL ) + { + // Random seed initialization + srand1( BoardGetRandomSeed( ) ); + + // Choose a random device address + DevAddr = randr( 0, 0x01FFFFFF ); + } + else + convertAddress(DevAddr, _devAddr); + uint8_t NwkSKey[16]; + uint8_t AppSKey[16]; + convertKey(NwkSKey, _nwkSKey, 16); + convertKey(AppSKey, _appSKey, 16); + _mibReq.Type = MIB_NET_ID; + _mibReq.Param.NetID = 0;//LORAWAN_NETWORK_ID; + LoRaMacMibSetRequestConfirm( &_mibReq ); + + _mibReq.Type = MIB_DEV_ADDR; + _mibReq.Param.DevAddr = DevAddr; + LoRaMacMibSetRequestConfirm( &_mibReq ); + + _mibReq.Type = MIB_NWK_SKEY; + _mibReq.Param.NwkSKey = NwkSKey; + LoRaMacMibSetRequestConfirm( &_mibReq ); + + _mibReq.Type = MIB_APP_SKEY; + _mibReq.Param.AppSKey = AppSKey; + LoRaMacMibSetRequestConfirm( &_mibReq ); + + _mibReq.Type = MIB_NETWORK_JOINED; + _mibReq.Param.IsNetworkJoined = true; + LoRaMacMibSetRequestConfirm( &_mibReq ); + + } +} + +void LoRaNode::sendFrame(char frame[], int dim, int port, bool confirmed){ + //TODO: check maximum length + for(int i = 0; i < dim; i++) + _appData[i] = frame[i]; + + AppPort = port; + AppDataSize = dim; + nextTx = send(port, confirmed); +} + +void LoRaNode::poll(int port, bool confirm) { + char payload[] = { 0x00 }; + return sendFrame(payload, 1, port, confirm); +} + +void LoRaNode::showStatus(){ + Serial.println(); + Serial.println("LoRa Node parameters:"); + Serial.print("Frequency: "); +#ifdef REGION_EU868 + Serial.print(868); +#else + Serial.print(915); +#endif + Serial.println(" MHz"); + + if(_initialized){ // print these data only if begin function was called + + //FXOSC = 32MHz + uint8_t msb = Radio.Read(0x02); + uint8_t lsb = Radio.Read(0x03); + uint32_t br = (msb<<8) | lsb; + Serial.print("BitRate: "); + Serial.print(32000000/br); + Serial.println(" b/s"); + + Serial.print("Datarate SF"); + Serial.println((Radio.Read(0x1E) & 0b11110000)>>4); + + Serial.print("Bandwidth: "); + uint8_t bw = ((Radio.Read(0x1D) & 0b11110000) >> 4); + switch(bw){ + case 0: + Serial.println("7800 Hz"); + break; + case 1: + Serial.println("10400 Hz"); + break; + case 2: + Serial.println("15600 Hz"); + break; + case 3: + Serial.println("20800 Hz"); + break; + case 4: + Serial.println("31250 Hz"); + break; + case 5: + Serial.println("41700 Hz"); + break; + case 6: + Serial.println("62500 Hz"); + break; + case 7: + Serial.println("125000 Hz"); + break; + case 8: + Serial.println("250000 Hz"); + break; + case 9: + Serial.println("500000 Hz"); + break; + } + } + + Serial.println(); + if(_otaa){ + if(_appKey == NULL && _appEui == NULL && _devEui == NULL){ + Serial.println("No parameters defined yet."); + return; + } + Serial.print("Application Key: {"); + for(int i = 0; i < 32; i += 2){ + Serial.print(" 0x"); + Serial.print(_appKey[i]); + Serial.print(_appKey[i+1]); + } + Serial.println(" }"); + Serial.print("Application Eui: {"); + for(int i = 0; i < 16; i += 2){ + Serial.print(" 0x"); + Serial.print(_appEui[i]); + Serial.print(_appEui[i+1]); + } + Serial.println(" }"); + if(_devEui != NULL){ + Serial.print("Device Eui: {"); + for(int i = 0; i < 16; i += 2){ + Serial.print(" 0x"); + Serial.print(_devEui[i]); + Serial.print(_devEui[i+1]); + } + Serial.println(" }"); + } + else{ + uint8_t DevEui[8]; + BoardGetUniqueId( DevEui ); + Serial.print("Device Eui: {"); + for(int i = 0; i < 8; i++){ + Serial.print(" 0x"); + if(DevEui[i] <= 0x0F) + Serial.print(0); + Serial.print(DevEui[i], HEX); + } + Serial.println(" }"); + } + if(joined) + Serial.println("The node has joined the network."); + else + Serial.println("The node hasn't joined the network yet."); + } + else{ //ABP + if(_devAddr == NULL && _nwkSKey == NULL && _appSKey == NULL){ + Serial.println("No parameters defined yet."); + return; + } + Serial.print("Device address: 0x"); + Serial.println(_devAddr); + Serial.print("Network Session Key: {"); + for(int i = 0; i < 32; i += 2){ + Serial.print(" 0x"); + Serial.print(_nwkSKey[i]); + Serial.print(_nwkSKey[i+1]); + } + Serial.println(" }"); + Serial.print("Application Session Key: {"); + for(int i = 0; i < 32; i += 2){ + Serial.print(" 0x"); + Serial.print(_appSKey[i]); + Serial.print(_appSKey[i+1]); + } + Serial.println(" }"); + } + Serial.println(); +} + + +void LoRaNode::sleep(uint32_t ms){ +#ifdef ARDUINO_ARCH_NRF52 + LowPower.sleep(ms); +#else + delay(ms); +#endif +} + +void LoRaNode::setSF(SF sf){ +#ifdef REGION_EU868 + switch(sf){ + case SF7: + _dr = DR_5; + break; + case SF8: + _dr = DR_4; + break; + case SF9: + _dr = DR_3; + case SF10: + _dr = DR_2; + break; + case SF11: + _dr = DR_1; + break; + case SF12: + _dr = DR_0; + break; + default: + _dr = DR_0; + break; + } +#elif defined (REGION_US915_HYBRID) | defined (REGION_US915) + switch(sf){ + case SF7: + _dr = DR_3; + break; + case SF8: + _dr = DR_2; + break; + case SF9: + _dr = DR_1; + case SF10: + _dr = DR_0; + break; + default: + _dr = DR_0; + break; + } +#endif +} + +void LoRaNode::setTxPower(int power){ + +#ifdef REGION_EU868 + if(power >= 16){ + _power = 0; + return; + } + if(power >= 14){ + _power = 1; + return; + } + if(power >= 12){ + _power = 2; + return; + } + if(power >= 10){ + _power = 3; + return; + } + if(power >= 8){ + _power = 4; + return; + } + if(power >= 6){ + _power = 5; + return; + } + if(power >= 4){ + _power = 6; + return; + } + else{ + _power = 7; + return; + } + +#elif defined (REGION_US915_HYBRID) | defined (REGION_US915) + if(power >= 30){ + _power = 0; + return; + } + if(power >= 28){ + _power = 1; + return; + } + if(power >= 26){ + _power = 2; + return; + } + if(power >= 24){ + _power = 3; + return; + } + if(power >= 22){ + _power = 4; + return; + } + if(power >= 20){ + _power = 5; + return; + } + if(power >= 18){ + _power = 6; + return; + } + if(power >= 16){ + _power = 7; + return; + } + if(power >= 14){ + _power = 8; + return; + } + + +#endif +} + +// private + +bool LoRaNode::send(int port, bool confirmed){ + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + + if( LoRaMacQueryTxPossible(AppDataSize, &txInfo) != LORAMAC_STATUS_OK ) + { + // Send empty frame in order to flush MAC commands + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE; + } + else + { + if(confirmed == false) + { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = AppPort; + mcpsReq.Req.Unconfirmed.fBuffer = _appData; + mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize; + mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE; + } + else + { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = AppPort; + mcpsReq.Req.Confirmed.fBuffer = _appData; + mcpsReq.Req.Confirmed.fBufferSize = AppDataSize; + mcpsReq.Req.Confirmed.NbTrials = NR_OF_TRIALS; + mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE; + } + } + + if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) + { + return false; + } + return true; +} + + + +LoRaNode node; diff --git a/libraries/LoRa_Node/src/LoRaNode.h b/libraries/LoRa_Node/src/LoRaNode.h new file mode 100644 index 0000000..5e2428d --- /dev/null +++ b/libraries/LoRa_Node/src/LoRaNode.h @@ -0,0 +1,126 @@ +/* + Copyright (c) 2016 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 +*/ + +#ifndef __LORA_NODE_H_ +#define __LORA_NODE_H_ + +#include "Arduino.h" +#define USE_MODEM_LORA + +#include "config.h" +#include "boards/arduino/board.h" + +#ifdef __cplusplus +extern "C"{ +#endif //__cplusplus + +#include "mac/region/Region.h" +#include "radio/radio.h" +#include "system/gpio.h" +#include "system/timer.h" +#include "mac/LoRaMac.h" + +#if defined ( REGION_EU868 ) + #include "mac/LoRaMacTest.h" +#endif + +#include "boards/mcu/arduino/utilities.h" + +#ifdef __cplusplus +} +#endif //__cplusplus + +#if defined ( REGION_EU868 ) + +#define LC4 { 867100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +#define LC5 { 867300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +#define LC6 { 867500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +#define LC7 { 867700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +#define LC8 { 867900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +#define LC9 { 868800000, 0, { ( ( DR_7 << 4 ) | DR_7 ) }, 2 } +#define LC10 { 868300000, 0, { ( ( DR_6 << 4 ) | DR_6 ) }, 1 } + +#endif + +#define LORAWAN_APP_DATA_MAX_SIZE 64 +#define LORAWAN_DEFAULT_DATARATE DR_0 + + +/*! + * LoRaWAN compliance tests support data + */ + +typedef void (*LoRaNodeEventHandler)(unsigned char * buffer, int buffSize, int port); + +enum SF{ + SF7, + SF8, + SF9, + SF10, + SF11, + SF12 +}; + +class LoRaNode { + public: + LoRaNode(); + + void onReceive(LoRaNodeEventHandler handler); //function for registering a callback to be called when a message is received + + void begin(); //begin initialization function + void joinOTAA(const char *appEui, const char *appKey); //function to configure the otaa parameters + void joinOTAA(const char *appEui, const char *appKey, const char *devEui); + void joinABP(const char * devAddr, const char * nwkSKey, const char * appSKey); //function to configure the ABP parameters + void sendFrame(char frame[], int dim, int port = 1, bool confirmed = false); //function to send a frame + void poll(int port, bool confirm = false); //function to send a dummy frame in order to open the receive windows + void showStatus(); //print node parameters + void sleep(uint32_t ms); + + void setSF(SF sf); + void setTxPower(int power); + + bool nextTx; + bool joined; + LoRaNodeEventHandler onReceiveCallback; + const char * _appEui; + const char * _appKey; + const char * _devEui; + uint8_t _appDataSize; //set function + uint8_t _appData[LORAWAN_APP_DATA_MAX_SIZE]; + uint8_t _appPort; + bool _confirmed; + + private: + bool send(int port, bool confirmed); + + bool _otaa; + const char * _devAddr; + const char * _nwkSKey; + const char * _appSKey; + LoRaMacPrimitives_t _LoRaMacPrimitives; + LoRaMacCallback_t _LoRaMacCallbacks; + MibRequestConfirm_t _mibReq; + bool _initialized; + uint8_t _dr; + uint8_t _power; + +}; + +extern LoRaNode node; + +#endif __LORA_NODE_H_ \ No newline at end of file diff --git a/libraries/LoRa_Node/src/boards/arduino/adc-board.c b/libraries/LoRa_Node/src/boards/arduino/adc-board.c new file mode 100644 index 0000000..8d440f8 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/adc-board.c @@ -0,0 +1,138 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Board ADC driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include "board.h" +#include "adc-board.h" + + +void AdcMcuInit( Adc_t *obj, PinNames adcInput ) +{ + // obj->Adc.Instance = ( ADC_TypeDef *)ADC1_BASE; + GpioInit( &obj->AdcInput, adcInput, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +} + +void AdcMcuFormat( Adc_t *obj, AdcResolution AdcRes, AdcNumConversion AdcNumConv, AdcTriggerConv AdcTrig, AdcDataAlignement AdcDataAlig ) +{ + // ADC_HandleTypeDef *adc; + + if( AdcRes == ADC_12_BIT ) + { + analogWriteResolution(12); + analogReadResolution(12); + } + else if( AdcRes == ADC_10_BIT ) + { + analogWriteResolution(10); + analogReadResolution(10); + } + else if( AdcRes == ADC_8_BIT ) + { + analogWriteResolution(8); + analogReadResolution(8); + } + else if( AdcRes == ADC_6_BIT ) + {//arduino boards don't support 6-bit resolution + analogWriteResolution(8); + analogReadResolution(8); + } + //not supported by Arduino boards + // if( AdcNumConv == SINGLE_CONVERSION ) + // { + // obj->Adc.Init.ContinuousConvMode = DISABLE; + // } + // else + // { + // obj->Adc.Init.ContinuousConvMode = ENABLE; + // } + + // if( AdcTrig == CONVERT_MANUAL_TRIG ) + // { + // obj->Adc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONVEDGE_NONE; + // } + // else if( AdcTrig == CONVERT_RISING_EDGE ) + // { + // obj->Adc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; + // } + // else if( AdcTrig == CONVERT_FALLING_EDGE ) + // { + // obj->Adc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_FALLING; + // } + // else + // { + // obj->Adc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISINGFALLING; + // } + + // if( AdcDataAlig == DATA_RIGHT_ALIGNED ) + // { + // obj->Adc.Init.DataAlign = ADC_DATAALIGN_RIGHT; + // } + // else + // { + // obj->Adc.Init.DataAlign = ADC_DATAALIGN_LEFT; + // } + + // obj->Adc.Init.NbrOfConversion = 1; + + // adc = &obj->Adc; + // HAL_ADC_Init( adc ); +} + +uint16_t AdcMcuRead( Adc_t *obj, uint8_t channel ) +{ + return analogRead(obj->AdcInput.pinIndex); + // ADC_HandleTypeDef *hadc; + // ADC_ChannelConfTypeDef adcConf; + // uint16_t adcData = 0; + + // hadc = &obj->Adc; + + // /* Enable HSI */ + // __HAL_RCC_HSI_ENABLE(); + + // /* Wait till HSI is ready */ + // while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET) + // { + // } + + // __HAL_RCC_ADC1_CLK_ENABLE( ); + + // adcConf.Channel = channel; + // adcConf.Rank = ADC_REGULAR_RANK_1; + // adcConf.SamplingTime = ADC_SAMPLETIME_192CYCLES; + + // HAL_ADC_ConfigChannel( hadc, &adcConf); + + // /* Enable ADC1 */ + // __HAL_ADC_ENABLE( hadc) ; + + // /* Start ADC1 Software Conversion */ + // HAL_ADC_Start( hadc); + + // HAL_ADC_PollForConversion( hadc, HAL_MAX_DELAY ); + + // adcData = HAL_ADC_GetValue ( hadc); + + // __HAL_ADC_DISABLE( hadc) ; + + // if( ( adcConf.Channel == ADC_CHANNEL_TEMPSENSOR ) || ( adcConf.Channel == ADC_CHANNEL_VREFINT ) ) + // { + // HAL_ADC_DeInit( hadc ); + // } + // __HAL_RCC_ADC1_CLK_DISABLE( ); + + // /* Disable HSI */ + // __HAL_RCC_HSI_DISABLE(); + + // return adcData; +} diff --git a/libraries/LoRa_Node/src/boards/arduino/adc-board.h b/libraries/LoRa_Node/src/boards/arduino/adc-board.h new file mode 100644 index 0000000..1b38e6d --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/adc-board.h @@ -0,0 +1,96 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Board ADC driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __ADC_MCU_H__ +#define __ADC_MCU_H__ + +//--------------------modified +#include "../../system/adc.h" + +/*! + * Register the old AdcMcuReadChannel function to the new function + * which makes an additional parameter available to select the read out channel + */ +// #define AdcMcuReadChannel( obj ) AdcMcuRead( obj, ADC_CHANNEL_8 ) + +/*! + * ADC resolution + */ +typedef enum +{ + ADC_12_BIT = 0, + ADC_10_BIT, + ADC_8_BIT, + ADC_6_BIT +}AdcResolution; + +/*! + * ADC conversion trigger + */ +typedef enum +{ + CONVERT_MANUAL_TRIG = 0, + CONVERT_RISING_EDGE, + CONVERT_FALLING_EDGE, + CONVERT_RISING_FALLING_EDGE +}AdcTriggerConv; + +/*! + * ADC data alignment + */ +typedef enum +{ + DATA_RIGHT_ALIGNED = 0, + DATA_LEFT_ALIGNED +}AdcDataAlignement; + + +/*! + * ADC conversion mode + */ +typedef enum +{ + SINGLE_CONVERSION = 0, + CONTIMUOUS_CONVERSION +}AdcNumConversion; + +/*! + * \brief Initializes the ADC object and MCU peripheral + * + * \param [IN] obj ADC object + * \param [IN] scl ADC input pin + */ +void AdcMcuInit( Adc_t *obj, PinNames adcInput ); + +/*! + * \brief DeInitializes the ADC object and MCU peripheral + * + * \param [IN] obj ADC object + */ +void AdcMcuDeInit( Adc_t *obj ); + +/*! + * \brief Initializes the ADC internal parameters + * + * \param [IN] obj ADC object + * \param [IN] AdcRes ADC resolution + * \param [IN] AdcNumConv ADC number of conversion + * \param [IN] AdcTrig ADC conversion trigger + * \param [IN] AdcDataAlig ADC data output alignement + */ +void AdcMcuFormat( Adc_t *obj, AdcResolution AdcRes, AdcNumConversion AdcNumConv, AdcTriggerConv AdcTrig, AdcDataAlignement AdcDataAlig ); + +uint16_t AdcMcuRead( Adc_t *obj, uint8_t channel ); + +#endif // __ADC_MCU_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/board.cpp b/libraries/LoRa_Node/src/boards/arduino/board.cpp new file mode 100644 index 0000000..6da6a25 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/board.cpp @@ -0,0 +1,144 @@ +/* + Copyright (c) 2016 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 "board.h" + +/*! + * Unique Devices IDs register set (nRF52) + */ +#define ID1 ( 0x10000060 ) +#define ID2 ( 0x10000064 ) + +/*! + * IO Extander pins objects + */ +Gpio_t Led13; + +#if defined( USE_RADIO_DEBUG ) +Gpio_t DbgPin1; +Gpio_t DbgPin2; +#endif + +/*! + * Nested interrupt counter. + * + * \remark Interrupt should only be fully disabled once the value is 0 + */ +static uint8_t IrqNestLevel = 0; + +void BoardDisableIrq( void ) +{ + __disable_irq( ); + IrqNestLevel++; +} + +void BoardEnableIrq( void ) +{ + IrqNestLevel--; + if( IrqNestLevel == 0 ) + { + __enable_irq( ); + } +} + +void BoardInitPeriph( void ) +{ + /* Init the GPIO extender pins */ + // GpioInit( &Led13, IRQ_MPL3115, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + // Switch LED 13 OFF + // GpioWrite( &Led13, 1 ); + RtcInit(); +} + +static void BoardUnusedIoInit( void ) +{ + //TODO +} + +void BoardInitMcu( void ) +{ + BoardUnusedIoInit( ); + +#if defined( USE_RADIO_DEBUG ) + GpioInit( &DbgPin1, P_A1, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + GpioInit( &DbgPin2, P_A5, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +#endif + SpiInit( &SX1276.Spi, RADIO_MOSI, RADIO_MISO, RADIO_SCLK, NC ); + SX1276IoInit( ); +} + +void BoardDeInitMcu( void ) +{ + // Gpio_t ioPin; + + // GpioInit( &ioPin, OSC_HSE_IN, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + // GpioInit( &ioPin, OSC_HSE_OUT, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + // GpioInit( &ioPin, OSC_LSE_IN, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_DOWN, 1 ); + // GpioInit( &ioPin, OSC_LSE_OUT, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_DOWN, 1 ); +} + +uint32_t BoardGetRandomSeed( void ) +{ + // return ( ( *( uint32_t* )ID1 ) ^ ( *( uint32_t* )ID2 ) ^ ( *( uint32_t* )ID3 ) ); +} + +void BoardGetUniqueId( uint8_t *id ) +{ + id[7] = ( ( *( uint32_t* )ID1 )+ ( *( uint32_t* )ID2 ) ) >> 24; + id[6] = ( ( *( uint32_t* )ID1 )+ ( *( uint32_t* )ID2 ) ) >> 16; + id[5] = ( ( *( uint32_t* )ID1 )+ ( *( uint32_t* )ID2 ) ) >> 8; + id[4] = ( ( *( uint32_t* )ID1 )+ ( *( uint32_t* )ID2 ) ); + id[3] = ( ( *( uint32_t* )ID2 ) ) >> 24; + id[2] = ( ( *( uint32_t* )ID2 ) ) >> 16; + id[1] = ( ( *( uint32_t* )ID2 ) ) >> 8; + id[0] = ( ( *( uint32_t* )ID2 ) ); +} + +uint8_t BoardGetBatteryLevel( void ) +{ + +} + +uint8_t GetBoardPowerSource( void ) +{ +//TODO +} + +#ifdef USE_FULL_ASSERT +/* + * Function Name : assert_failed + * Description : Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * Input : - file: pointer to the source file name + * - line: assert_param error line source number + * Output : None + * Return : None + */ +void assert_failed( uint8_t* file, uint32_t line ) +{ + /* User can add his own implementation to report the file name and line number, + ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ + + /* Infinite loop */ + while( 1 ) + { + } +} +#endif diff --git a/libraries/LoRa_Node/src/boards/arduino/board.h b/libraries/LoRa_Node/src/boards/arduino/board.h new file mode 100644 index 0000000..8cff838 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/board.h @@ -0,0 +1,154 @@ +/* + Copyright (c) 2016 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 +*/ + +#ifndef __BOARD_H__ +#define __BOARD_H__ + +//-----------modified +#include "Arduino.h" + + +#include "rtc-board.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "system/gpio.h" +#include "sx1276-board.h" +#include "system/spi.h" +#ifdef __cplusplus +} +#endif + +//------------------------ + +/*! + * Generic definition + */ +#ifndef SUCCESS +#define SUCCESS 1 +#endif + +#ifndef FAIL +#define FAIL 0 +#endif + +/*! + * Board IO Extender pins definitions + */ +#define LED_13 13 + + +/*! + * Board MCU pins definitions + */ +#define RADIO_RESET P_A0 + +#define RADIO_MOSI P_11 +#define RADIO_MISO P_12 +#define RADIO_SCLK P_13 +#define RADIO_NSS P_10 + +#define RADIO_DIO_0 P_2 +#define RADIO_DIO_1 P_3 +#define RADIO_DIO_2 P_4 +#define RADIO_DIO_3 P_7 +#define RADIO_DIO_4 P_8 +#define RADIO_DIO_5 P_9 + +#define RADIO_ANT_SWITCH P_A4 + +/*! + * LED GPIO pins objects + */ +extern Gpio_t Led13; + +//#define USE_RADIO_DEBUG + +#if defined( USE_RADIO_DEBUG ) +extern Gpio_t DbgPin1; +extern Gpio_t DbgPin2; +#endif + +#ifdef __cplusplus +extern "C"{ +#endif +/*! + * \brief Disable interrupts + * + * \remark IRQ nesting is managed + */ +void BoardDisableIrq( void ); + +/*! + * \brief Enable interrupts + * + * \remark IRQ nesting is managed + */ +void BoardEnableIrq( void ); + +#ifdef __cplusplus +} +#endif + +/*! + * \brief Initializes the target board peripherals. + */ +void BoardInitMcu( void ); + +/*! + * \brief Initializes the boards peripherals. + */ +void BoardInitPeriph( void ); + +/*! + * \brief De-initializes the target board peripherals to decrease power + * consumption. + */ +void BoardDeInitMcu( void ); + +/*! + * \brief Get the current battery level + * + * \retval value battery level ( 0: very low, 254: fully charged ) + */ +uint8_t BoardGetBatteryLevel( void ); + +/*! + * Returns a pseudo random seed generated using the MCU Unique ID + * + * \retval seed Generated pseudo random seed + */ +uint32_t BoardGetRandomSeed( void ); + +/*! + * \brief Gets the board 64 bits unique ID + * + * \param [IN] id Pointer to an array that will contain the Unique ID + */ +void BoardGetUniqueId( uint8_t *id ); + +/*! + * \brief Get the board power source + * + * \retval value power source ( 0: USB_POWER, 1: BATTERY_POWER ) + */ +uint8_t GetBoardPowerSource( void ); + +#endif // __BOARD_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/eeprom-board.c b/libraries/LoRa_Node/src/boards/arduino/eeprom-board.c new file mode 100644 index 0000000..afaf722 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/eeprom-board.c @@ -0,0 +1,216 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Timer objects and scheduling management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +// #include "board.h" + +// #include "i2c-board.h" +// #include "eeprom-board.h" + +// #define DEVICE_I2C_ADDRESS 0xA8 + +// static uint8_t I2cDeviceAddr = DEVICE_I2C_ADDRESS; + +// #define EE_PAGE_SIZE 64 + +// uint8_t EepromMcuWriteBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ) +// { + // uint8_t nbPage = 0; + // uint8_t nbBytes = 0; + // uint8_t nbBytesRemaining = 0; + // uint16_t lAddr = 0; + + // lAddr = addr % EE_PAGE_SIZE; + // nbBytesRemaining = EE_PAGE_SIZE - lAddr; + // nbPage = size / EE_PAGE_SIZE; + // nbBytes = size % EE_PAGE_SIZE; + + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_16 ); + // /*!< If lAddr is EE_PAGE_SIZE aligned */ + // if( lAddr == 0 ) + // { + // /*!< If size < EE_PAGE_SIZE */ + // if( nbPage == 0 ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, size ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // } + // /*!< If size > EE_PAGE_SIZE */ + // else + // { + // while( nbPage-- ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, EE_PAGE_SIZE ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // addr += EE_PAGE_SIZE; + // buffer += EE_PAGE_SIZE; + // } + + // if( nbBytes != 0 ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, nbBytes ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // } + // } + // } + // /*!< If addr is not EE_PAGE_SIZE aligned */ + // else + // { + // /*!< If size < EE_PAGE_SIZE */ + // if( nbPage== 0 ) + // { + // /*!< If the number of data to be written is more than the remaining space + // in the current page: */ + // if ( size > nbBytesRemaining ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, nbBytesRemaining ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, ( addr + nbBytesRemaining ), + // ( uint8_t* )( buffer + nbBytesRemaining ), + // ( size - nbBytesRemaining ) ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // } + // else + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, nbBytes ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // } + // } + // /*!< If size > EE_PAGE_SIZE */ + // else + // { + // size -= nbBytesRemaining; + // nbPage = size / EE_PAGE_SIZE; + // nbBytes = size % EE_PAGE_SIZE; + + // if( nbBytesRemaining != 0 ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, nbBytesRemaining ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // addr += nbBytesRemaining; + // buffer += nbBytesRemaining; + // } + + // while( nbPage-- ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, EE_PAGE_SIZE ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // addr += EE_PAGE_SIZE; + // buffer += EE_PAGE_SIZE; + // } + // if( nbBytes != 0 ) + // { + // if( I2cWriteBuffer( &I2c, I2cDeviceAddr, addr, buffer, nbBytes ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // if( I2cMcuWaitStandbyState( &I2c, I2cDeviceAddr ) == FAIL ) + // { + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return FAIL; + // } + // } + // } + // } + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + // return SUCCESS; +// } + +// uint8_t EepromMcuReadBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ) +// { + // uint8_t status = FAIL; + + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_16 ); + + // status = I2cReadBuffer( &I2c, I2cDeviceAddr, addr, buffer, size ); + + // I2cSetAddrSize( &I2c, I2C_ADDR_SIZE_8 ); + + // return status; +// } + +// void EepromMcuSetDeviceAddr( uint8_t addr ) +// { + // I2cDeviceAddr = addr; +// } + +// uint8_t EepromMcuGetDeviceAddr( void ) +// { + // return I2cDeviceAddr; +// } diff --git a/libraries/LoRa_Node/src/boards/arduino/eeprom-board.h b/libraries/LoRa_Node/src/boards/arduino/eeprom-board.h new file mode 100644 index 0000000..f99d8a6 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/eeprom-board.h @@ -0,0 +1,56 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Timer objects and scheduling management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __EEPROM_MCU_H__ +#define __EEPROM_MCU_H__ + +/*! + * Writes the given buffer to the EEPROM at the specified address. + * + * \param[IN] addr EEPROM address to write to + * \param[IN] buffer Pointer to the buffer to be written. + * \param[IN] size Size of the buffer to be written. + * \retval status [SUCCESS, FAIL] + */ +uint8_t EepromMcuWriteBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * Reads the EEPROM at the specified address to the given buffer. + * + * \param[IN] addr EEPROM address to read from + * \param[OUT] buffer Pointer to the buffer to be written with read data. + * \param[IN] size Size of the buffer to be read. + * \retval status [SUCCESS, FAIL] + */ +uint8_t EepromMcuReadBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * Sets the device address. + * + * \remark Useful for I2C external EEPROMS + * + * \param[IN] addr External EEPROM address + */ +void EepromMcuSetDeviceAddr( uint8_t addr ); + +/*! + * Gets the current device address. + * + * \remark Useful for I2C external EEPROMS + * + * \retval addr External EEPROM address + */ +uint8_t EepromMcuGetDeviceAddr( void ); + +#endif // __EEPROM_MCU_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/gpio-board.c b/libraries/LoRa_Node/src/boards/arduino/gpio-board.c new file mode 100644 index 0000000..a678393 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/gpio-board.c @@ -0,0 +1,117 @@ +/* + Copyright (c) 2016 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 "board.h" +#include "gpio-board.h" + +void GpioMcuInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value ) +{ + if( pin == NC ) + { + return; + } + obj->pin = pin; + + obj->pinIndex = obj->pin; + obj->port = 0; //Arduino Primo only + + if( mode == PIN_INPUT ) + { + if(type == PIN_PULL_UP) + pinMode(obj->pinIndex, INPUT_PULLUP); + else + pinMode(obj->pinIndex, INPUT); + } + else if(mode == PIN_OUTPUT) // mode ouptut + { + pinMode(obj->pinIndex, OUTPUT); + // Sets initial output value + digitalWrite(obj->pinIndex, value); + } +} + +void GpioMcuSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler ) +{ + uint32_t mode; + + if( irqHandler == NULL ) + { + return; + } + + if(irqMode == IRQ_RISING_EDGE) + mode = RISING; + else if(irqMode == IRQ_FALLING_EDGE) + mode = FALLING; + else + mode = CHANGE; + + attachInterrupt(obj->pinIndex, irqHandler, mode); +} + +void GpioMcuRemoveInterrupt( Gpio_t *obj ) +{ + detachInterrupt(obj->pinIndex); +} + +void GpioMcuWrite( Gpio_t *obj, uint32_t value ) +{ + if( ( obj == NULL ) || ( obj->port == NULL ) ) + { + // assert_param( FAIL ); + } + // Check if pin is not connected + if( obj->pin == NC ) + { + return; + } + + digitalWrite(obj->pinIndex, value); +} + +void GpioMcuToggle( Gpio_t *obj ) +{ + if( ( obj == NULL ) || ( obj->port == NULL ) ) + { + // assert_param( FAIL ); + } + + // Check if pin is not connected + if( obj->pin == NC ) + { + return; + } + + bool val=digitalRead(obj->pinIndex); + digitalWrite(obj->pinIndex, !val); +} + +uint32_t GpioMcuRead( Gpio_t *obj ) +{ + if( obj == NULL ) + { + // assert_param( FAIL ); + } + // Check if pin is not connected + if( obj->pin == NC ) + { + return 0; + } + + return digitalRead(obj->pinIndex); +} \ No newline at end of file diff --git a/libraries/LoRa_Node/src/boards/arduino/gpio-board.h b/libraries/LoRa_Node/src/boards/arduino/gpio-board.h new file mode 100644 index 0000000..c316579 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/gpio-board.h @@ -0,0 +1,78 @@ +/* + Copyright (c) 2016 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 +*/ + +#ifndef __GPIO_MCU_H__ +#define __GPIO_MCU_H__ + +/*! + * \brief Initializes the given GPIO object + * + * \param [IN] obj Pointer to the GPIO object to be initialized + * \param [IN] pin Pin name ( please look in pinName-board.h file ) + * \param [IN] mode Pin mode [PIN_INPUT, PIN_OUTPUT, + * PIN_ALTERNATE_FCT, PIN_ANALOGIC] + * \param [IN] config Pin config [PIN_PUSH_PULL, PIN_OPEN_DRAIN] + * \param [IN] type Pin type [PIN_NO_PULL, PIN_PULL_UP, PIN_PULL_DOWN] + * \param [IN] value Default output value at initialisation + */ +void GpioMcuInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value ); + +/*! + * \brief GPIO IRQ Initialization + * + * \param [IN] obj Pointer to the GPIO object to be initialized + * \param [IN] irqMode IRQ mode [NO_IRQ, IRQ_RISING_EDGE, + * IRQ_FALLING_EDGE, IRQ_RISING_FALLING_EDGE] + * \param [IN] irqPriority IRQ priority [IRQ_VERY_LOW_PRIORITY, IRQ_LOW_PRIORITY + * IRQ_MEDIUM_PRIORITY, IRQ_HIGH_PRIORITY + * IRQ_VERY_HIGH_PRIORITY] + * \param [IN] irqHandler Callback function pointer + */ +void GpioMcuSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler ); + +/*! + * \brief GPIO IRQ DeInitialization + * + * \param [IN] obj Pointer to the GPIO object to be Deinitialized + */ +void GpioMcuRemoveInterrupt( Gpio_t *obj ); + +/*! + * \brief Writes the given value to the GPIO output + * + * \param [IN] obj Pointer to the GPIO object + * \param [IN] value New GPIO output value + */ +void GpioMcuWrite( Gpio_t *obj, uint32_t value ); + +/*! + * \brief Toggle the value to the GPIO output + * + * \param [IN] obj Pointer to the GPIO object + */ +void GpioMcuToggle( Gpio_t *obj ); + +/*! + * \brief Reads the current GPIO input value + * + * \param [IN] obj Pointer to the GPIO object + * \retval value Current GPIO input value + */ +uint32_t GpioMcuRead( Gpio_t *obj ); + +#endif // __GPIO_MCU_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/i2c-board.cpp b/libraries/LoRa_Node/src/boards/arduino/i2c-board.cpp new file mode 100644 index 0000000..dd41879 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/i2c-board.cpp @@ -0,0 +1,152 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Bleeper board I2C driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include "board.h" +#include "i2c-board.h" +//#include "Wire.h" + +/*! + * The value of the maximal timeout for I2C waiting loops + */ +#define TIMEOUT_MAX 0x8000 + +// I2cAddrSize I2cInternalAddrSize = I2C_ADDR_SIZE_8; + +/*! + * MCU I2C peripherals enumeration + */ +typedef enum { + // I2C_1 = ( uint32_t )I2C1_BASE, + // I2C_2 = ( uint32_t )I2C2_BASE, +} I2cName; + +void I2cMcuInit( I2c_t *obj, PinNames scl, PinNames sda ) +{ + // __HAL_RCC_I2C1_CLK_DISABLE( ); + // __HAL_RCC_I2C1_CLK_ENABLE( ); + // __HAL_RCC_I2C1_FORCE_RESET( ); + // __HAL_RCC_I2C1_RELEASE_RESET( ); + + // obj->I2c.Instance = ( I2C_TypeDef * )I2C1_BASE; + + // GpioInit( &obj->Scl, scl, PIN_ALTERNATE_FCT, PIN_OPEN_DRAIN, PIN_NO_PULL, GPIO_AF4_I2C1 ); + // GpioInit( &obj->Sda, sda, PIN_ALTERNATE_FCT, PIN_OPEN_DRAIN, PIN_NO_PULL, GPIO_AF4_I2C1 ); +} + +void I2cMcuFormat( I2c_t *obj, I2cMode mode, I2cDutyCycle dutyCycle, bool I2cAckEnable, I2cAckAddrMode AckAddrMode, uint32_t I2cFrequency ) +{ + // Wire.setClock(I2cFrequency); + // Wire.begin(); + // I2C_HandleTypeDef *i2c; + + // __HAL_RCC_I2C1_CLK_ENABLE( ); + // obj->I2c.Init.ClockSpeed = I2cFrequency; + + // if( dutyCycle == I2C_DUTY_CYCLE_2 ) + // { + // obj->I2c.Init.DutyCycle = I2C_DUTYCYCLE_2; + // } + // else + // { + // obj->I2c.Init.DutyCycle = I2C_DUTYCYCLE_16_9; + // } + + // obj->I2c.Init.OwnAddress1 = 0; + // obj->I2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + // obj->I2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + // obj->I2c.Init.OwnAddress2 = 0; + // obj->I2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED; + // obj->I2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED; + + // i2c = &obj->I2c; + // HAL_I2C_Init( i2c ); +} + +void I2cMcuDeInit( I2c_t *obj ) +{ + // Wire.end(); + // I2C_HandleTypeDef *i2c; + // i2c = &obj->I2c; + + // HAL_I2C_DeInit( i2c ); + + // if( obj->I2c.Instance == ( I2C_TypeDef * ) I2C1_BASE ) + // { + // __HAL_RCC_I2C1_FORCE_RESET(); + // __HAL_RCC_I2C1_RELEASE_RESET(); + // __HAL_RCC_I2C1_CLK_DISABLE( ); + // } + // else + // { + // __HAL_RCC_I2C2_FORCE_RESET(); + // __HAL_RCC_I2C2_RELEASE_RESET(); + // __HAL_RCC_I2C2_CLK_DISABLE( ); + // } + + // GpioInit( &obj->Scl, obj->Scl.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + // GpioInit( &obj->Sda, obj->Sda.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +} + +void I2cSetAddrSize( I2c_t *obj, I2cAddrSize addrSize ) +{ + // I2cInternalAddrSize = addrSize; +} + +uint8_t I2cMcuWriteBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ) +{ + // uint8_t status = FAIL; + // uint16_t memAddSize = 0; + + // I2C_HandleTypeDef *i2c; + // i2c = &obj->I2c; + + // if( I2cInternalAddrSize == I2C_ADDR_SIZE_8 ) + // { + // memAddSize = I2C_MEMADD_SIZE_8BIT; + // } + // else + // { + // memAddSize = I2C_MEMADD_SIZE_16BIT; + // } + // status = ( HAL_I2C_Mem_Write( i2c, deviceAddr, addr, memAddSize, buffer, size, 2000 ) == HAL_OK ) ? SUCCESS : FAIL; + // return status; +} + +uint8_t I2cMcuReadBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ) +{ + // uint8_t status = FAIL; + // uint16_t memAddSize = 0; + + // I2C_HandleTypeDef *i2c; + // i2c = &obj->I2c; + // if( I2cInternalAddrSize == I2C_ADDR_SIZE_8 ) + // { + // memAddSize = I2C_MEMADD_SIZE_8BIT; + // } + // else + // { + // memAddSize = I2C_MEMADD_SIZE_16BIT; + // } + // status = ( HAL_I2C_Mem_Read( i2c, deviceAddr, addr, memAddSize, buffer, size, 2000 ) == HAL_OK ) ? SUCCESS : FAIL; + // return status; +} + +uint8_t I2cMcuWaitStandbyState( I2c_t *obj, uint8_t deviceAddr ) +{ + // uint8_t status = FAIL; + // I2C_HandleTypeDef *i2c; + // i2c = &obj->I2c; + // status = ( HAL_I2C_IsDeviceReady( i2c, deviceAddr, 300, 4096 ) == HAL_OK ) ? SUCCESS : FAIL;; + // return status; +} diff --git a/libraries/LoRa_Node/src/boards/arduino/i2c-board.h b/libraries/LoRa_Node/src/boards/arduino/i2c-board.h new file mode 100644 index 0000000..ecbd4ed --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/i2c-board.h @@ -0,0 +1,125 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Bleeper board I2C driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __I2C_MCU_H__ +#define __I2C_MCU_H__ + +//-------------------modified +#include "system/i2c.h" +//--------------------------- + +/*! + * Operation Mode for the I2C + */ +typedef enum +{ + MODE_I2C = 0, + MODE_SMBUS_DEVICE, + MODE_SMBUS_HOST +}I2cMode; + +/*! + * I2C signal duty cycle + */ +typedef enum +{ + I2C_DUTY_CYCLE_2 = 0, + I2C_DUTY_CYCLE_16_9 +}I2cDutyCycle; + +/*! + * I2C select if the acknowledge in after the 7th or 10th bit + */ +typedef enum +{ + I2C_ACK_ADD_7_BIT = 0, + I2C_ACK_ADD_10_BIT +}I2cAckAddrMode; + +/*! + * Internal device address size + */ +typedef enum +{ + I2C_ADDR_SIZE_8 = 0, + I2C_ADDR_SIZE_16, +}I2cAddrSize; + +/*! + * \brief Initializes the I2C object and MCU peripheral + * + * \param [IN] obj I2C object + * \param [IN] scl I2C Scl pin name to be used + * \param [IN] sda I2C Sda pin name to be used + */ +void I2cMcuInit( I2c_t *obj, PinNames scl, PinNames sda ); + +/*! + * \brief Initializes the I2C object and MCU peripheral + * + * \param [IN] obj I2C object + * \param [IN] mode Mode of operation for the I2C Bus + * \param [IN] dutyCycle Signal duty cycle + * \param [IN] I2cAckEnable Enable or Disable to ack + * \param [IN] AckAddrMode 7bit or 10 bit addressing + * \param [IN] I2cFrequency I2C bus clock frequency + */ +void I2cMcuFormat( I2c_t *obj, I2cMode mode, I2cDutyCycle dutyCycle, bool I2cAckEnable, I2cAckAddrMode AckAddrMode, uint32_t I2cFrequency ); + +/*! + * \brief DeInitializes the I2C object and MCU peripheral + * + * \param [IN] obj I2C object + */ +void I2cMcuDeInit( I2c_t *obj ); + +/*! + * \brief Write several data to the I2C device + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + * \param [IN] addr register address + * \param [IN] buffer data buffer to write + * \param [IN] size number of data byte to write + */ +uint8_t I2cMcuWriteBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * \brief Read several data byte from the I2C device + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + * \param [IN] addr register address + * \param [IN] buffer data buffer used to store the data read + * \param [IN] size number of data byte to read + */ +uint8_t I2cMcuReadBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * \brief Waits until the given device is in standby mode + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + */ +uint8_t I2cMcuWaitStandbyState( I2c_t *obj, uint8_t deviceAddr ); + +/*! + * \brief Sets the internal device address size + * + * \param [IN] obj I2C object + * \param [IN] addrSize Internal address size + */ +void I2cSetAddrSize( I2c_t *obj, I2cAddrSize addrSize ); + +#endif // __I2C_MCU_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/pinName-board.h b/libraries/LoRa_Node/src/boards/arduino/pinName-board.h new file mode 100644 index 0000000..442f12f --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/pinName-board.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2016 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 +*/ + +#ifndef __PIN_NAME_MCU_H__ +#define __PIN_NAME_MCU_H__ + +/*! + * STM32 Pin Names + */ +#define MCU_PINS \ + P_0 = 0, P_1, P_2, P_3, P_4, P_5, P_6, P_7, P_8, P_9, P_10, P_11, P_12, P_13, P_A0, P_A1, P_A2, \ + P_A3, P_A4, P_A5 + +#endif // __PIN_NAME_MCU_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/rtc-board.h b/libraries/LoRa_Node/src/boards/arduino/rtc-board.h new file mode 100644 index 0000000..c41730b --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/rtc-board.h @@ -0,0 +1,103 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: MCU RTC timer and low power modes management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __RTC_BOARD_H__ +#define __RTC_BOARD_H__ + +/*! + * \brief Timer time variable definition + */ +#ifndef TimerTime_t +typedef uint32_t TimerTime_t; +#endif +#ifdef __cplusplus +extern "C"{ +#endif +#include "system/timer.h" +/*! + * \brief Initializes the RTC timer + * + * \remark The timer is based on the RTC + */ +void RtcInit( void ); + +/*! + * \brief Start the RTC timer + * + * \remark The timer is based on the RTC Alarm running at 32.768KHz + * + * \param[IN] timeout Duration of the Timer + */ +void RtcSetTimeout( uint32_t timeout ); + +/*! + * \brief Adjust the value of the timeout to handle wakeup time from Alarm and GPIO irq + * + * \param[IN] timeout Duration of the Timer without compensation for wakeup time + * \retval new value for the Timeout with compensations + */ +TimerTime_t RtcGetAdjustedTimeoutValue( uint32_t timeout ); + +/*! + * \brief Get the RTC timer value + * + * \retval RTC Timer value + */ +TimerTime_t RtcGetTimerValue( void ); + +/*! + * \brief Get the RTC timer elapsed time since the last Alarm was set + * + * \retval RTC Elapsed time since the last alarm + */ +TimerTime_t RtcGetElapsedAlarmTime( void ); + +/*! + * \brief Compute the timeout time of a future event in time + * + * \param[IN] futureEventInTime Value in time + * \retval time Time between now and the futureEventInTime + */ +TimerTime_t RtcComputeFutureEventTime( TimerTime_t futureEventInTime ); + +/*! + * \brief Compute the elapsed time since a fix event in time + * + * \param[IN] eventInTime Value in time + * \retval elapsed Time since the eventInTime + */ +TimerTime_t RtcComputeElapsedTime( TimerTime_t eventInTime ); + +/*! + * \brief This function blocks the MCU from going into Low Power mode + * + * \param [IN] status [true: Enable, false: Disable + */ +void BlockLowPowerDuringTask ( bool status ); + +/*! + * \brief Sets the MCU into low power STOP mode + */ +void RtcEnterLowPowerStopMode( void ); + +/*! + * \brief Restore the MCU to its normal operation mode + */ +void RtcRecoverMcuStatus( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __RTC_BOARD_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/spi-board.cpp b/libraries/LoRa_Node/src/boards/arduino/spi-board.cpp new file mode 100644 index 0000000..6b8684d --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/spi-board.cpp @@ -0,0 +1,143 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Bleeper board SPI driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include "board.h" +#include "spi-board.h" + +#include "SPI.h" + +/*! + * \brief Find First Set + * This function identifies the least significant index or position of the + * bits set to one in the word + * + * \param [in] value Value to find least significant index + * \retval bitIndex Index of least significat bit at one + */ +__STATIC_INLINE uint8_t __ffs( uint32_t value ) +{ + return( uint32_t )( 32 - __CLZ( value & ( -value ) ) ); +} + +/*! + * MCU SPI peripherals enumeration + */ +// typedef enum +// { + // SPI_1 = ( uint32_t )SPI1_BASE, + // SPI_2 = ( uint32_t )SPI2_BASE, +// }SPIName; + +void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss ) +{ + + if( nss != NC ) + { + GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, 0x05U ); + } + else + { + // obj->Spi.Init.NSS = SPI_NSS_SOFT; + } + + SPI.setDataMode(SPI_MODE1); + SpiFrequency( obj, 10000000 ); + if( nss == NC ) + { + SPI.begin(); + } + else + { + SPI.beginSlave(); + } +} + +void SpiDeInit( Spi_t *obj ) +{ + SPI.end(); +} + +void SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave ) +{ + // obj->Spi.Init.Direction = SPI_DIRECTION_2LINES; + // if( bits == SPI_DATASIZE_8BIT ) + // { + // obj->Spi.Init.DataSize = SPI_DATASIZE_8BIT; + // } + // else + // { + // obj->Spi.Init.DataSize = SPI_DATASIZE_16BIT; + // } + // obj->Spi.Init.CLKPolarity = cpol; + // obj->Spi.Init.CLKPhase = cpha; + // obj->Spi.Init.FirstBit = SPI_FIRSTBIT_MSB; + // obj->Spi.Init.TIMode = SPI_TIMODE_DISABLE; + // obj->Spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + // obj->Spi.Init.CRCPolynomial = 7; + + // if( slave == 0 ) + // { + // obj->Spi.Init.Mode = SPI_MODE_MASTER; + // } + // else + // { + // obj->Spi.Init.Mode = SPI_MODE_SLAVE; + // } +} + +void SpiFrequency( Spi_t *obj, uint32_t hz ) +{ + uint32_t divisor; + + if(hz > 6000000) + divisor = SPI_CLOCK_DIV2; + else if(hz > 3000000) + divisor = SPI_CLOCK_DIV4; + else if(hz > 1500000) + divisor = SPI_CLOCK_DIV8; + else if(hz > 750000) + divisor = SPI_CLOCK_DIV16; + else if(hz > 375000) + divisor = SPI_CLOCK_DIV32; + else if(hz > 190000) + divisor = SPI_CLOCK_DIV64; + else + divisor = SPI_CLOCK_DIV128; + + SPI.setClockDivider(divisor); +} + +/*FlagStatus*/uint16_t SpiGetFlag( Spi_t *obj, uint16_t flag ) +{ + // FlagStatus bitstatus = RESET; + + // Check the status of the specified SPI flag + // if( ( obj->Spi.Instance->SR & flag ) != ( uint16_t )RESET ) + // { + // SPI_I2S_FLAG is set + // bitstatus = SET; + // } + // else + // { + // SPI_I2S_FLAG is reset + // bitstatus = RESET; + // } + // Return the SPI_I2S_FLAG status + // return bitstatus; +} + +uint16_t SpiInOut( Spi_t *obj, uint16_t outData ) +{ + return SPI.transfer(outData); +} diff --git a/libraries/LoRa_Node/src/boards/arduino/spi-board.h b/libraries/LoRa_Node/src/boards/arduino/spi-board.h new file mode 100644 index 0000000..ec631a3 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/spi-board.h @@ -0,0 +1,47 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Bleeper board SPI driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SPI_MCU_H__ +#define __SPI_MCU_H__ + +//-------------modified +// #include "..\..\system\spi.h" +//--------------------- +typedef struct Spi_s Spi_t; +/*! + * SPI driver structure definition + */ +struct Spi_s +{ + // SPI_HandleTypeDef Spi; + Gpio_t Mosi; + Gpio_t Miso; + Gpio_t Sclk; + Gpio_t Nss; +}; + +#ifdef __cplusplus +extern "C"{ +#endif +uint16_t SpiInOut( Spi_t *obj, uint16_t outData ); +uint16_t SpiGetFlag( Spi_t *obj, uint16_t flag ); +void SpiFrequency( Spi_t *obj, uint32_t hz ); +void SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave ); +void SpiDeInit( Spi_t *obj );void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss ); +#ifdef __cplusplus +} +#endif + + +#endif // __SPI_MCU_H__ diff --git a/libraries/LoRa_Node/src/boards/arduino/sx1276-board.c b/libraries/LoRa_Node/src/boards/arduino/sx1276-board.c new file mode 100644 index 0000000..5733194 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/sx1276-board.c @@ -0,0 +1,322 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1276 driver specific target board functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include "board.h" + +//---------------------------modified +// #include "radio.h" +// #include "sx1276/sx1276.h" + +#include "../../radio/radio.h" +#include "../../radio/sx1276/sx1276.h" +//-------------------------- + +#include "sx1276-board.h" + +/*! + * Flag used to set the RF switch control pins in low power mode when the radio is not active. + */ +static bool RadioIsActive = false; + +/*! + * Radio driver structure initialization + */ +const struct Radio_s Radio = +{ + SX1276Init, + SX1276GetStatus, + SX1276SetModem, + SX1276SetChannel, + SX1276IsChannelFree, + SX1276Random, + SX1276SetRxConfig, + SX1276SetTxConfig, + SX1276CheckRfFrequency, + SX1276GetTimeOnAir, + SX1276Send, + SX1276SetSleep, + SX1276SetStby, + SX1276SetRx, + SX1276StartCad, + SX1276SetTxContinuousWave, + SX1276ReadRssi, + SX1276Write, + SX1276Read, + SX1276WriteBuffer, + SX1276ReadBuffer, + SX1276SetMaxPayloadLength, + SX1276SetPublicNetwork +}; + +/*! + * Antenna switch GPIO pins objects + */ +Gpio_t AntSwitch; + +/*! + * Type of the supported board. [SX1276MB1MAS / SX1276MB1LAS] + */ +typedef enum BoardType +{ + BOARD_SX1276MB1MAS = 0, + BOARD_SX1276MB1LAS, + BOARD_UNKNOWN +}BoardType_t; + +static uint8_t BoardConnected = BOARD_UNKNOWN; //1 = SX1276MB1LAS; 0 = SX1276MB1MAS + +uint8_t SX1276DetectBoardType( void ) +{ + if( BoardConnected == BOARD_UNKNOWN ) + { + GpioInit( &AntSwitch, RADIO_ANT_SWITCH, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + DelayMs( 1 ); + if( GpioRead( &AntSwitch ) == 1 ) + { + BoardConnected = BOARD_SX1276MB1LAS; + } + else + { + BoardConnected = BOARD_SX1276MB1MAS; + } + GpioInit( &AntSwitch, RADIO_ANT_SWITCH, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + DelayMs( 1 ); + } + SX1276AntSwInit( ); + return BoardConnected; +} + +void SX1276IoInit( void ) +{ + SX1276DetectBoardType( ); + + GpioInit( &SX1276.Spi.Nss, RADIO_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 ); + + GpioInit( &SX1276.DIO0, RADIO_DIO_0, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + GpioInit( &SX1276.DIO1, RADIO_DIO_1, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + GpioInit( &SX1276.DIO2, RADIO_DIO_2, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + GpioInit( &SX1276.DIO3, RADIO_DIO_3, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + GpioInit( &SX1276.DIO4, RADIO_DIO_4, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + GpioInit( &SX1276.DIO5, RADIO_DIO_5, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); +} + +void SX1276IoIrqInit( DioIrqHandler **irqHandlers ) +{ + GpioSetInterrupt( &SX1276.DIO0, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[0] ); + GpioSetInterrupt( &SX1276.DIO1, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[1] ); + GpioSetInterrupt( &SX1276.DIO2, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[2] ); + GpioSetInterrupt( &SX1276.DIO3, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[3] ); + GpioSetInterrupt( &SX1276.DIO4, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[4] ); + GpioSetInterrupt( &SX1276.DIO5, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[5] ); +} + +void SX1276IoDeInit( void ) +{ + GpioInit( &SX1276.Spi.Nss, RADIO_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + GpioInit( &SX1276.DIO0, RADIO_DIO_0, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + GpioInit( &SX1276.DIO1, RADIO_DIO_1, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + GpioInit( &SX1276.DIO2, RADIO_DIO_2, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + GpioInit( &SX1276.DIO3, RADIO_DIO_3, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + GpioInit( &SX1276.DIO4, RADIO_DIO_4, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + GpioInit( &SX1276.DIO5, RADIO_DIO_5, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +} + +void SX1276SetRfTxPower( int8_t power ) +{ + uint8_t paConfig = 0; + uint8_t paDac = 0; + + paConfig = SX1276Read( REG_PACONFIG ); + paDac = SX1276Read( REG_PADAC ); + + paConfig = ( paConfig & RF_PACONFIG_PASELECT_MASK ) | SX1276GetPaSelect( SX1276.Settings.Channel ); + paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK ) | 0x70; + + if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST ) + { + if( power > 17 ) + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_ON; + } + else + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_OFF; + } + if( ( paDac & RF_PADAC_20DBM_ON ) == RF_PADAC_20DBM_ON ) + { + if( power < 5 ) + { + power = 5; + } + if( power > 20 ) + { + power = 20; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 5 ) & 0x0F ); + } + else + { + if( power < 2 ) + { + power = 2; + } + if( power > 17 ) + { + power = 17; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 2 ) & 0x0F ); + } + } + else + { + if( power < -1 ) + { + power = -1; + } + if( power > 14 ) + { + power = 14; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power + 1 ) & 0x0F ); + } + SX1276Write( REG_PACONFIG, paConfig ); + SX1276Write( REG_PADAC, paDac ); +} + +uint8_t SX1276GetPaSelect( uint32_t channel ) +{ + return RF_PACONFIG_PASELECT_PABOOST; +} + +// void SX1276SetAntSwLowPower( bool status ) +// { + // if( RadioIsActive != status ) + // { + // RadioIsActive = status; + + // if( status == false ) + // { + // SX1276AntSwInit( ); + // } + // else + // { + // SX1276AntSwDeInit( ); + // } + // } +// } + +// void SX1276AntSwInit( void ) +// { + // GpioWrite( &AntSwitch, 0 ); +// } + +// void SX1276AntSwDeInit( void ) +// { + // GpioWrite( &AntSwitch, 0 ); +// } + +// void SX1276SetAntSw( uint8_t opMode ) +// { + // switch( opMode ) + // { + // case RFLR_OPMODE_TRANSMITTER: + // GpioWrite( &AntSwitch, 1 ); + // break; + // case RFLR_OPMODE_RECEIVER: + // case RFLR_OPMODE_RECEIVER_SINGLE: + // case RFLR_OPMODE_CAD: + // default: + // GpioWrite( &AntSwitch, 0 ); + // break; + // } +// } + +void SX1276SetAntSwLowPower( bool status ) + +{ + + if( RadioIsActive != status ) + + { + + RadioIsActive = status; + + + + if( status == false ) + + { + + } + + else + + { + + } + + } + +} + + + +void SX1276AntSwInit( void ) + +{ + +} + + + +void SX1276AntSwDeInit( void ) + +{ + +} + + + +void SX1276SetAntSw( uint8_t opMode ) + +{ + + switch( opMode ) + + { + + case RFLR_OPMODE_TRANSMITTER: + + break; + + case RFLR_OPMODE_RECEIVER: + + case RFLR_OPMODE_RECEIVER_SINGLE: + + case RFLR_OPMODE_CAD: + + default: + + break; + + } + +} + +bool SX1276CheckRfFrequency( uint32_t frequency ) +{ + // Implement check. Currently all frequencies are supported + return true; +} diff --git a/libraries/LoRa_Node/src/boards/arduino/sx1276-board.h b/libraries/LoRa_Node/src/boards/arduino/sx1276-board.h new file mode 100644 index 0000000..a273a46 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/arduino/sx1276-board.h @@ -0,0 +1,123 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1276 driver specific target board functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SX1276_ARCH_H__ +#define __SX1276_ARCH_H__ + +//-------------modified +#include "../../radio/sx1276/sx1276.h" +//------------------------ +/*! + * \brief Radio hardware registers initialization definition + * + * \remark Can be automatically generated by the SX1276 GUI (not yet implemented) + */ +#define RADIO_INIT_REGISTERS_VALUE \ +{ \ + { MODEM_FSK , REG_LNA , 0x23 },\ + { MODEM_FSK , REG_RXCONFIG , 0x1E },\ + { MODEM_FSK , REG_RSSICONFIG , 0xD2 },\ + { MODEM_FSK , REG_AFCFEI , 0x01 },\ + { MODEM_FSK , REG_PREAMBLEDETECT , 0xAA },\ + { MODEM_FSK , REG_OSC , 0x07 },\ + { MODEM_FSK , REG_SYNCCONFIG , 0x12 },\ + { MODEM_FSK , REG_SYNCVALUE1 , 0xC1 },\ + { MODEM_FSK , REG_SYNCVALUE2 , 0x94 },\ + { MODEM_FSK , REG_SYNCVALUE3 , 0xC1 },\ + { MODEM_FSK , REG_PACKETCONFIG1 , 0xD8 },\ + { MODEM_FSK , REG_FIFOTHRESH , 0x8F },\ + { MODEM_FSK , REG_IMAGECAL , 0x02 },\ + { MODEM_FSK , REG_DIOMAPPING1 , 0x00 },\ + { MODEM_FSK , REG_DIOMAPPING2 , 0x30 },\ + { MODEM_LORA, REG_LR_PAYLOADMAXLENGTH, 0x40 },\ +} \ + +#define RF_MID_BAND_THRESH 525000000 + +/*! + * \brief Initializes the radio I/Os pins interface + */ +void SX1276IoInit( void ); + +/*! + * \brief Initializes DIO IRQ handlers + * + * \param [IN] irqHandlers Array containing the IRQ callback functions + */ +void SX1276IoIrqInit( DioIrqHandler **irqHandlers ); + +/*! + * \brief De-initializes the radio I/Os pins interface. + * + * \remark Useful when going in MCU low power modes + */ +void SX1276IoDeInit( void ); + +/*! + * \brief Sets the radio output power. + * + * \param [IN] power Sets the RF output power + */ +void SX1276SetRfTxPower( int8_t power ); + +/*! + * \brief Gets the board PA selection configuration + * + * \param [IN] channel Channel frequency in Hz + * \retval PaSelect RegPaConfig PaSelect value + */ +uint8_t SX1276GetPaSelect( uint32_t channel ); + +/*! + * \brief Set the RF Switch I/Os pins in Low Power mode + * + * \param [IN] status enable or disable + */ +void SX1276SetAntSwLowPower( bool status ); + +/*! + * \brief Initializes the RF Switch I/Os pins interface + */ +void SX1276AntSwInit( void ); + +/*! + * \brief De-initializes the RF Switch I/Os pins interface + * + * \remark Needed to decrease the power consumption in MCU low power modes + */ +void SX1276AntSwDeInit( void ); + +/*! + * \brief Controls the antenna switch if necessary. + * + * \remark see errata note + * + * \param [IN] opMode Current radio operating mode + */ +void SX1276SetAntSw( uint8_t opMode ); + +/*! + * \brief Checks if the given RF frequency is supported by the hardware + * + * \param [IN] frequency RF frequency to be checked + * \retval isSupported [true: supported, false: unsupported] + */ +bool SX1276CheckRfFrequency( uint32_t frequency ); + +/*! + * Radio hardware and global parameters + */ +extern SX1276_t SX1276; + +#endif // __SX1276_ARCH_H__ diff --git a/libraries/LoRa_Node/src/boards/mcu/arduino/arduino_hal.c b/libraries/LoRa_Node/src/boards/mcu/arduino/arduino_hal.c new file mode 100644 index 0000000..6edd66f --- /dev/null +++ b/libraries/LoRa_Node/src/boards/mcu/arduino/arduino_hal.c @@ -0,0 +1,27 @@ +/* + Copyright (c) 2016 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 "Arduino.h" + +void HAL_Delay(__IO uint32_t Delay) +{ +#if defined(ARDUINO_ARCH_NRF52) + nrf_delay_ms(Delay); +#endif + // delay(Delay); //delay function cannot be used inside interrupt routine +} \ No newline at end of file diff --git a/libraries/LoRa_Node/src/boards/mcu/arduino/nrf52/rtc-board.c b/libraries/LoRa_Node/src/boards/mcu/arduino/nrf52/rtc-board.c new file mode 100644 index 0000000..97ef823 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/mcu/arduino/nrf52/rtc-board.c @@ -0,0 +1,241 @@ +/* + Copyright (c) 2016 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 +*/ + +#if defined(ARDUINO_ARCH_NRF52) + +#include "boards/arduino/board.h" +#include "boards/arduino/rtc-board.h" +#include "nrf_timer.h" + +/*! + * RTC Time base in ms + */ +#define RTC_ALARM_TICK_DURATION 1 // 1 tick every 32us +#define RTC_ALARM_TICK_PER_MS 1 // 1/31.25 = tick duration in ms + +typedef uint32_t* RtcCalendar_t; +TimerTime_t RtcCalendarContext; +volatile TimerTime_t now; +volatile uint32_t McuWakeUpTime = 0; +volatile bool NonScheduledWakeUp = false; +static bool RtcTimerEventAllowsLowPower = false; +volatile uint32_t timeout; + + + +void RtcInit( void ) +{ + uint32_t err_code; + now=0; + + sd_clock_hfclk_request(); + uint32_t runn = 0; + do{sd_clock_hfclk_is_running(&runn);} + while(!runn); + + nrf_timer_mode_set(NRF_TIMER3, NRF_TIMER_MODE_TIMER); + nrf_timer_bit_width_set(NRF_TIMER3, NRF_TIMER_BIT_WIDTH_32); + nrf_timer_frequency_set(NRF_TIMER3, NRF_TIMER_FREQ_16MHz); + uint32_t ticks=nrf_timer_ms_to_ticks(1, NRF_TIMER_FREQ_16MHz); + nrf_timer_cc_write(NRF_TIMER3, NRF_TIMER_CC_CHANNEL0, ticks); + nrf_timer_shorts_enable(NRF_TIMER3, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK); + + // enable interrupt + nrf_timer_int_enable(NRF_TIMER3, NRF_TIMER_INT_COMPARE0_MASK); + NVIC_SetPriority(TIMER3_IRQn, 2); //high priority + NVIC_ClearPendingIRQ(TIMER3_IRQn); + NVIC_EnableIRQ(TIMER3_IRQn); + + nrf_timer_task_trigger(NRF_TIMER3, NRF_TIMER_TASK_START); +} + + +static TimerTime_t RtcConvertCalendarTickToTimerTime( RtcCalendar_t *calendar ) +{ + + // TimerTime_t timeCounter = 0; + // RtcCalendar_t now; + // double timeCounterTemp = 0.0; + + // Passing a NULL pointer will compute from "now" else, + // compute from the given calendar value + if( calendar == NULL ) + { + return now; + // rtc.getTime(); + // rtc.getDate(); + // now.CalendarDate = rtc.date; + // now.CalendarTime = rtc.time; + } + else + { + return *calendar; + // now = *calendar; + } + + //compute the elapsed seconds starting by 01/01/01 00:00:00 + // uint32_t days = rtc.rdn(now.CalendarDate.year, now.CalendarDate.month, now.CalendarDate.day) - rtc.rdn(1,1,1); + // timeCounter = (now.CalendarTime.hour * SecondsInHour) + (now.CalendarTime.minute * SecondsInMinute) + now.CalendarTime.second; + // timeCounter += SecondsInDay; + // Years (calculation valid up to year 2099) + // for( int16_t i = 0; i < ( now.CalendarDate.Year + now.CalendarCentury ); i++ ) + // { + // if( ( i == 0 ) || ( i % 4 ) == 0 ) + // { + // timeCounterTemp += ( double )SecondsInLeapYear; + // } + // else + // { + // timeCounterTemp += ( double )SecondsInYear; + // } + // } + + // Months (calculation valid up to year 2099)*/ + // if( ( now.CalendarDate.Year == 0 ) || ( ( now.CalendarDate.Year + now.CalendarCentury ) % 4 ) == 0 ) + // { + // for( uint8_t i = 0; i < ( now.CalendarDate.Month - 1 ); i++ ) + // { + // timeCounterTemp += ( double )( DaysInMonthLeapYear[i] * SecondsInDay ); + // } + // } + // else + // { + // for( uint8_t i = 0; i < ( now.CalendarDate.Month - 1 ); i++ ) + // { + // timeCounterTemp += ( double )( DaysInMonth[i] * SecondsInDay ); + // } + // } + + // timeCounterTemp += ( double )( ( uint32_t )now.CalendarTime.Seconds + + // ( ( uint32_t )now.CalendarTime.Minutes * SecondsInMinute ) + + // ( ( uint32_t )now.CalendarTime.Hours * SecondsInHour ) + + // ( ( uint32_t )( now.CalendarDate.Date * SecondsInDay ) ) ); + + // timeCounterTemp = ( double )timeCounterTemp * RTC_ALARM_TICK_DURATION; + + // timeCounter = round( timeCounterTemp ); + // return ( timeCounter ); +} + +TimerTime_t RtcGetElapsedAlarmTime( void ) +{ + TimerTime_t currentTime = 0; + TimerTime_t contextTime = 0; + + currentTime = RtcConvertCalendarTickToTimerTime( NULL ); + contextTime = RtcConvertCalendarTickToTimerTime( &RtcCalendarContext ); + + if( currentTime < contextTime ) + { + return( currentTime + ( 0xFFFFFFFF - contextTime ) ); + } + else + { + return( currentTime - contextTime ); + } +} + +TimerTime_t RtcGetTimerValue( void ) +{ + return( RtcConvertCalendarTickToTimerTime( NULL ) ); +} + + +TimerTime_t RtcComputeElapsedTime( TimerTime_t eventInTime ) +{ + TimerTime_t elapsedTime = 0; + + // Needed at boot, cannot compute with 0 or elapsed time will be equal to current time + if( eventInTime == 0 ) + { + return 0; + } + + elapsedTime = RtcConvertCalendarTickToTimerTime( NULL ); + + if( elapsedTime < eventInTime ) + { // roll over of the counter + return( elapsedTime + ( 0xFFFFFFFF - eventInTime ) ); + } + else + { + return( elapsedTime - eventInTime ); + } +} + +TimerTime_t RtcComputeFutureEventTime( TimerTime_t futureEventInTime ) +{ + return( RtcGetTimerValue( ) + futureEventInTime ); +} + +TimerTime_t RtcGetAdjustedTimeoutValue( uint32_t timeout ) +{ + // if( timeout > McuWakeUpTime ) + // { // we have waken up from a GPIO and we have lost "McuWakeUpTime" that we need to compensate on next event + // if( NonScheduledWakeUp == true ) + // { + // NonScheduledWakeUp = false; + // timeout -= McuWakeUpTime; + // } + // } + + // if( timeout > McuWakeUpTime ) + // { // we don't go in Low Power mode for delay below 50ms (needed for LEDs) + // if( timeout < 50 ) // 50 ms + // { + // RtcTimerEventAllowsLowPower = false; + // } + // else + // { + // RtcTimerEventAllowsLowPower = true; + // timeout -= McuWakeUpTime; + // } + // } + return timeout; +} + + +static void RtcStartWakeUpAlarm( uint32_t timeoutValue ) +{ + // every 164 ms we loose 1ms + uint32_t adjustment = timeoutValue / 100; + uint32_t partialAdjustment = adjustment; + while(partialAdjustment > 100){ + //if the adjustment is greater than 174 ms we need to calculate the adjustment over the adjustment + adjustment += partialAdjustment / 100; + partialAdjustment = partialAdjustment / 100; + } + + timeout = now + timeoutValue/* + adjustment*/; + RtcCalendarContext = now; +} + +void RtcSetTimeout( uint32_t timeout ) +{ + RtcStartWakeUpAlarm( timeout ); +} + + +void TIMER3_IRQHandler(void){ + nrf_timer_event_clear(NRF_TIMER3, NRF_TIMER_EVENT_COMPARE0); + now++; + if(timeout == now) + TimerIrqHandler( ); +} + +#endif \ No newline at end of file diff --git a/libraries/LoRa_Node/src/boards/mcu/arduino/utilities.c b/libraries/LoRa_Node/src/boards/mcu/arduino/utilities.c new file mode 100644 index 0000000..61326ec --- /dev/null +++ b/libraries/LoRa_Node/src/boards/mcu/arduino/utilities.c @@ -0,0 +1,85 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Helper functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include +#include +#include "boards/arduino/board.h" +#include "utilities.h" + +/*! + * Redefinition of rand() and srand() standard C functions. + * These functions are redefined in order to get the same behavior across + * different compiler toolchains implementations. + */ +// Standard random functions redefinition start +#define RAND_LOCAL_MAX 2147483647L + +static uint32_t next = 1; + +int32_t rand1( void ) +{ + return ( ( next = next * 1103515245L + 12345L ) % RAND_LOCAL_MAX ); +} + +void srand1( uint32_t seed ) +{ + next = seed; +} +// Standard random functions redefinition end + +int32_t randr( int32_t min, int32_t max ) +{ + return ( int32_t )rand1( ) % ( max - min + 1 ) + min; +} + +void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size ) +{ + while( size-- ) + { + *dst++ = *src++; + } +} + +void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size ) +{ + dst = dst + ( size - 1 ); + while( size-- ) + { + *dst-- = *src++; + } +} + +void memset1( uint8_t *dst, uint8_t value, uint16_t size ) +{ + while( size-- ) + { + *dst++ = value; + } +} + +int8_t Nibble2HexChar( uint8_t a ) +{ + if( a < 10 ) + { + return '0' + a; + } + else if( a < 16 ) + { + return 'A' + ( a - 10 ); + } + else + { + return '?'; + } +} diff --git a/libraries/LoRa_Node/src/boards/mcu/arduino/utilities.h b/libraries/LoRa_Node/src/boards/mcu/arduino/utilities.h new file mode 100644 index 0000000..39ae174 --- /dev/null +++ b/libraries/LoRa_Node/src/boards/mcu/arduino/utilities.h @@ -0,0 +1,99 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Helper functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __UTILITIES_H__ +#define __UTILITIES_H__ + +/*! + * \brief Returns the minimum value betwen a and b + * + * \param [IN] a 1st value + * \param [IN] b 2nd value + * \retval minValue Minimum value + */ +#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) + +/*! + * \brief Returns the maximum value betwen a and b + * + * \param [IN] a 1st value + * \param [IN] b 2nd value + * \retval maxValue Maximum value + */ +#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) + +/*! + * \brief Returns 2 raised to the power of n + * + * \param [IN] n power value + * \retval result of raising 2 to the power n + */ +#define POW2( n ) ( 1 << n ) + +/*! + * \brief Initializes the pseudo ramdom generator initial value + * + * \param [IN] seed Pseudo ramdom generator initial value + */ +void srand1( uint32_t seed ); + +/*! + * \brief Computes a random number between min and max + * + * \param [IN] min range minimum value + * \param [IN] max range maximum value + * \retval random random value in range min..max + */ +int32_t randr( int32_t min, int32_t max ); + +/*! + * \brief Copies size elements of src array to dst array + * + * \remark STM32 Standard memcpy function only works on pointers that are aligned + * + * \param [OUT] dst Destination array + * \param [IN] src Source array + * \param [IN] size Number of bytes to be copied + */ +void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size ); + +/*! + * \brief Copies size elements of src array to dst array reversing the byte order + * + * \param [OUT] dst Destination array + * \param [IN] src Source array + * \param [IN] size Number of bytes to be copied + */ +void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size ); + +/*! + * \brief Set size elements of dst array with value + * + * \remark STM32 Standard memset function only works on pointers that are aligned + * + * \param [OUT] dst Destination array + * \param [IN] value Default value + * \param [IN] size Number of bytes to be copied + */ +void memset1( uint8_t *dst, uint8_t value, uint16_t size ); + +/*! + * \brief Converts a nibble to an hexadecimal character + * + * \param [IN] a Nibble to be converted + * \retval hexChar Converted hexadecimal character + */ +int8_t Nibble2HexChar( uint8_t a ); + +#endif // __UTILITIES_H__ diff --git a/libraries/LoRa_Node/src/config.h b/libraries/LoRa_Node/src/config.h new file mode 100644 index 0000000..8106efe --- /dev/null +++ b/libraries/LoRa_Node/src/config.h @@ -0,0 +1,23 @@ +/* + Copyright (c) 2016 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 +*/ + +// use this define if you are in Europe region +#define REGION_EU868 + +// use this define if you are in US region +// #define REGION_US915_HYBRID diff --git a/libraries/LoRa_Node/src/mac/LoRaMac.c b/libraries/LoRa_Node/src/mac/LoRaMac.c new file mode 100644 index 0000000..920c89c --- /dev/null +++ b/libraries/LoRa_Node/src/mac/LoRaMac.c @@ -0,0 +1,3260 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE ) +*/ +//modified---------------------- +//#include "board.h" +#include "boards/arduino/board.h" +#include "system/timer.h" +//------------------------------ + +#include "LoRaMacCrypto.h" +#include "LoRaMac.h" +#include "region/Region.h" +#include "LoRaMacTest.h" + +/*! + * Maximum PHY layer payload size + */ +#define LORAMAC_PHY_MAXPAYLOAD 255 + +/*! + * Maximum MAC commands buffer size + */ +#define LORA_MAC_COMMAND_MAX_LENGTH 15 + +/*! + * LoRaMac region. + */ +static LoRaMacRegion_t LoRaMacRegion; + +/*! + * LoRaMac duty cycle for the back-off procedure during the first hour. + */ +#define BACKOFF_DC_1_HOUR 100 + +/*! + * LoRaMac duty cycle for the back-off procedure during the next 10 hours. + */ +#define BACKOFF_DC_10_HOURS 1000 + +/*! + * LoRaMac duty cycle for the back-off procedure during the next 24 hours. + */ +#define BACKOFF_DC_24_HOURS 10000 + +/*! + * Device IEEE EUI + */ +static uint8_t *LoRaMacDevEui; + +/*! + * Application IEEE EUI + */ +static uint8_t *LoRaMacAppEui; + +/*! + * AES encryption/decryption cipher application key + */ +static uint8_t *LoRaMacAppKey; + +/*! + * AES encryption/decryption cipher network session key + */ +static uint8_t LoRaMacNwkSKey[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/*! + * AES encryption/decryption cipher application session key + */ +static uint8_t LoRaMacAppSKey[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/*! + * Device nonce is a random value extracted by issuing a sequence of RSSI + * measurements + */ +static uint16_t LoRaMacDevNonce; + +/*! + * Network ID ( 3 bytes ) + */ +static uint32_t LoRaMacNetID; + +/*! + * Mote Address + */ +static uint32_t LoRaMacDevAddr; + +/*! + * Multicast channels linked list + */ +static MulticastParams_t *MulticastChannels = NULL; + +/*! + * Actual device class + */ +static DeviceClass_t LoRaMacDeviceClass; + +/*! + * Indicates if the node is connected to a private or public network + */ +static bool PublicNetwork; + +/*! + * Indicates if the node supports repeaters + */ +static bool RepeaterSupport; + +/*! + * Buffer containing the data to be sent or received. + */ +static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD]; + +/*! + * Length of packet in LoRaMacBuffer + */ +static uint16_t LoRaMacBufferPktLen = 0; + +/*! + * Length of the payload in LoRaMacBuffer + */ +static uint8_t LoRaMacTxPayloadLen = 0; + +/*! + * Buffer containing the upper layer data. + */ +static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD]; + +/*! + * LoRaMAC frame counter. Each time a packet is sent the counter is incremented. + * Only the 16 LSB bits are sent + */ +static uint32_t UpLinkCounter = 0; + +/*! + * LoRaMAC frame counter. Each time a packet is received the counter is incremented. + * Only the 16 LSB bits are received + */ +static uint32_t DownLinkCounter = 0; + +/*! + * IsPacketCounterFixed enables the MIC field tests by fixing the + * UpLinkCounter value + */ +static bool IsUpLinkCounterFixed = false; + +/*! + * Used for test purposes. Disables the opening of the reception windows. + */ +static bool IsRxWindowsEnabled = true; + +/*! + * Indicates if the MAC layer has already joined a network. + */ +static bool IsLoRaMacNetworkJoined = false; + +/*! + * LoRaMac ADR control status + */ +static bool AdrCtrlOn = false; + +/*! + * Counts the number of missed ADR acknowledgements + */ +static uint32_t AdrAckCounter = 0; + +/*! + * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates + * if the nodes needs to manage the server acknowledgement. + */ +static bool NodeAckRequested = false; + +/*! + * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates + * if the ACK bit must be set for the next transmission + */ +static bool SrvAckRequested = false; + +/*! + * Indicates if the MAC layer wants to send MAC commands + */ +static bool MacCommandsInNextTx = false; + +/*! + * Contains the current MacCommandsBuffer index + */ +static uint8_t MacCommandsBufferIndex = 0; + +/*! + * Contains the current MacCommandsBuffer index for MAC commands to repeat + */ +static uint8_t MacCommandsBufferToRepeatIndex = 0; + +/*! + * Buffer containing the MAC layer commands + */ +static uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH]; + +/*! + * Buffer containing the MAC layer commands which must be repeated + */ +static uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH]; + +/*! + * LoRaMac parameters + */ +LoRaMacParams_t LoRaMacParams; + +/*! + * LoRaMac default parameters + */ +LoRaMacParams_t LoRaMacParamsDefaults; + +/*! + * Uplink messages repetitions counter + */ +static uint8_t ChannelsNbRepCounter = 0; + +/*! + * Maximum duty cycle + * \remark Possibility to shutdown the device. + */ +static uint8_t MaxDCycle = 0; + +/*! + * Aggregated duty cycle management + */ +static uint16_t AggregatedDCycle; +static TimerTime_t AggregatedLastTxDoneTime; +static TimerTime_t AggregatedTimeOff; + +/*! + * Enables/Disables duty cycle management (Test only) + */ +static bool DutyCycleOn; + +/*! + * Current channel index + */ +static uint8_t Channel; + +/*! + * Current channel index + */ +static uint8_t LastTxChannel; + +/*! + * Stores the time at LoRaMac initialization. + * + * \remark Used for the BACKOFF_DC computation. + */ +static TimerTime_t LoRaMacInitializationTime = 0; + +/*! + * LoRaMac internal states + */ +enum eLoRaMacState +{ + LORAMAC_IDLE = 0x00000000, + LORAMAC_TX_RUNNING = 0x00000001, + LORAMAC_RX = 0x00000002, + LORAMAC_ACK_REQ = 0x00000004, + LORAMAC_ACK_RETRY = 0x00000008, + LORAMAC_TX_DELAYED = 0x00000010, + LORAMAC_TX_CONFIG = 0x00000020, + LORAMAC_RX_ABORT = 0x00000040, +}; + +/*! + * LoRaMac internal state + */ +uint32_t LoRaMacState = LORAMAC_IDLE; + +/*! + * LoRaMac timer used to check the LoRaMacState (runs every second) + */ +static TimerEvent_t MacStateCheckTimer; + +/*! + * LoRaMac upper layer event functions + */ +static LoRaMacPrimitives_t *LoRaMacPrimitives; + +/*! + * LoRaMac upper layer callback functions + */ +static LoRaMacCallback_t *LoRaMacCallbacks; + +/*! + * Radio events function pointer + */ +static RadioEvents_t RadioEvents; + +/*! + * LoRaMac duty cycle delayed Tx timer + */ +static TimerEvent_t TxDelayedTimer; + +/*! + * LoRaMac reception windows timers + */ +static TimerEvent_t RxWindowTimer1; +static TimerEvent_t RxWindowTimer2; + +/*! + * LoRaMac reception windows delay + * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME + * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME + */ +static uint32_t RxWindow1Delay; +static uint32_t RxWindow2Delay; + +/*! + * LoRaMac Rx windows configuration + */ +static RxConfigParams_t RxWindow1Config; +static RxConfigParams_t RxWindow2Config; + +/*! + * Acknowledge timeout timer. Used for packet retransmissions. + */ +static TimerEvent_t AckTimeoutTimer; + +/*! + * Number of trials to get a frame acknowledged + */ +static uint8_t AckTimeoutRetries = 1; + +/*! + * Number of trials to get a frame acknowledged + */ +static uint8_t AckTimeoutRetriesCounter = 1; + +/*! + * Indicates if the AckTimeout timer has expired or not + */ +static bool AckTimeoutRetry = false; + +/*! + * Last transmission time on air + */ +TimerTime_t TxTimeOnAir = 0; + +/*! + * Number of trials for the Join Request + */ +static uint8_t JoinRequestTrials; + +/*! + * Maximum number of trials for the Join Request + */ +static uint8_t MaxJoinRequestTrials; + +/*! + * Structure to hold an MCPS indication data. + */ +static McpsIndication_t McpsIndication; + +/*! + * Structure to hold MCPS confirm data. + */ +static McpsConfirm_t McpsConfirm; + +/*! + * Structure to hold MLME confirm data. + */ +static MlmeConfirm_t MlmeConfirm; + +/*! + * Holds the current rx window slot + */ +static uint8_t RxSlot = 0; + +/*! + * LoRaMac tx/rx operation state + */ +LoRaMacFlags_t LoRaMacFlags; + +/*! + * \brief Function to be executed on Radio Tx Done event + */ +static void OnRadioTxDone( void ); + +/*! + * \brief This function prepares the MAC to abort the execution of function + * OnRadioRxDone in case of a reception error. + */ +static void PrepareRxDoneAbort( void ); + +/*! + * \brief Function to be executed on Radio Rx Done event + */ +static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); + +/*! + * \brief Function executed on Radio Tx Timeout event + */ +static void OnRadioTxTimeout( void ); + +/*! + * \brief Function executed on Radio Rx error event + */ +static void OnRadioRxError( void ); + +/*! + * \brief Function executed on Radio Rx Timeout event + */ +static void OnRadioRxTimeout( void ); + +/*! + * \brief Function executed on Resend Frame timer event. + */ +static void OnMacStateCheckTimerEvent( void ); + +/*! + * \brief Function executed on duty cycle delayed Tx timer event + */ +static void OnTxDelayedTimerEvent( void ); + +/*! + * \brief Function executed on first Rx window timer event + */ +static void OnRxWindow1TimerEvent( void ); + +/*! + * \brief Function executed on second Rx window timer event + */ +static void OnRxWindow2TimerEvent( void ); + +/*! + * \brief Function executed on AckTimeout timer event + */ +static void OnAckTimeoutTimerEvent( void ); + +/*! + * \brief Initializes and opens the reception window + * + * \param [IN] rxContinuous Set to true, if the RX is in continuous mode + * \param [IN] maxRxWindow Maximum RX window timeout + */ +static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow ); + +/*! + * \brief Adds a new MAC command to be sent. + * + * \Remark MAC layer internal function + * + * \param [in] cmd MAC command to be added + * [MOTE_MAC_LINK_CHECK_REQ, + * MOTE_MAC_LINK_ADR_ANS, + * MOTE_MAC_DUTY_CYCLE_ANS, + * MOTE_MAC_RX2_PARAM_SET_ANS, + * MOTE_MAC_DEV_STATUS_ANS + * MOTE_MAC_NEW_CHANNEL_ANS] + * \param [in] p1 1st parameter ( optional depends on the command ) + * \param [in] p2 2nd parameter ( optional depends on the command ) + * + * \retval status Function status [0: OK, 1: Unknown command, 2: Buffer full] + */ +static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ); + +/*! + * \brief Parses the MAC commands which must be repeated. + * + * \Remark MAC layer internal function + * + * \param [IN] cmdBufIn Buffer which stores the MAC commands to send + * \param [IN] length Length of the input buffer to parse + * \param [OUT] cmdBufOut Buffer which stores the MAC commands which must be + * repeated. + * + * \retval Size of the MAC commands to repeat. + */ +static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ); + +/*! + * \brief Validates if the payload fits into the frame, taking the datarate + * into account. + * + * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0 + * + * \param lenN Length of the application payload. The length depends on the + * datarate and is region specific + * + * \param datarate Current datarate + * + * \param fOptsLen Length of the fOpts field + * + * \retval [false: payload does not fit into the frame, true: payload fits into + * the frame] + */ +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); + +/*! + * \brief Decodes MAC commands in the fOpts field and in the payload + */ +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ); + +/*! + * \brief LoRaMAC layer generic send frame + * + * \param [IN] macHdr MAC header field + * \param [IN] fPort MAC payload port + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \retval status Status of the operation. + */ +LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/*! + * \brief LoRaMAC layer frame buffer initialization + * + * \param [IN] macHdr MAC header field + * \param [IN] fCtrl MAC frame control field + * \param [IN] fOpts MAC commands buffer + * \param [IN] fPort MAC payload port + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \retval status Status of the operation. + */ +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/* + * \brief Schedules the frame according to the duty cycle + * + * \retval Status of the operation + */ +static LoRaMacStatus_t ScheduleTx( void ); + +/* + * \brief Calculates the back-off time for the band of a channel. + * + * \param [IN] channel The last Tx channel index + */ +static void CalculateBackOff( uint8_t channel ); + +/*! + * \brief LoRaMAC layer prepared frame buffer transmission with channel specification + * + * \remark PrepareFrame must be called at least once before calling this + * function. + * + * \param [IN] channel Channel to transmit on + * \retval status Status of the operation. + */ +LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ); + +/*! + * \brief Sets the radio in continuous transmission mode + * + * \remark Uses the radio parameters set on the previous transmission. + * + * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode + * \retval status Status of the operation. + */ +LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ); + +/*! + * \brief Sets the radio in continuous transmission mode + * + * \remark Uses the radio parameters set on the previous transmission. + * + * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode + * \param [IN] frequency RF frequency to be set. + * \param [IN] power RF ouptput power to be set. + * \retval status Status of the operation. + */ +LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ); + +/*! + * \brief Resets MAC specific parameters to default + */ +static void ResetMacParameters( void ); + +static void OnRadioTxDone( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + SetBandTxDoneParams_t txDone; + TimerTime_t curTime = TimerGetCurrentTime( ); + + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + // Setup timers + if( IsRxWindowsEnabled == true ) + { + TimerSetValue( &RxWindowTimer1, RxWindow1Delay ); + TimerStart( &RxWindowTimer1 ); + if( LoRaMacDeviceClass != CLASS_C ) + { + TimerSetValue( &RxWindowTimer2, RxWindow2Delay ); + TimerStart( &RxWindowTimer2 ); + } + if( ( LoRaMacDeviceClass == CLASS_C ) || ( NodeAckRequested == true ) ) + { + getPhy.Attribute = PHY_ACK_TIMEOUT; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + phyParam.Value ); + TimerStart( &AckTimeoutTimer ); + } + } + else + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + + if( LoRaMacFlags.Value == 0 ) + { + LoRaMacFlags.Bits.McpsReq = 1; + } + LoRaMacFlags.Bits.MacDone = 1; + } + + // Store last Tx channel + LastTxChannel = Channel; + // Update last tx done time for the current channel + txDone.Channel = Channel; + txDone.LastTxDoneTime = curTime; + RegionSetBandTxDone( LoRaMacRegion, &txDone ); + // Update Aggregated last tx done time + AggregatedLastTxDoneTime = curTime; + + if( NodeAckRequested == false ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + ChannelsNbRepCounter++; + } +} + +static void PrepareRxDoneAbort( void ) +{ + LoRaMacState |= LORAMAC_RX_ABORT; + + if( NodeAckRequested ) + { + OnAckTimeoutTimerEvent( ); + } + + LoRaMacFlags.Bits.McpsInd = 1; + LoRaMacFlags.Bits.MacDone = 1; + + // Trig OnMacCheckTimerEvent call as soon as possible + TimerSetValue( &MacStateCheckTimer, 1 ); + TimerStart( &MacStateCheckTimer ); +} + +static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) +{ + LoRaMacHeader_t macHdr; + LoRaMacFrameCtrl_t fCtrl; + ApplyCFListParams_t applyCFList; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + bool skipIndication = false; + + uint8_t pktHeaderLen = 0; + uint32_t address = 0; + uint8_t appPayloadStartIndex = 0; + uint8_t port = 0xFF; + uint8_t frameLen = 0; + uint32_t mic = 0; + uint32_t micRx = 0; + + uint16_t sequenceCounter = 0; + uint16_t sequenceCounterPrev = 0; + uint16_t sequenceCounterDiff = 0; + uint32_t downLinkCounter = 0; + + MulticastParams_t *curMulticastParams = NULL; + uint8_t *nwkSKey = LoRaMacNwkSKey; + uint8_t *appSKey = LoRaMacAppSKey; + + uint8_t multicast = 0; + + bool isMicOk = false; + + McpsConfirm.AckReceived = false; + McpsIndication.Rssi = rssi; + McpsIndication.Snr = snr; + McpsIndication.RxSlot = RxSlot; + McpsIndication.Port = 0; + McpsIndication.Multicast = 0; + McpsIndication.FramePending = 0; + McpsIndication.Buffer = NULL; + McpsIndication.BufferSize = 0; + McpsIndication.RxData = false; + McpsIndication.AckReceived = false; + McpsIndication.DownLinkCounter = 0; + McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + + Radio.Sleep( ); + TimerStop( &RxWindowTimer2 ); + + macHdr.Value = payload[pktHeaderLen++]; + + switch( macHdr.Bits.MType ) + { + case FRAME_TYPE_JOIN_ACCEPT: + if( IsLoRaMacNetworkJoined == true ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 ); + + LoRaMacRxPayload[0] = macHdr.Value; + + LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic ); + + micRx |= ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN]; + micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 ); + micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 ); + micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 ); + + if( micRx == mic ) + { + LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey ); + + LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4]; + LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 ); + LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 ); + + LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7]; + LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 ); + LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 ); + LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 ); + + // DLSettings + LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07; + LoRaMacParams.Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F; + + // RxDelay + LoRaMacParams.ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F ); + if( LoRaMacParams.ReceiveDelay1 == 0 ) + { + LoRaMacParams.ReceiveDelay1 = 1; + } + LoRaMacParams.ReceiveDelay1 *= 1e3; + LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1e3; + + // Apply CF list + applyCFList.Payload = &LoRaMacRxPayload[13]; + // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC + applyCFList.Size = size - 17; + + RegionApplyCFList( LoRaMacRegion, &applyCFList ); + + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + IsLoRaMacNetworkJoined = true; + LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; + } + else + { + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + } + break; + case FRAME_TYPE_DATA_CONFIRMED_DOWN: + case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: + { + address = payload[pktHeaderLen++]; + address |= ( (uint32_t)payload[pktHeaderLen++] << 8 ); + address |= ( (uint32_t)payload[pktHeaderLen++] << 16 ); + address |= ( (uint32_t)payload[pktHeaderLen++] << 24 ); + + if( address != LoRaMacDevAddr ) + { + curMulticastParams = MulticastChannels; + while( curMulticastParams != NULL ) + { + if( address == curMulticastParams->Address ) + { + multicast = 1; + nwkSKey = curMulticastParams->NwkSKey; + appSKey = curMulticastParams->AppSKey; + downLinkCounter = curMulticastParams->DownLinkCounter; + break; + } + curMulticastParams = curMulticastParams->Next; + } + if( multicast == 0 ) + { + // We are not the destination of this frame. + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + PrepareRxDoneAbort( ); + return; + } + } + else + { + multicast = 0; + nwkSKey = LoRaMacNwkSKey; + appSKey = LoRaMacAppSKey; + downLinkCounter = DownLinkCounter; + } + + fCtrl.Value = payload[pktHeaderLen++]; + + sequenceCounter = ( uint16_t )payload[pktHeaderLen++]; + sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8; + + appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen; + + micRx |= ( uint32_t )payload[size - LORAMAC_MFR_LEN]; + micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 1] << 8 ); + micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 2] << 16 ); + micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 3] << 24 ); + + sequenceCounterPrev = ( uint16_t )downLinkCounter; + sequenceCounterDiff = ( sequenceCounter - sequenceCounterPrev ); + + if( sequenceCounterDiff < ( 1 << 15 ) ) + { + downLinkCounter += sequenceCounterDiff; + LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic ); + if( micRx == mic ) + { + isMicOk = true; + } + } + else + { + // check for sequence roll-over + uint32_t downLinkCounterTmp = downLinkCounter + 0x10000 + ( int16_t )sequenceCounterDiff; + LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounterTmp, &mic ); + if( micRx == mic ) + { + isMicOk = true; + downLinkCounter = downLinkCounterTmp; + } + } + + // Check for a the maximum allowed counter difference + getPhy.Attribute = PHY_MAX_FCNT_GAP; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + if( sequenceCounterDiff >= phyParam.Value ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; + McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + + if( isMicOk == true ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + McpsIndication.Multicast = multicast; + McpsIndication.FramePending = fCtrl.Bits.FPending; + McpsIndication.Buffer = NULL; + McpsIndication.BufferSize = 0; + McpsIndication.DownLinkCounter = downLinkCounter; + + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + + AdrAckCounter = 0; + MacCommandsBufferToRepeatIndex = 0; + + // Update 32 bits downlink counter + if( multicast == 1 ) + { + McpsIndication.McpsIndication = MCPS_MULTICAST; + + if( ( curMulticastParams->DownLinkCounter == downLinkCounter ) && + ( curMulticastParams->DownLinkCounter != 0 ) ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + curMulticastParams->DownLinkCounter = downLinkCounter; + } + else + { + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + SrvAckRequested = true; + McpsIndication.McpsIndication = MCPS_CONFIRMED; + + if( ( DownLinkCounter == downLinkCounter ) && + ( DownLinkCounter != 0 ) ) + { + // Duplicated confirmed downlink. Skip indication. + // In this case, the MAC layer shall accept the MAC commands + // which are included in the downlink retransmission. + // It should not provide the same frame to the application + // layer again. + skipIndication = true; + } + } + else + { + SrvAckRequested = false; + McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + + if( ( DownLinkCounter == downLinkCounter ) && + ( DownLinkCounter != 0 ) ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + } + DownLinkCounter = downLinkCounter; + } + + // This must be done before parsing the payload and the MAC commands. + // We need to reset the MacCommandsBufferIndex here, since we need + // to take retransmissions and repetitions into account. Error cases + // will be handled in function OnMacStateCheckTimerEvent. + if( McpsConfirm.McpsRequest == MCPS_CONFIRMED ) + { + if( fCtrl.Bits.Ack == 1 ) + {// Reset MacCommandsBufferIndex when we have received an ACK. + MacCommandsBufferIndex = 0; + } + } + else + {// Reset the variable if we have received any valid frame. + MacCommandsBufferIndex = 0; + } + + // Process payload and MAC commands + if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 ) + { + port = payload[appPayloadStartIndex++]; + frameLen = ( size - 4 ) - appPayloadStartIndex; + + McpsIndication.Port = port; + + if( port == 0 ) + { + // Only allow frames which do not have fOpts + if( fCtrl.Bits.FOptsLen == 0 ) + { + LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, + frameLen, + nwkSKey, + address, + DOWN_LINK, + downLinkCounter, + LoRaMacRxPayload ); + + // Decode frame payload MAC commands + ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr ); + } + else + { + skipIndication = true; + } + } + else + { + if( fCtrl.Bits.FOptsLen > 0 ) + { + // Decode Options field MAC commands. Omit the fPort. + ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr ); + } + + LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, + frameLen, + appSKey, + address, + DOWN_LINK, + downLinkCounter, + LoRaMacRxPayload ); + + if( skipIndication == false ) + { + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = frameLen; + McpsIndication.RxData = true; + } + } + } + else + { + if( fCtrl.Bits.FOptsLen > 0 ) + { + // Decode Options field MAC commands + ProcessMacCommands( payload, 8, appPayloadStartIndex, snr ); + } + } + + if( skipIndication == false ) + { + // Check if the frame is an acknowledgement + if( fCtrl.Bits.Ack == 1 ) + { + McpsConfirm.AckReceived = true; + McpsIndication.AckReceived = true; + + // Stop the AckTimeout timer as no more retransmissions + // are needed. + TimerStop( &AckTimeoutTimer ); + } + else + { + McpsConfirm.AckReceived = false; + + if( AckTimeoutRetriesCounter > AckTimeoutRetries ) + { + // Stop the AckTimeout timer as no more retransmissions + // are needed. + TimerStop( &AckTimeoutTimer ); + } + } + } + // Provide always an indication, skip the callback to the user application, + // in case of a confirmed downlink retransmission. + LoRaMacFlags.Bits.McpsInd = 1; + LoRaMacFlags.Bits.McpsIndSkip = skipIndication; + } + else + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + + PrepareRxDoneAbort( ); + return; + } + } + break; + case FRAME_TYPE_PROPRIETARY: + { + memcpy1( LoRaMacRxPayload, &payload[pktHeaderLen], size ); + + McpsIndication.McpsIndication = MCPS_PROPRIETARY; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = size - pktHeaderLen; + + LoRaMacFlags.Bits.McpsInd = 1; + break; + } + default: + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + break; + } + LoRaMacFlags.Bits.MacDone = 1; + + // Trig OnMacCheckTimerEvent call as soon as possible + TimerSetValue( &MacStateCheckTimer, 1 ); + TimerStart( &MacStateCheckTimer ); +} + +static void OnRadioTxTimeout( void ) +{ + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + LoRaMacFlags.Bits.MacDone = 1; +} + +static void OnRadioRxError( void ) +{ + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + if( RxSlot == 0 ) + { + if( NodeAckRequested == true ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; + } + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; + + if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) + { + LoRaMacFlags.Bits.MacDone = 1; + } + } + else + { + if( NodeAckRequested == true ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; + } + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; + LoRaMacFlags.Bits.MacDone = 1; + } +} + +static void OnRadioRxTimeout( void ) +{ + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + if( RxSlot == 1 ) + { + if( NodeAckRequested == true ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + } + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + + if( LoRaMacDeviceClass != CLASS_C ) + { + LoRaMacFlags.Bits.MacDone = 1; + } + } +} + +static void OnMacStateCheckTimerEvent( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + bool txTimeout = false; + + TimerStop( &MacStateCheckTimer ); + + if( LoRaMacFlags.Bits.MacDone == 1 ) + { + if( ( LoRaMacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT ) + { + LoRaMacState &= ~LORAMAC_RX_ABORT; + LoRaMacState &= ~LORAMAC_TX_RUNNING; + } + + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) ) + { + if( ( McpsConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) || + ( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) ) + { + // Stop transmit cycle due to tx timeout. + LoRaMacState &= ~LORAMAC_TX_RUNNING; + MacCommandsBufferIndex = 0; + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + McpsConfirm.AckReceived = false; + McpsConfirm.TxTimeOnAir = 0; + txTimeout = true; + } + } + + if( ( NodeAckRequested == false ) && ( txTimeout == false ) ) + { + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) ) + { + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) + {// Procedure for the join request + MlmeConfirm.NbRetries = JoinRequestTrials; + + if( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_OK ) + {// Node joined successfully + UpLinkCounter = 0; + ChannelsNbRepCounter = 0; + LoRaMacState &= ~LORAMAC_TX_RUNNING; + } + else + { + if( JoinRequestTrials >= MaxJoinRequestTrials ) + { + LoRaMacState &= ~LORAMAC_TX_RUNNING; + } + else + { + LoRaMacFlags.Bits.MacDone = 0; + // Sends the same frame again + OnTxDelayedTimerEvent( ); + } + } + } + else + {// Procedure for all other frames + if( ( ChannelsNbRepCounter >= LoRaMacParams.ChannelsNbRep ) || ( LoRaMacFlags.Bits.McpsInd == 1 ) ) + { + if( LoRaMacFlags.Bits.McpsInd == 0 ) + { // Maximum repititions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter. + // Only process the case when the MAC did not receive a downlink. + MacCommandsBufferIndex = 0; + AdrAckCounter++; + } + + ChannelsNbRepCounter = 0; + + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + + LoRaMacState &= ~LORAMAC_TX_RUNNING; + } + else + { + LoRaMacFlags.Bits.MacDone = 0; + // Sends the same frame again + OnTxDelayedTimerEvent( ); + } + } + } + } + + if( LoRaMacFlags.Bits.McpsInd == 1 ) + {// Procedure if we received a frame + if( ( McpsConfirm.AckReceived == true ) || ( AckTimeoutRetriesCounter > AckTimeoutRetries ) ) + { + AckTimeoutRetry = false; + NodeAckRequested = false; + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + + LoRaMacState &= ~LORAMAC_TX_RUNNING; + } + } + + if( ( AckTimeoutRetry == true ) && ( ( LoRaMacState & LORAMAC_TX_DELAYED ) == 0 ) ) + {// Retransmissions procedure for confirmed uplinks + AckTimeoutRetry = false; + if( ( AckTimeoutRetriesCounter < AckTimeoutRetries ) && ( AckTimeoutRetriesCounter <= MAX_ACK_RETRIES ) ) + { + AckTimeoutRetriesCounter++; + + if( ( AckTimeoutRetriesCounter % 2 ) == 1 ) + { + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + getPhy.Datarate = LoRaMacParams.ChannelsDatarate; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParams.ChannelsDatarate = phyParam.Value; + } + // Try to send the frame again + if( ScheduleTx( ) == LORAMAC_STATUS_OK ) + { + LoRaMacFlags.Bits.MacDone = 0; + } + else + { + // The DR is not applicable for the payload size + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; + + MacCommandsBufferIndex = 0; + LoRaMacState &= ~LORAMAC_TX_RUNNING; + NodeAckRequested = false; + McpsConfirm.AckReceived = false; + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate; + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + } + } + else + { + RegionInitDefaults( LoRaMacRegion, INIT_TYPE_RESTORE ); + + LoRaMacState &= ~LORAMAC_TX_RUNNING; + + MacCommandsBufferIndex = 0; + NodeAckRequested = false; + McpsConfirm.AckReceived = false; + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + } + } + } + // Handle reception for Class B and Class C + if( ( LoRaMacState & LORAMAC_RX ) == LORAMAC_RX ) + { + LoRaMacState &= ~LORAMAC_RX; + } + if( LoRaMacState == LORAMAC_IDLE ) + { + if( LoRaMacFlags.Bits.McpsReq == 1 ) + { + LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm ); + LoRaMacFlags.Bits.McpsReq = 0; + } + + if( LoRaMacFlags.Bits.MlmeReq == 1 ) + { + LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); + LoRaMacFlags.Bits.MlmeReq = 0; + } + + // Procedure done. Reset variables. + LoRaMacFlags.Bits.MacDone = 0; + } + else + { + // Operation not finished restart timer + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + TimerStart( &MacStateCheckTimer ); + } + + if( LoRaMacFlags.Bits.McpsInd == 1 ) + { + if( LoRaMacDeviceClass == CLASS_C ) + {// Activate RX2 window for Class C + OnRxWindow2TimerEvent( ); + } + if( LoRaMacFlags.Bits.McpsIndSkip == 0 ) + { + LoRaMacPrimitives->MacMcpsIndication( &McpsIndication ); + } + LoRaMacFlags.Bits.McpsIndSkip = 0; + LoRaMacFlags.Bits.McpsInd = 0; + } +} + +static void OnTxDelayedTimerEvent( void ) +{ + LoRaMacHeader_t macHdr; + LoRaMacFrameCtrl_t fCtrl; + AlternateDrParams_t altDr; + + TimerStop( &TxDelayedTimer ); + LoRaMacState &= ~LORAMAC_TX_DELAYED; + + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) + { + ResetMacParameters( ); + + altDr.NbTrials = JoinRequestTrials + 1; + LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); + + macHdr.Value = 0; + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + + fCtrl.Value = 0; + fCtrl.Bits.Adr = AdrCtrlOn; + + /* In case of join request retransmissions, the stack must prepare + * the frame again, because the network server keeps track of the random + * LoRaMacDevNonce values to prevent reply attacks. */ + PrepareFrame( &macHdr, &fCtrl, 0, NULL, 0 ); + } + + ScheduleTx( ); +} + +static void OnRxWindow1TimerEvent( void ) +{ + TimerStop( &RxWindowTimer1 ); + RxSlot = 0; + + RxWindow1Config.Channel = Channel; + RxWindow1Config.DrOffset = LoRaMacParams.Rx1DrOffset; + RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; + RxWindow1Config.RepeaterSupport = RepeaterSupport; + RxWindow1Config.RxContinuous = false; + RxWindow1Config.Window = RxSlot; + + if( LoRaMacDeviceClass == CLASS_C ) + { + Radio.Standby( ); + } + + RegionRxConfig( LoRaMacRegion, &RxWindow1Config, ( int8_t* )&McpsIndication.RxDatarate ); + RxWindowSetup( RxWindow1Config.RxContinuous, LoRaMacParams.MaxRxWindow ); +} + +static void OnRxWindow2TimerEvent( void ) +{ + TimerStop( &RxWindowTimer2 ); + + RxWindow2Config.Channel = Channel; + RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; + RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; + RxWindow2Config.RepeaterSupport = RepeaterSupport; + RxWindow2Config.Window = 1; + + if( LoRaMacDeviceClass != CLASS_C ) + { + RxWindow2Config.RxContinuous = false; + } + else + { + RxWindow2Config.RxContinuous = true; + } + + if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true ) + { + RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); + RxSlot = RxWindow2Config.Window; + } +} + +static void OnAckTimeoutTimerEvent( void ) +{ + TimerStop( &AckTimeoutTimer ); + + if( NodeAckRequested == true ) + { + AckTimeoutRetry = true; + LoRaMacState &= ~LORAMAC_ACK_REQ; + } + if( LoRaMacDeviceClass == CLASS_C ) + { + LoRaMacFlags.Bits.MacDone = 1; + } +} + +static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow ) +{ + if( rxContinuous == false ) + { + Radio.Rx( maxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } +} + +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint16_t maxN = 0; + uint16_t payloadSize = 0; + + // Setup PHY request + getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + getPhy.Datarate = datarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + + // Get the maximum payload length + if( RepeaterSupport == true ) + { + getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + } + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + maxN = phyParam.Value; + + // Calculate the resulting payload size + payloadSize = ( lenN + fOptsLen ); + + // Validation of the application payload size + if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) ) + { + return true; + } + return false; +} + +static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_BUSY; + // The maximum buffer length must take MAC commands to re-send into account. + uint8_t bufLen = LORA_MAC_COMMAND_MAX_LENGTH - MacCommandsBufferToRepeatIndex; + + switch( cmd ) + { + case MOTE_MAC_LINK_CHECK_REQ: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this command + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_LINK_ADR_ANS: + if( MacCommandsBufferIndex < ( bufLen - 1 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Margin + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_DUTY_CYCLE_ANS: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this answer + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_RX_PARAM_SETUP_ANS: + if( MacCommandsBufferIndex < ( bufLen - 1 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Status: Datarate ACK, Channel ACK + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_DEV_STATUS_ANS: + if( MacCommandsBufferIndex < ( bufLen - 2 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // 1st byte Battery + // 2nd byte Margin + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + MacCommandsBuffer[MacCommandsBufferIndex++] = p2; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_NEW_CHANNEL_ANS: + if( MacCommandsBufferIndex < ( bufLen - 1 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Status: Datarate range OK, Channel frequency OK + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_RX_TIMING_SETUP_ANS: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this answer + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_TX_PARAM_SETUP_ANS: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this answer + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_DL_CHANNEL_ANS: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Status: Uplink frequency exists, Channel frequency OK + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + default: + return LORAMAC_STATUS_SERVICE_UNKNOWN; + } + if( status == LORAMAC_STATUS_OK ) + { + MacCommandsInNextTx = true; + } + return status; +} + +static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ) +{ + uint8_t i = 0; + uint8_t cmdCount = 0; + + if( ( cmdBufIn == NULL ) || ( cmdBufOut == NULL ) ) + { + return 0; + } + + for( i = 0; i < length; i++ ) + { + switch( cmdBufIn[i] ) + { + // STICKY + case MOTE_MAC_DL_CHANNEL_ANS: + case MOTE_MAC_RX_PARAM_SETUP_ANS: + { // 1 byte payload + cmdBufOut[cmdCount++] = cmdBufIn[i++]; + cmdBufOut[cmdCount++] = cmdBufIn[i]; + break; + } + case MOTE_MAC_RX_TIMING_SETUP_ANS: + { // 0 byte payload + cmdBufOut[cmdCount++] = cmdBufIn[i]; + break; + } + // NON-STICKY + case MOTE_MAC_DEV_STATUS_ANS: + { // 2 bytes payload + i += 2; + break; + } + case MOTE_MAC_LINK_ADR_ANS: + case MOTE_MAC_NEW_CHANNEL_ANS: + { // 1 byte payload + i++; + break; + } + case MOTE_MAC_TX_PARAM_SETUP_ANS: + case MOTE_MAC_DUTY_CYCLE_ANS: + case MOTE_MAC_LINK_CHECK_REQ: + { // 0 byte payload + break; + } + default: + break; + } + } + + return cmdCount; +} + +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ) +{ + uint8_t status = 0; + + while( macIndex < commandsSize ) + { + // Decode Frame MAC commands + switch( payload[macIndex++] ) + { + case SRV_MAC_LINK_CHECK_ANS: + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MlmeConfirm.DemodMargin = payload[macIndex++]; + MlmeConfirm.NbGateways = payload[macIndex++]; + break; + case SRV_MAC_LINK_ADR_REQ: + { + LinkAdrReqParams_t linkAdrReq; + int8_t linkAdrDatarate = DR_0; + int8_t linkAdrTxPower = TX_POWER_0; + uint8_t linkAdrNbRep = 0; + uint8_t linkAdrNbBytesParsed = 0; + + // Fill parameter structure + linkAdrReq.Payload = &payload[macIndex - 1]; + linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 ); + + // Process the ADR requests + status = RegionLinkAdrReq( LoRaMacRegion, &linkAdrReq, &linkAdrDatarate, + &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed ); + + if( ( status & 0x07 ) == 0x07 ) + { + if( ( AdrCtrlOn == false ) && + ( ( LoRaMacParams.ChannelsDatarate != linkAdrDatarate ) || + ( LoRaMacParams.ChannelsTxPower != linkAdrTxPower ) ) ) + { // ADR disabled don't handle ADR requests if server tries to change datarate or tx power + status = 0; + } + else + { + LoRaMacParams.ChannelsDatarate = linkAdrDatarate; + LoRaMacParams.ChannelsTxPower = linkAdrTxPower; + LoRaMacParams.ChannelsNbRep = linkAdrNbRep; + } + } + + // Add the answers to the buffer + for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ ) + { + AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 ); + } + // Update MAC index + macIndex += linkAdrNbBytesParsed - 1; + } + break; + case SRV_MAC_DUTY_CYCLE_REQ: + MaxDCycle = payload[macIndex++]; + AggregatedDCycle = 1 << MaxDCycle; + AddMacCommand( MOTE_MAC_DUTY_CYCLE_ANS, 0, 0 ); + break; + case SRV_MAC_RX_PARAM_SETUP_REQ: + { + RxParamSetupReqParams_t rxParamSetupReq; + status = 0x07; + + rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07; + rxParamSetupReq.Datarate = payload[macIndex] & 0x0F; + macIndex++; + + rxParamSetupReq.Frequency = ( uint32_t )payload[macIndex++]; + rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 8; + rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 16; + rxParamSetupReq.Frequency *= 100; + + // Perform request on region + status = RegionRxParamSetupReq( LoRaMacRegion, &rxParamSetupReq ); + + if( ( status & 0x07 ) == 0x07 ) + { + LoRaMacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate; + LoRaMacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency; + LoRaMacParams.Rx1DrOffset = rxParamSetupReq.DrOffset; + } + AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 ); + } + break; + case SRV_MAC_DEV_STATUS_REQ: + { + uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE; + if( ( LoRaMacCallbacks != NULL ) && ( LoRaMacCallbacks->GetBatteryLevel != NULL ) ) + { + batteryLevel = LoRaMacCallbacks->GetBatteryLevel( ); + } + AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr ); + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: + { + NewChannelReqParams_t newChannelReq; + ChannelParams_t chParam; + status = 0x03; + + newChannelReq.ChannelId = payload[macIndex++]; + newChannelReq.NewChannel = &chParam; + + chParam.Frequency = ( uint32_t )payload[macIndex++]; + chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8; + chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16; + chParam.Frequency *= 100; + chParam.Rx1Frequency = 0; + chParam.DrRange.Value = payload[macIndex++]; + + status = RegionNewChannelReq( LoRaMacRegion, &newChannelReq ); + + AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 ); + } + break; + case SRV_MAC_RX_TIMING_SETUP_REQ: + { + uint8_t delay = payload[macIndex++] & 0x0F; + + if( delay == 0 ) + { + delay++; + } + LoRaMacParams.ReceiveDelay1 = delay * 1e3; + LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1e3; + AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 ); + } + break; + case SRV_MAC_TX_PARAM_SETUP_REQ: + { + TxParamSetupReqParams_t txParamSetupReq; + uint8_t eirpDwellTime = payload[macIndex++]; + + txParamSetupReq.UplinkDwellTime = 0; + txParamSetupReq.DownlinkDwellTime = 0; + + if( ( eirpDwellTime & 0x20 ) == 0x20 ) + { + txParamSetupReq.UplinkDwellTime = 1; + } + if( ( eirpDwellTime & 0x10 ) == 0x10 ) + { + txParamSetupReq.DownlinkDwellTime = 1; + } + txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F; + + // Check the status for correctness + if( RegionTxParamSetupReq( LoRaMacRegion, &txParamSetupReq ) != -1 ) + { + // Accept command + LoRaMacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime; + LoRaMacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime; + LoRaMacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp]; + // Add command response + AddMacCommand( MOTE_MAC_TX_PARAM_SETUP_ANS, 0, 0 ); + } + } + break; + case SRV_MAC_DL_CHANNEL_REQ: + { + DlChannelReqParams_t dlChannelReq; + status = 0x03; + + dlChannelReq.ChannelId = payload[macIndex++]; + dlChannelReq.Rx1Frequency = ( uint32_t )payload[macIndex++]; + dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 8; + dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 16; + dlChannelReq.Rx1Frequency *= 100; + + status = RegionDlChannelReq( LoRaMacRegion, &dlChannelReq ); + + AddMacCommand( MOTE_MAC_DL_CHANNEL_ANS, status, 0 ); + } + break; + default: + // Unknown command. ABORT MAC commands processing + return; + } + } +} + +LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + LoRaMacFrameCtrl_t fCtrl; + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + + fCtrl.Value = 0; + fCtrl.Bits.FOptsLen = 0; + fCtrl.Bits.FPending = 0; + fCtrl.Bits.Ack = false; + fCtrl.Bits.AdrAckReq = false; + fCtrl.Bits.Adr = AdrCtrlOn; + + // Prepare the frame + status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize ); + + // Validate status + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // Reset confirm parameters + McpsConfirm.NbRetries = 0; + McpsConfirm.AckReceived = false; + McpsConfirm.UpLinkCounter = UpLinkCounter; + + status = ScheduleTx( ); + return status; +} + +static LoRaMacStatus_t ScheduleTx( void ) +{ + TimerTime_t dutyCycleTimeOff = 0; + NextChanParams_t nextChan; + + // Check if the device is off + if( MaxDCycle == 255 ) + { + return LORAMAC_STATUS_DEVICE_OFF; + } + if( MaxDCycle == 0 ) + { + AggregatedTimeOff = 0; + } + + // Update Backoff + CalculateBackOff( LastTxChannel ); + + nextChan.AggrTimeOff = AggregatedTimeOff; + nextChan.Datarate = LoRaMacParams.ChannelsDatarate; + nextChan.DutyCycleEnabled = DutyCycleOn; + nextChan.Joined = IsLoRaMacNetworkJoined; + nextChan.LastAggrTx = AggregatedLastTxDoneTime; + + // Select channel + while( RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false ) + { + // Set the default datarate + LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; + // Update datarate in the function parameters + nextChan.Datarate = LoRaMacParams.ChannelsDatarate; + } + + // Compute Rx1 windows parameters + RegionComputeRxWindowParameters( LoRaMacRegion, + RegionApplyDrOffset( LoRaMacRegion, LoRaMacParams.DownlinkDwellTime, LoRaMacParams.ChannelsDatarate, LoRaMacParams.Rx1DrOffset ), + LoRaMacParams.MinRxSymbols, + LoRaMacParams.SystemMaxRxError, + &RxWindow1Config ); + // Compute Rx2 windows parameters + RegionComputeRxWindowParameters( LoRaMacRegion, + LoRaMacParams.Rx2Channel.Datarate, + LoRaMacParams.MinRxSymbols, + LoRaMacParams.SystemMaxRxError, + &RxWindow2Config ); + + if( IsLoRaMacNetworkJoined == false ) + { + RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset; + RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset; + } + else + { + if( ValidatePayloadLength( LoRaMacTxPayloadLen, LoRaMacParams.ChannelsDatarate, MacCommandsBufferIndex ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset; + RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset; + } +dutyCycleTimeOff = 0; ////<-------- TO BE REMOVED + // Schedule transmission of frame + if( dutyCycleTimeOff == 0 ) + { + // Try to send now + return SendFrameOnChannel( Channel ); + } + else + { + // Send later - prepare timer + LoRaMacState |= LORAMAC_TX_DELAYED; + TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff ); + TimerStart( &TxDelayedTimer ); + + return LORAMAC_STATUS_OK; + } +} + +static void CalculateBackOff( uint8_t channel ) +{ + CalcBackOffParams_t calcBackOff; + + calcBackOff.Joined = IsLoRaMacNetworkJoined; + calcBackOff.DutyCycleEnabled = DutyCycleOn; + calcBackOff.Channel = channel; + calcBackOff.ElapsedTime = TimerGetElapsedTime( LoRaMacInitializationTime ); + calcBackOff.TxTimeOnAir = TxTimeOnAir; + + // Update regional back-off + RegionCalcBackOff( LoRaMacRegion, &calcBackOff ); + + // Update aggregated time-off + AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); +} + +static void ResetMacParameters( void ) +{ + IsLoRaMacNetworkJoined = false; + + // Counters + UpLinkCounter = 0; + DownLinkCounter = 0; + AdrAckCounter = 0; + + ChannelsNbRepCounter = 0; + + AckTimeoutRetries = 1; + AckTimeoutRetriesCounter = 1; + AckTimeoutRetry = false; + + MaxDCycle = 0; + AggregatedDCycle = 1; + + MacCommandsBufferIndex = 0; + MacCommandsBufferToRepeatIndex = 0; + + IsRxWindowsEnabled = true; + + LoRaMacParams.ChannelsTxPower = LoRaMacParamsDefaults.ChannelsTxPower; + LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; + LoRaMacParams.Rx1DrOffset = LoRaMacParamsDefaults.Rx1DrOffset; + LoRaMacParams.Rx2Channel = LoRaMacParamsDefaults.Rx2Channel; + LoRaMacParams.UplinkDwellTime = LoRaMacParamsDefaults.UplinkDwellTime; + LoRaMacParams.DownlinkDwellTime = LoRaMacParamsDefaults.DownlinkDwellTime; + LoRaMacParams.MaxEirp = LoRaMacParamsDefaults.MaxEirp; + LoRaMacParams.AntennaGain = LoRaMacParamsDefaults.AntennaGain; + + NodeAckRequested = false; + SrvAckRequested = false; + MacCommandsInNextTx = false; + + // Reset Multicast downlink counters + MulticastParams_t *cur = MulticastChannels; + while( cur != NULL ) + { + cur->DownLinkCounter = 0; + cur = cur->Next; + } + + // Initialize channel index. + Channel = 0; + LastTxChannel = Channel; +} + +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + AdrNextParams_t adrNext; + uint16_t i; + uint8_t pktHeaderLen = 0; + uint32_t mic = 0; + const void* payload = fBuffer; + uint8_t framePort = fPort; + + LoRaMacBufferPktLen = 0; + + NodeAckRequested = false; + + if( fBuffer == NULL ) + { + fBufferSize = 0; + } + + LoRaMacTxPayloadLen = fBufferSize; + + LoRaMacBuffer[pktHeaderLen++] = macHdr->Value; + + switch( macHdr->Bits.MType ) + { + case FRAME_TYPE_JOIN_REQ: + LoRaMacBufferPktLen = pktHeaderLen; + + memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacAppEui, 8 ); + LoRaMacBufferPktLen += 8; + memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacDevEui, 8 ); + LoRaMacBufferPktLen += 8; + + LoRaMacDevNonce = Radio.Random( ); + + LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF; + + LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic ); + + LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF; + + break; + case FRAME_TYPE_DATA_CONFIRMED_UP: + NodeAckRequested = true; + //Intentional fallthrough + case FRAME_TYPE_DATA_UNCONFIRMED_UP: + if( IsLoRaMacNetworkJoined == false ) + { + return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet + } + + // Adr next request + adrNext.UpdateChanMask = true; + adrNext.AdrEnabled = fCtrl->Bits.Adr; + adrNext.AdrAckCounter = AdrAckCounter; + adrNext.Datarate = LoRaMacParams.ChannelsDatarate; + adrNext.TxPower = LoRaMacParams.ChannelsTxPower; + adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + + fCtrl->Bits.AdrAckReq = RegionAdrNext( LoRaMacRegion, &adrNext, + &LoRaMacParams.ChannelsDatarate, &LoRaMacParams.ChannelsTxPower, &AdrAckCounter ); + + if( SrvAckRequested == true ) + { + SrvAckRequested = false; + fCtrl->Bits.Ack = 1; + } + + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF; + + LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value; + + LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF; + + // Copy the MAC commands which must be re-send into the MAC command buffer + memcpy1( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex ); + MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex; + + if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) ) + { + if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) ) + { + fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; + + // Update FCtrl field with new value of OptionsLength + LoRaMacBuffer[0x05] = fCtrl->Value; + for( i = 0; i < MacCommandsBufferIndex; i++ ) + { + LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; + } + } + } + else + { + if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) ) + { + LoRaMacTxPayloadLen = MacCommandsBufferIndex; + payload = MacCommandsBuffer; + framePort = 0; + } + } + MacCommandsInNextTx = false; + // Store MAC commands which must be re-send in case the device does not receive a downlink anymore + MacCommandsBufferToRepeatIndex = ParseMacCommandsToRepeat( MacCommandsBuffer, MacCommandsBufferIndex, MacCommandsBufferToRepeat ); + if( MacCommandsBufferToRepeatIndex > 0 ) + { + MacCommandsInNextTx = true; + } + + if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) ) + { + LoRaMacBuffer[pktHeaderLen++] = framePort; + + if( framePort == 0 ) + { + LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] ); + } + else + { + LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] ); + } + } + LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen; + + LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic ); + + LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF; + + LoRaMacBufferPktLen += LORAMAC_MFR_LEN; + + break; + case FRAME_TYPE_PROPRIETARY: + if( ( fBuffer != NULL ) && ( LoRaMacTxPayloadLen > 0 ) ) + { + memcpy1( LoRaMacBuffer + pktHeaderLen, ( uint8_t* ) fBuffer, LoRaMacTxPayloadLen ); + LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen; + } + break; + default: + return LORAMAC_STATUS_SERVICE_UNKNOWN; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) +{ + TxConfigParams_t txConfig; + int8_t txPower = 0; + + txConfig.Channel = channel; + txConfig.Datarate = LoRaMacParams.ChannelsDatarate; + txConfig.TxPower = LoRaMacParams.ChannelsTxPower; + txConfig.MaxEirp = LoRaMacParams.MaxEirp; + txConfig.AntennaGain = LoRaMacParams.AntennaGain; + txConfig.PktLen = LoRaMacBufferPktLen; + + RegionTxConfig( LoRaMacRegion, &txConfig, &txPower, &TxTimeOnAir ); + + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate; + McpsConfirm.TxPower = txPower; + + // Store the time on air + McpsConfirm.TxTimeOnAir = TxTimeOnAir; + MlmeConfirm.TxTimeOnAir = TxTimeOnAir; + + // Starts the MAC layer status check timer + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + TimerStart( &MacStateCheckTimer ); + + if( IsLoRaMacNetworkJoined == false ) + { + JoinRequestTrials++; + } + + // Send now + Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen ); + + LoRaMacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ) +{ + ContinuousWaveParams_t continuousWave; + + continuousWave.Channel = Channel; + continuousWave.Datarate = LoRaMacParams.ChannelsDatarate; + continuousWave.TxPower = LoRaMacParams.ChannelsTxPower; + continuousWave.MaxEirp = LoRaMacParams.MaxEirp; + continuousWave.AntennaGain = LoRaMacParams.AntennaGain; + continuousWave.Timeout = timeout; + + RegionSetContinuousWave( LoRaMacRegion, &continuousWave ); + + // Starts the MAC layer status check timer + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + TimerStart( &MacStateCheckTimer ); + + LoRaMacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ) +{ + Radio.SetTxContinuousWave( frequency, power, timeout ); + + // Starts the MAC layer status check timer + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + TimerStart( &MacStateCheckTimer ); + + LoRaMacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks, LoRaMacRegion_t region, uint8_t dr, uint8_t txPower ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + if( primitives == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( primitives->MacMcpsConfirm == NULL ) || + ( primitives->MacMcpsIndication == NULL ) || + ( primitives->MacMlmeConfirm == NULL ) ) + + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Verify if the region is supported + if( RegionIsActive( region ) == false ) + + { + return LORAMAC_STATUS_REGION_NOT_SUPPORTED; + } + + LoRaMacPrimitives = primitives; + LoRaMacCallbacks = callbacks; + LoRaMacRegion = region; + + LoRaMacFlags.Value = 0; + + LoRaMacDeviceClass = CLASS_A; + LoRaMacState = LORAMAC_IDLE; + + JoinRequestTrials = 0; + MaxJoinRequestTrials = 1; + RepeaterSupport = false; + + // Reset duty cycle times + AggregatedLastTxDoneTime = 0; + AggregatedTimeOff = 0; + + // Reset to defaults + getPhy.Attribute = PHY_DUTY_CYCLE; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + DutyCycleOn = ( bool ) phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_POWER; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + // LoRaMacParamsDefaults.ChannelsTxPower = phyParam.Value; + LoRaMacParamsDefaults.ChannelsTxPower = txPower; + + getPhy.Attribute = PHY_DEF_TX_DR; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + // LoRaMacParamsDefaults.ChannelsDatarate = phyParam.Value; + LoRaMacParamsDefaults.ChannelsDatarate = dr; + + getPhy.Attribute = PHY_MAX_RX_WINDOW; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.MaxRxWindow = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY1; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.ReceiveDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY2; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.ReceiveDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.JoinAcceptDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.JoinAcceptDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DR1_OFFSET; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.Rx1DrOffset = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_FREQUENCY; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.Rx2Channel.Frequency = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_DR; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.Rx2Channel.Datarate = phyParam.Value; + + getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.UplinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.DownlinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_MAX_EIRP; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.MaxEirp = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ANTENNA_GAIN; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + LoRaMacParamsDefaults.AntennaGain = phyParam.fValue; + + RegionInitDefaults( LoRaMacRegion, INIT_TYPE_INIT ); + + // Init parameters which are not set in function ResetMacParameters + LoRaMacParamsDefaults.ChannelsNbRep = 1; + LoRaMacParamsDefaults.SystemMaxRxError = 10; + LoRaMacParamsDefaults.MinRxSymbols = 6; + + LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError; + LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols; + LoRaMacParams.MaxRxWindow = LoRaMacParamsDefaults.MaxRxWindow; + LoRaMacParams.ReceiveDelay1 = LoRaMacParamsDefaults.ReceiveDelay1; + LoRaMacParams.ReceiveDelay2 = LoRaMacParamsDefaults.ReceiveDelay2; + LoRaMacParams.JoinAcceptDelay1 = LoRaMacParamsDefaults.JoinAcceptDelay1; + LoRaMacParams.JoinAcceptDelay2 = LoRaMacParamsDefaults.JoinAcceptDelay2; + LoRaMacParams.ChannelsNbRep = LoRaMacParamsDefaults.ChannelsNbRep; + + ResetMacParameters( ); + + // Initialize timers + TimerInit( &MacStateCheckTimer, OnMacStateCheckTimerEvent ); + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + + TimerInit( &TxDelayedTimer, OnTxDelayedTimerEvent ); + TimerInit( &RxWindowTimer1, OnRxWindow1TimerEvent ); + TimerInit( &RxWindowTimer2, OnRxWindow2TimerEvent ); + TimerInit( &AckTimeoutTimer, OnAckTimeoutTimerEvent ); + + // Store the current initialization time + LoRaMacInitializationTime = TimerGetCurrentTime( ); + + // Initialize Radio driver + RadioEvents.TxDone = OnRadioTxDone; + RadioEvents.RxDone = OnRadioRxDone; + RadioEvents.RxError = OnRadioRxError; + RadioEvents.TxTimeout = OnRadioTxTimeout; + RadioEvents.RxTimeout = OnRadioRxTimeout; + Radio.Init( &RadioEvents ); + + // Random seed initialization + srand1( Radio.Random( ) ); + + PublicNetwork = true; + Radio.SetPublicNetwork( PublicNetwork ); + Radio.Sleep( ); + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ) +{ + AdrNextParams_t adrNext; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + int8_t datarate = LoRaMacParamsDefaults.ChannelsDatarate; + int8_t txPower = LoRaMacParamsDefaults.ChannelsTxPower; + uint8_t fOptLen = MacCommandsBufferIndex + MacCommandsBufferToRepeatIndex; + + if( txInfo == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Setup ADR request + adrNext.UpdateChanMask = false; + adrNext.AdrEnabled = AdrCtrlOn; + adrNext.AdrAckCounter = AdrAckCounter; + adrNext.Datarate = LoRaMacParams.ChannelsDatarate; + adrNext.TxPower = LoRaMacParams.ChannelsTxPower; + adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + + // We call the function for information purposes only. We don't want to + // apply the datarate, the tx power and the ADR ack counter. + RegionAdrNext( LoRaMacRegion, &adrNext, &datarate, &txPower, &AdrAckCounter ); + + // Setup PHY request + getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + getPhy.Datarate = datarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + + // Change request in case repeater is supported + if( RepeaterSupport == true ) + { + getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + } + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + txInfo->CurrentPayloadSize = phyParam.Value; + + // Verify if the fOpts fit into the maximum payload + if( txInfo->CurrentPayloadSize >= fOptLen ) + { + txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - fOptLen; + } + else + { + txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize; + // The fOpts don't fit into the maximum payload. Omit the MAC commands to + // ensure that another uplink is possible. + fOptLen = 0; + MacCommandsBufferIndex = 0; + MacCommandsBufferToRepeatIndex = 0; + } + + // Verify if the fOpts and the payload fit into the maximum payload + if( ValidatePayloadLength( size, datarate, fOptLen ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + if( mibGet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + switch( mibGet->Type ) + { + case MIB_DEVICE_CLASS: + { + mibGet->Param.Class = LoRaMacDeviceClass; + break; + } + case MIB_NETWORK_JOINED: + { + mibGet->Param.IsNetworkJoined = IsLoRaMacNetworkJoined; + break; + } + case MIB_ADR: + { + mibGet->Param.AdrEnable = AdrCtrlOn; + break; + } + case MIB_NET_ID: + { + mibGet->Param.NetID = LoRaMacNetID; + break; + } + case MIB_DEV_ADDR: + { + mibGet->Param.DevAddr = LoRaMacDevAddr; + break; + } + case MIB_NWK_SKEY: + { + mibGet->Param.NwkSKey = LoRaMacNwkSKey; + break; + } + case MIB_APP_SKEY: + { + mibGet->Param.AppSKey = LoRaMacAppSKey; + break; + } + case MIB_PUBLIC_NETWORK: + { + mibGet->Param.EnablePublicNetwork = PublicNetwork; + break; + } + case MIB_REPEATER_SUPPORT: + { + mibGet->Param.EnableRepeaterSupport = RepeaterSupport; + break; + } + case MIB_CHANNELS: + { + getPhy.Attribute = PHY_CHANNELS; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + + mibGet->Param.ChannelList = phyParam.Channels; + break; + } + case MIB_RX2_CHANNEL: + { + mibGet->Param.Rx2Channel = LoRaMacParams.Rx2Channel; + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + mibGet->Param.Rx2Channel = LoRaMacParamsDefaults.Rx2Channel; + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + + mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_MASK: + { + getPhy.Attribute = PHY_CHANNELS_MASK; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + + mibGet->Param.ChannelsMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_NB_REP: + { + mibGet->Param.ChannelNbRep = LoRaMacParams.ChannelsNbRep; + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + mibGet->Param.MaxRxWindow = LoRaMacParams.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + mibGet->Param.ReceiveDelay1 = LoRaMacParams.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + mibGet->Param.ReceiveDelay2 = LoRaMacParams.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + mibGet->Param.JoinAcceptDelay1 = LoRaMacParams.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + mibGet->Param.JoinAcceptDelay2 = LoRaMacParams.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + mibGet->Param.ChannelsDefaultDatarate = LoRaMacParamsDefaults.ChannelsDatarate; + break; + } + case MIB_CHANNELS_DATARATE: + { + mibGet->Param.ChannelsDatarate = LoRaMacParams.ChannelsDatarate; + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + mibGet->Param.ChannelsDefaultTxPower = LoRaMacParamsDefaults.ChannelsTxPower; + break; + } + case MIB_CHANNELS_TX_POWER: + { + mibGet->Param.ChannelsTxPower = LoRaMacParams.ChannelsTxPower; + break; + } + case MIB_UPLINK_COUNTER: + { + mibGet->Param.UpLinkCounter = UpLinkCounter; + break; + } + case MIB_DOWNLINK_COUNTER: + { + mibGet->Param.DownLinkCounter = DownLinkCounter; + break; + } + case MIB_MULTICAST_CHANNEL: + { + mibGet->Param.MulticastList = MulticastChannels; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + mibGet->Param.SystemMaxRxError = LoRaMacParams.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + mibGet->Param.MinRxSymbols = LoRaMacParams.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + mibGet->Param.AntennaGain = LoRaMacParams.AntennaGain; + break; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + + return status; +} + +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + ChanMaskSetParams_t chanMaskSet; + VerifyParams_t verify; + + if( mibSet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + switch( mibSet->Type ) + { + case MIB_DEVICE_CLASS: + { + LoRaMacDeviceClass = mibSet->Param.Class; + switch( LoRaMacDeviceClass ) + { + case CLASS_A: + { + // Set the radio into sleep to setup a defined state + Radio.Sleep( ); + break; + } + case CLASS_B: + { + break; + } + case CLASS_C: + { + // Set the NodeAckRequested indicator to default + NodeAckRequested = false; + OnRxWindow2TimerEvent( ); + break; + } + } + break; + } + case MIB_NETWORK_JOINED: + { + IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined; + break; + } + case MIB_ADR: + { + AdrCtrlOn = mibSet->Param.AdrEnable; + break; + } + case MIB_NET_ID: + { + LoRaMacNetID = mibSet->Param.NetID; + break; + } + case MIB_DEV_ADDR: + { + LoRaMacDevAddr = mibSet->Param.DevAddr; + break; + } + case MIB_NWK_SKEY: + { + if( mibSet->Param.NwkSKey != NULL ) + { + memcpy1( LoRaMacNwkSKey, mibSet->Param.NwkSKey, + sizeof( LoRaMacNwkSKey ) ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_APP_SKEY: + { + if( mibSet->Param.AppSKey != NULL ) + { + memcpy1( LoRaMacAppSKey, mibSet->Param.AppSKey, + sizeof( LoRaMacAppSKey ) ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_PUBLIC_NETWORK: + { + PublicNetwork = mibSet->Param.EnablePublicNetwork; + Radio.SetPublicNetwork( PublicNetwork ); + break; + } + case MIB_REPEATER_SUPPORT: + { + RepeaterSupport = mibSet->Param.EnableRepeaterSupport; + break; + } + case MIB_RX2_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_RX_DR ) == true ) + { + LoRaMacParams.Rx2Channel = mibSet->Param.Rx2Channel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_RX_DR ) == true ) + { + LoRaMacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; + chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK; + + if( RegionChanMaskSet( LoRaMacRegion, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; + chanMaskSet.ChannelsMaskType = CHANNELS_MASK; + + if( RegionChanMaskSet( LoRaMacRegion, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_NB_REP: + { + if( ( mibSet->Param.ChannelNbRep >= 1 ) && + ( mibSet->Param.ChannelNbRep <= 15 ) ) + { + LoRaMacParams.ChannelsNbRep = mibSet->Param.ChannelNbRep; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + LoRaMacParams.MaxRxWindow = mibSet->Param.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + LoRaMacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + LoRaMacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + LoRaMacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + LoRaMacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_DEF_TX_DR ) == true ) + { + LoRaMacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true ) + { + LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsDefaultTxPower; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_DEF_TX_POWER ) == true ) + { + LoRaMacParamsDefaults.ChannelsTxPower = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsTxPower; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_POWER ) == true ) + { + LoRaMacParams.ChannelsTxPower = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_UPLINK_COUNTER: + { + UpLinkCounter = mibSet->Param.UpLinkCounter; + break; + } + case MIB_DOWNLINK_COUNTER: + { + DownLinkCounter = mibSet->Param.DownLinkCounter; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + LoRaMacParams.AntennaGain = mibSet->Param.AntennaGain; + break; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + + return status; +} + +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ) +{ + ChannelAddParams_t channelAdd; + + // Validate if the MAC is in a correct state + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + if( ( LoRaMacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + channelAdd.NewChannel = ¶ms; + channelAdd.ChannelId = id; + + return RegionChannelAdd( LoRaMacRegion, &channelAdd ); +} + +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ) +{ + ChannelRemoveParams_t channelRemove; + + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + if( ( LoRaMacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + channelRemove.ChannelId = id; + + if( RegionChannelsRemove( LoRaMacRegion, &channelRemove ) == false ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam ) +{ + if( channelParam == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + // Reset downlink counter + channelParam->DownLinkCounter = 0; + + if( MulticastChannels == NULL ) + { + // New node is the fist element + MulticastChannels = channelParam; + } + else + { + MulticastParams_t *cur = MulticastChannels; + + // Search the last node in the list + while( cur->Next != NULL ) + { + cur = cur->Next; + } + // This function always finds the last node + cur->Next = channelParam; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ) +{ + if( channelParam == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( MulticastChannels != NULL ) + { + if( MulticastChannels == channelParam ) + { + // First element + MulticastChannels = channelParam->Next; + } + else + { + MulticastParams_t *cur = MulticastChannels; + + // Search the node in the list + while( cur->Next && cur->Next != channelParam ) + { + cur = cur->Next; + } + // If we found the node, remove it + if( cur->Next ) + { + cur->Next = channelParam->Next; + } + } + channelParam->Next = NULL; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + AlternateDrParams_t altDr; + VerifyParams_t verify; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + if( mlmeRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) ); + + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + switch( mlmeRequest->Type ) + { + case MLME_JOIN: + { + if( ( LoRaMacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) + { + return LORAMAC_STATUS_BUSY; + } + + if( ( mlmeRequest->Req.Join.DevEui == NULL ) || + ( mlmeRequest->Req.Join.AppEui == NULL ) || + ( mlmeRequest->Req.Join.AppKey == NULL ) || + ( mlmeRequest->Req.Join.NbTrials == 0 ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Verify the parameter NbTrials for the join procedure + verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_NB_JOIN_TRIALS ) == false ) + { + // Value not supported, get default + getPhy.Attribute = PHY_DEF_NB_JOIN_TRIALS; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + mlmeRequest->Req.Join.NbTrials = ( uint8_t ) phyParam.Value; + } + + LoRaMacFlags.Bits.MlmeReq = 1; + MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + LoRaMacDevEui = mlmeRequest->Req.Join.DevEui; + LoRaMacAppEui = mlmeRequest->Req.Join.AppEui; + LoRaMacAppKey = mlmeRequest->Req.Join.AppKey; + MaxJoinRequestTrials = mlmeRequest->Req.Join.NbTrials; + + // Reset variable JoinRequestTrials + JoinRequestTrials = 0; + + // Setup header information + macHdr.Value = 0; + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + + ResetMacParameters( ); + + altDr.NbTrials = JoinRequestTrials + 1; + + LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); + + status = Send( &macHdr, 0, NULL, 0 ); + break; + } + case MLME_LINK_CHECK: + { + LoRaMacFlags.Bits.MlmeReq = 1; + // LoRaMac will send this command piggy-pack + MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + status = AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 ); + break; + } + case MLME_TXCW: + { + MlmeConfirm.MlmeRequest = mlmeRequest->Type; + LoRaMacFlags.Bits.MlmeReq = 1; + status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout ); + break; + } + case MLME_TXCW_1: + { + MlmeConfirm.MlmeRequest = mlmeRequest->Type; + LoRaMacFlags.Bits.MlmeReq = 1; + status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power ); + break; + } + default: + break; + } + + if( status != LORAMAC_STATUS_OK ) + { + NodeAckRequested = false; + LoRaMacFlags.Bits.MlmeReq = 0; + } + + return status; +} + +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + VerifyParams_t verify; + uint8_t fPort = 0; + void *fBuffer; + uint16_t fBufferSize; + int8_t datarate; + bool readyToSend = false; + + if( mcpsRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) || + ( ( LoRaMacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) ) + { + return LORAMAC_STATUS_BUSY; + } + + macHdr.Value = 0; + memset1 ( ( uint8_t* ) &McpsConfirm, 0, sizeof( McpsConfirm ) ); + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + switch( mcpsRequest->Type ) + { + case MCPS_UNCONFIRMED: + { + readyToSend = true; + AckTimeoutRetries = 1; + + macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; + fPort = mcpsRequest->Req.Unconfirmed.fPort; + fBuffer = mcpsRequest->Req.Unconfirmed.fBuffer; + fBufferSize = mcpsRequest->Req.Unconfirmed.fBufferSize; + datarate = mcpsRequest->Req.Unconfirmed.Datarate; + break; + } + case MCPS_CONFIRMED: + { + readyToSend = true; + AckTimeoutRetriesCounter = 1; + AckTimeoutRetries = mcpsRequest->Req.Confirmed.NbTrials; + + macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; + fPort = mcpsRequest->Req.Confirmed.fPort; + fBuffer = mcpsRequest->Req.Confirmed.fBuffer; + fBufferSize = mcpsRequest->Req.Confirmed.fBufferSize; + datarate = mcpsRequest->Req.Confirmed.Datarate; + break; + } + case MCPS_PROPRIETARY: + { + readyToSend = true; + AckTimeoutRetries = 1; + + macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY; + fBuffer = mcpsRequest->Req.Proprietary.fBuffer; + fBufferSize = mcpsRequest->Req.Proprietary.fBufferSize; + datarate = mcpsRequest->Req.Proprietary.Datarate; + break; + } + default: + break; + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); + // Apply the minimum possible datarate. + // Some regions have limitations for the minimum datarate. + datarate = MAX( datarate, phyParam.Value ); + + if( readyToSend == true ) + { + if( AdrCtrlOn == false ) + { + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true ) + { + LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + + status = Send( &macHdr, fPort, fBuffer, fBufferSize ); + if( status == LORAMAC_STATUS_OK ) + { + McpsConfirm.McpsRequest = mcpsRequest->Type; + LoRaMacFlags.Bits.McpsReq = 1; + } + else + { + NodeAckRequested = false; + } + } + + return status; +} + +void LoRaMacTestRxWindowsOn( bool enable ) +{ + IsRxWindowsEnabled = enable; +} + +void LoRaMacTestSetMic( uint16_t txPacketCounter ) +{ + UpLinkCounter = txPacketCounter; + IsUpLinkCounterFixed = true; +} + +void LoRaMacTestSetDutyCycleOn( bool enable ) +{ + VerifyParams_t verify; + + verify.DutyCycle = enable; + + if( RegionVerify( LoRaMacRegion, &verify, PHY_DUTY_CYCLE ) == true ) + { + DutyCycleOn = enable; + } +} + +void LoRaMacTestSetChannel( uint8_t channel ) +{ + Channel = channel; +} diff --git a/libraries/LoRa_Node/src/mac/LoRaMac.h b/libraries/LoRa_Node/src/mac/LoRaMac.h new file mode 100644 index 0000000..7efc1c4 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/LoRaMac.h @@ -0,0 +1,1947 @@ +/*! + * \file LoRaMac.h + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMAC LoRa MAC layer implementation + * This module specifies the API implementation of the LoRaMAC layer. + * This is a placeholder for a detailed description of the LoRaMac + * layer and the supported features. + * \{ + * + * \example classA/LoRaMote/main.c + * LoRaWAN class A application example for the LoRaMote. + * + * \example classB/LoRaMote/main.c + * LoRaWAN class B application example for the LoRaMote. + * + * \example classC/LoRaMote/main.c + * LoRaWAN class C application example for the LoRaMote. + */ +#ifndef __LORAMAC_H__ +#define __LORAMAC_H__ + +//modified------------------------------- +//#include "boards/arduino/rtc-board.h" //added this row +//--------------------------------------- + +/*! + * Beacon interval in ms + */ +#define BEACON_INTERVAL 128000 + +/*! + * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT in ms + */ +#define MAC_STATE_CHECK_TIMEOUT 1000 + +/*! + * Maximum number of times the MAC layer tries to get an acknowledge. + */ +#define MAX_ACK_RETRIES 8 + +/*! + * RSSI free threshold [dBm] + */ +#define RSSI_FREE_TH ( int8_t )( -90 ) + +/*! + * Frame direction definition for up-link communications + */ +#define UP_LINK 0 + +/*! + * Frame direction definition for down-link communications + */ +#define DOWN_LINK 1 + +/*! + * Sets the length of the LoRaMAC footer field. + * Mainly indicates the MIC field length + */ +#define LORAMAC_MFR_LEN 4 + +/*! + * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength + * in RxWindowSetup function. + * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD + */ +#define LORA_MAC_FRMPAYLOAD_OVERHEAD 13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4) + +/*! + * LoRaWAN devices classes definition + */ +typedef enum eDeviceClass +{ + /*! + * LoRaWAN device class A + * + * LoRaWAN Specification V1.0.1, chapter 3ff + */ + CLASS_A, + /*! + * LoRaWAN device class B + * + * LoRaWAN Specification V1.0.1, chapter 8ff + */ + CLASS_B, + /*! + * LoRaWAN device class C + * + * LoRaWAN Specification V1.0.1, chapter 17ff + */ + CLASS_C, +}DeviceClass_t; + +/*! + * LoRaMAC channels parameters definition + */ +typedef union uDrRange +{ + /*! + * Byte-access to the bits + */ + int8_t Value; + /*! + * Structure to store the minimum and the maximum datarate + */ + struct sFields + { + /*! + * Minimum data rate + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4] + */ + int8_t Min : 4; + /*! + * Maximum data rate + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4] + */ + int8_t Max : 4; + }Fields; +}DrRange_t; + +/*! + * LoRaMAC band parameters definition + */ +typedef struct sBand +{ + /*! + * Duty cycle + */ + uint16_t DCycle; + /*! + * Maximum Tx power + */ + int8_t TxMaxPower; + /*! + * Time stamp of the last Tx frame + */ + TimerTime_t LastTxDoneTime; + /*! + * Holds the time where the device is off + */ + TimerTime_t TimeOff; +}Band_t; + +/*! + * LoRaMAC channel definition + */ +typedef struct sChannelParams +{ + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Alternative frequency for RX window 1 + */ + uint32_t Rx1Frequency; + /*! + * Data rate definition + */ + DrRange_t DrRange; + /*! + * Band index + */ + uint8_t Band; +}ChannelParams_t; + +/*! + * LoRaMAC receive window 2 channel parameters + */ +typedef struct sRx2ChannelParams +{ + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Data rate + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_8, DR_9, DR_10, DR_11, DR_12, DR_13] + */ + uint8_t Datarate; +}Rx2ChannelParams_t; + +/*! + * Global MAC layer parameters + */ +typedef struct sLoRaMacParams +{ + /*! + * Channels TX power + */ + int8_t ChannelsTxPower; + /*! + * Channels data rate + */ + int8_t ChannelsDatarate; + /*! + * System overall timing error in milliseconds. + * [-SystemMaxRxError : +SystemMaxRxError] + * Default: +/-10 ms + */ + uint32_t SystemMaxRxError; + /*! + * Minimum required number of symbols to detect an Rx frame + * Default: 6 symbols + */ + uint8_t MinRxSymbols; + /*! + * LoRaMac maximum time a reception window stays open + */ + uint32_t MaxRxWindow; + /*! + * Receive delay 1 + */ + uint32_t ReceiveDelay1; + /*! + * Receive delay 2 + */ + uint32_t ReceiveDelay2; + /*! + * Join accept delay 1 + */ + uint32_t JoinAcceptDelay1; + /*! + * Join accept delay 1 + */ + uint32_t JoinAcceptDelay2; + /*! + * Number of uplink messages repetitions [1:15] (unconfirmed messages only) + */ + uint8_t ChannelsNbRep; + /*! + * Datarate offset between uplink and downlink on first window + */ + uint8_t Rx1DrOffset; + /*! + * LoRaMAC 2nd reception window settings + */ + Rx2ChannelParams_t Rx2Channel; + /*! + * Uplink dwell time configuration. 0: No limit, 1: 400ms + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time configuration. 0: No limit, 1: 400ms + */ + uint8_t DownlinkDwellTime; + /*! + * Maximum possible EIRP + */ + float MaxEirp; + /*! + * Antenna gain of the node + */ + float AntennaGain; +}LoRaMacParams_t; + +/*! + * LoRaMAC multicast channel parameter + */ +typedef struct sMulticastParams +{ + /*! + * Address + */ + uint32_t Address; + /*! + * Network session key + */ + uint8_t NwkSKey[16]; + /*! + * Application session key + */ + uint8_t AppSKey[16]; + /*! + * Downlink counter + */ + uint32_t DownLinkCounter; + /*! + * Reference pointer to the next multicast channel parameters in the list + */ + struct sMulticastParams *Next; +}MulticastParams_t; + +/*! + * LoRaMAC frame types + * + * LoRaWAN Specification V1.0.1, chapter 4.2.1, table 1 + */ +typedef enum eLoRaMacFrameType +{ + /*! + * LoRaMAC join request frame + */ + FRAME_TYPE_JOIN_REQ = 0x00, + /*! + * LoRaMAC join accept frame + */ + FRAME_TYPE_JOIN_ACCEPT = 0x01, + /*! + * LoRaMAC unconfirmed up-link frame + */ + FRAME_TYPE_DATA_UNCONFIRMED_UP = 0x02, + /*! + * LoRaMAC unconfirmed down-link frame + */ + FRAME_TYPE_DATA_UNCONFIRMED_DOWN = 0x03, + /*! + * LoRaMAC confirmed up-link frame + */ + FRAME_TYPE_DATA_CONFIRMED_UP = 0x04, + /*! + * LoRaMAC confirmed down-link frame + */ + FRAME_TYPE_DATA_CONFIRMED_DOWN = 0x05, + /*! + * LoRaMAC RFU frame + */ + FRAME_TYPE_RFU = 0x06, + /*! + * LoRaMAC proprietary frame + */ + FRAME_TYPE_PROPRIETARY = 0x07, +}LoRaMacFrameType_t; + +/*! + * LoRaMAC mote MAC commands + * + * LoRaWAN Specification V1.0.1, chapter 5, table 4 + */ +typedef enum eLoRaMacMoteCmd +{ + /*! + * LinkCheckReq + */ + MOTE_MAC_LINK_CHECK_REQ = 0x02, + /*! + * LinkADRAns + */ + MOTE_MAC_LINK_ADR_ANS = 0x03, + /*! + * DutyCycleAns + */ + MOTE_MAC_DUTY_CYCLE_ANS = 0x04, + /*! + * RXParamSetupAns + */ + MOTE_MAC_RX_PARAM_SETUP_ANS = 0x05, + /*! + * DevStatusAns + */ + MOTE_MAC_DEV_STATUS_ANS = 0x06, + /*! + * NewChannelAns + */ + MOTE_MAC_NEW_CHANNEL_ANS = 0x07, + /*! + * RXTimingSetupAns + */ + MOTE_MAC_RX_TIMING_SETUP_ANS = 0x08, + /*! + * TXParamSetupAns + */ + MOTE_MAC_TX_PARAM_SETUP_ANS = 0x09, + /*! + * DlChannelAns + */ + MOTE_MAC_DL_CHANNEL_ANS = 0x0A +}LoRaMacMoteCmd_t; + +/*! + * LoRaMAC server MAC commands + * + * LoRaWAN Specification V1.0.1 chapter 5, table 4 + */ +typedef enum eLoRaMacSrvCmd +{ + /*! + * LinkCheckAns + */ + SRV_MAC_LINK_CHECK_ANS = 0x02, + /*! + * LinkADRReq + */ + SRV_MAC_LINK_ADR_REQ = 0x03, + /*! + * DutyCycleReq + */ + SRV_MAC_DUTY_CYCLE_REQ = 0x04, + /*! + * RXParamSetupReq + */ + SRV_MAC_RX_PARAM_SETUP_REQ = 0x05, + /*! + * DevStatusReq + */ + SRV_MAC_DEV_STATUS_REQ = 0x06, + /*! + * NewChannelReq + */ + SRV_MAC_NEW_CHANNEL_REQ = 0x07, + /*! + * RXTimingSetupReq + */ + SRV_MAC_RX_TIMING_SETUP_REQ = 0x08, + /*! + * NewChannelReq + */ + SRV_MAC_TX_PARAM_SETUP_REQ = 0x09, + /*! + * DlChannelReq + */ + SRV_MAC_DL_CHANNEL_REQ = 0x0A, +}LoRaMacSrvCmd_t; + +/*! + * LoRaMAC Battery level indicator + */ +typedef enum eLoRaMacBatteryLevel +{ + /*! + * External power source + */ + BAT_LEVEL_EXT_SRC = 0x00, + /*! + * Battery level empty + */ + BAT_LEVEL_EMPTY = 0x01, + /*! + * Battery level full + */ + BAT_LEVEL_FULL = 0xFE, + /*! + * Battery level - no measurement available + */ + BAT_LEVEL_NO_MEASURE = 0xFF, +}LoRaMacBatteryLevel_t; + +/*! + * LoRaMAC header field definition (MHDR field) + * + * LoRaWAN Specification V1.0.1, chapter 4.2 + */ +typedef union uLoRaMacHeader +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to header bits + */ + struct sHdrBits + { + /*! + * Major version + */ + uint8_t Major : 2; + /*! + * RFU + */ + uint8_t RFU : 3; + /*! + * Message type + */ + uint8_t MType : 3; + }Bits; +}LoRaMacHeader_t; + +/*! + * LoRaMAC frame control field definition (FCtrl) + * + * LoRaWAN Specification V1.0.1, chapter 4.3.1 + */ +typedef union uLoRaMacFrameCtrl +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to bits + */ + struct sCtrlBits + { + /*! + * Frame options length + */ + uint8_t FOptsLen : 4; + /*! + * Frame pending bit + */ + uint8_t FPending : 1; + /*! + * Message acknowledge bit + */ + uint8_t Ack : 1; + /*! + * ADR acknowledgment request bit + */ + uint8_t AdrAckReq : 1; + /*! + * ADR control in frame header + */ + uint8_t Adr : 1; + }Bits; +}LoRaMacFrameCtrl_t; + +/*! + * Enumeration containing the status of the operation of a MAC service + */ +typedef enum eLoRaMacEventInfoStatus +{ + /*! + * Service performed successfully + */ + LORAMAC_EVENT_INFO_STATUS_OK = 0, + /*! + * An error occurred during the execution of the service + */ + LORAMAC_EVENT_INFO_STATUS_ERROR, + /*! + * A Tx timeout occurred + */ + LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT, + /*! + * An Rx timeout occurred on receive window 2 + */ + LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT, + /*! + * An Rx error occurred on receive window 1 + */ + LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, + /*! + * An Rx error occurred on receive window 2 + */ + LORAMAC_EVENT_INFO_STATUS_RX2_ERROR, + /*! + * An error occurred in the join procedure + */ + LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, + /*! + * A frame with an invalid downlink counter was received. The + * downlink counter of the frame was equal to the local copy + * of the downlink counter of the node. + */ + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED, + /*! + * The MAC could not retransmit a frame since the MAC decreased the datarate. The + * payload size is not applicable for the datarate. + */ + LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR, + /*! + * The node has lost MAX_FCNT_GAP or more frames. + */ + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS, + /*! + * An address error occurred + */ + LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL, + /*! + * message integrity check failure + */ + LORAMAC_EVENT_INFO_STATUS_MIC_FAIL, +}LoRaMacEventInfoStatus_t; + +/*! + * LoRaMac tx/rx operation state + */ +typedef union eLoRaMacFlags_t +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to bits + */ + struct sMacFlagBits + { + /*! + * MCPS-Req pending + */ + uint8_t McpsReq : 1; + /*! + * MCPS-Ind pending + */ + uint8_t McpsInd : 1; + /*! + * MCPS-Ind pending. Skip indication to the application layer + */ + uint8_t McpsIndSkip : 1; + /*! + * MLME-Req pending + */ + uint8_t MlmeReq : 1; + /*! + * MAC cycle done + */ + uint8_t MacDone : 1; + }Bits; +}LoRaMacFlags_t; + +/*! + * + * \brief LoRaMAC data services + * + * \details The following table list the primitives which are supported by the + * specific MAC data service: + * + * Name | Request | Indication | Response | Confirm + * --------------------- | :-----: | :--------: | :------: | :-----: + * \ref MCPS_UNCONFIRMED | YES | YES | NO | YES + * \ref MCPS_CONFIRMED | YES | YES | NO | YES + * \ref MCPS_MULTICAST | NO | YES | NO | NO + * \ref MCPS_PROPRIETARY | YES | YES | NO | YES + * + * The following table provides links to the function implementations of the + * related MCPS primitives: + * + * Primitive | Function + * ---------------- | :---------------------: + * MCPS-Request | \ref LoRaMacMlmeRequest + * MCPS-Confirm | MacMcpsConfirm in \ref LoRaMacPrimitives_t + * MCPS-Indication | MacMcpsIndication in \ref LoRaMacPrimitives_t + */ +typedef enum eMcps +{ + /*! + * Unconfirmed LoRaMAC frame + */ + MCPS_UNCONFIRMED, + /*! + * Confirmed LoRaMAC frame + */ + MCPS_CONFIRMED, + /*! + * Multicast LoRaMAC frame + */ + MCPS_MULTICAST, + /*! + * Proprietary frame + */ + MCPS_PROPRIETARY, +}Mcps_t; + +/*! + * LoRaMAC MCPS-Request for an unconfirmed frame + */ +typedef struct sMcpsReqUnconfirmed +{ + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application specific frame port values: [1...223] + * + * LoRaWAN Specification V1.0.1, chapter 4.3.2 + */ + uint8_t fPort; + /*! + * Pointer to the buffer of the frame payload + */ + void *fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; +}McpsReqUnconfirmed_t; + +/*! + * LoRaMAC MCPS-Request for a confirmed frame + */ +typedef struct sMcpsReqConfirmed +{ + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application specific frame port values: [1...223] + * + * LoRaWAN Specification V1.0.1, chapter 4.3.2 + */ + uint8_t fPort; + /*! + * Pointer to the buffer of the frame payload + */ + void *fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; + /*! + * Number of trials to transmit the frame, if the LoRaMAC layer did not + * receive an acknowledgment. The MAC performs a datarate adaptation, + * according to the LoRaWAN Specification V1.0.1, chapter 19.4, according + * to the following table: + * + * Transmission nb | Data Rate + * ----------------|----------- + * 1 (first) | DR + * 2 | DR + * 3 | max(DR-1,0) + * 4 | max(DR-1,0) + * 5 | max(DR-2,0) + * 6 | max(DR-2,0) + * 7 | max(DR-3,0) + * 8 | max(DR-3,0) + * + * Note, that if NbTrials is set to 1 or 2, the MAC will not decrease + * the datarate, in case the LoRaMAC layer did not receive an acknowledgment + */ + uint8_t NbTrials; +}McpsReqConfirmed_t; + +/*! + * LoRaMAC MCPS-Request for a proprietary frame + */ +typedef struct sMcpsReqProprietary +{ + /*! + * Pointer to the buffer of the frame payload + */ + void *fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; +}McpsReqProprietary_t; + +/*! + * LoRaMAC MCPS-Request structure + */ +typedef struct sMcpsReq +{ + /*! + * MCPS-Request type + */ + Mcps_t Type; + + /*! + * MCPS-Request parameters + */ + union uMcpsParam + { + /*! + * MCPS-Request parameters for an unconfirmed frame + */ + McpsReqUnconfirmed_t Unconfirmed; + /*! + * MCPS-Request parameters for a confirmed frame + */ + McpsReqConfirmed_t Confirmed; + /*! + * MCPS-Request parameters for a proprietary frame + */ + McpsReqProprietary_t Proprietary; + }Req; +}McpsReq_t; + +/*! + * LoRaMAC MCPS-Confirm + */ +typedef struct sMcpsConfirm +{ + /*! + * Holds the previously performed MCPS-Request + */ + Mcps_t McpsRequest; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Uplink datarate + */ + uint8_t Datarate; + /*! + * Transmission power + */ + int8_t TxPower; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * Provides the number of retransmissions + */ + uint8_t NbRetries; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * The uplink counter value related to the frame + */ + uint32_t UpLinkCounter; + /*! + * The uplink frequency related to the frame + */ + uint32_t UpLinkFrequency; +}McpsConfirm_t; + +/*! + * LoRaMAC MCPS-Indication primitive + */ +typedef struct sMcpsIndication +{ + /*! + * MCPS-Indication type + */ + Mcps_t McpsIndication; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Multicast + */ + uint8_t Multicast; + /*! + * Application port + */ + uint8_t Port; + /*! + * Downlink datarate + */ + uint8_t RxDatarate; + /*! + * Frame pending status + */ + uint8_t FramePending; + /*! + * Pointer to the received data stream + */ + uint8_t *Buffer; + /*! + * Size of the received data stream + */ + uint8_t BufferSize; + /*! + * Indicates, if data is available + */ + bool RxData; + /*! + * Rssi of the received packet + */ + int16_t Rssi; + /*! + * Snr of the received packet + */ + uint8_t Snr; + /*! + * Receive window + * + * [0: Rx window 1, 1: Rx window 2] + */ + uint8_t RxSlot; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * The downlink counter value for the received frame + */ + uint32_t DownLinkCounter; +}McpsIndication_t; + +/*! + * \brief LoRaMAC management services + * + * \details The following table list the primitives which are supported by the + * specific MAC management service: + * + * Name | Request | Indication | Response | Confirm + * --------------------- | :-----: | :--------: | :------: | :-----: + * \ref MLME_JOIN | YES | NO | NO | YES + * \ref MLME_LINK_CHECK | YES | NO | NO | YES + * \ref MLME_TXCW | YES | NO | NO | YES + * + * The following table provides links to the function implementations of the + * related MLME primitives. + * + * Primitive | Function + * ---------------- | :---------------------: + * MLME-Request | \ref LoRaMacMlmeRequest + * MLME-Confirm | MacMlmeConfirm in \ref LoRaMacPrimitives_t + */ +typedef enum eMlme +{ + /*! + * Initiates the Over-the-Air activation + * + * LoRaWAN Specification V1.0.1, chapter 6.2 + */ + MLME_JOIN, + /*! + * LinkCheckReq - Connectivity validation + * + * LoRaWAN Specification V1.0.1, chapter 5, table 4 + */ + MLME_LINK_CHECK, + /*! + * Sets Tx continuous wave mode + * + * LoRaWAN end-device certification + */ + MLME_TXCW, + /*! + * Sets Tx continuous wave mode (new LoRa-Alliance CC definition) + * + * LoRaWAN end-device certification + */ + MLME_TXCW_1, +}Mlme_t; + +/*! + * LoRaMAC MLME-Request for the join service + */ +typedef struct sMlmeReqJoin +{ + /*! + * Globally unique end-device identifier + * + * LoRaWAN Specification V1.0.1, chapter 6.2.1 + */ + uint8_t *DevEui; + /*! + * Application identifier + * + * LoRaWAN Specification V1.0.1, chapter 6.1.2 + */ + uint8_t *AppEui; + /*! + * AES-128 application key + * + * LoRaWAN Specification V1.0.1, chapter 6.2.2 + */ + uint8_t *AppKey; + /*! + * Number of trials for the join request. + */ + uint8_t NbTrials; +}MlmeReqJoin_t; + +/*! + * LoRaMAC MLME-Request for Tx continuous wave mode + */ +typedef struct sMlmeReqTxCw +{ + /*! + * Time in seconds while the radio is kept in continuous wave mode + */ + uint16_t Timeout; + /*! + * RF frequency to set (Only used with new way) + */ + uint32_t Frequency; + /*! + * RF output power to set (Only used with new way) + */ + uint8_t Power; +}MlmeReqTxCw_t; + +/*! + * LoRaMAC MLME-Request structure + */ +typedef struct sMlmeReq +{ + /*! + * MLME-Request type + */ + Mlme_t Type; + + /*! + * MLME-Request parameters + */ + union uMlmeParam + { + /*! + * MLME-Request parameters for a join request + */ + MlmeReqJoin_t Join; + /*! + * MLME-Request parameters for Tx continuous mode request + */ + MlmeReqTxCw_t TxCw; + }Req; +}MlmeReq_t; + +/*! + * LoRaMAC MLME-Confirm primitive + */ +typedef struct sMlmeConfirm +{ + /*! + * Holds the previously performed MLME-Request + */ + Mlme_t MlmeRequest; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * Demodulation margin. Contains the link margin [dB] of the last + * successfully received LinkCheckReq + */ + uint8_t DemodMargin; + /*! + * Number of gateways which received the last LinkCheckReq + */ + uint8_t NbGateways; + /*! + * Provides the number of retransmissions + */ + uint8_t NbRetries; +}MlmeConfirm_t; + +/*! + * LoRa Mac Information Base (MIB) + * + * The following table lists the MIB parameters and the related attributes: + * + * Attribute | Get | Set + * --------------------------------- | :-: | :-: + * \ref MIB_DEVICE_CLASS | YES | YES + * \ref MIB_NETWORK_JOINED | YES | YES + * \ref MIB_ADR | YES | YES + * \ref MIB_NET_ID | YES | YES + * \ref MIB_DEV_ADDR | YES | YES + * \ref MIB_NWK_SKEY | YES | YES + * \ref MIB_APP_SKEY | YES | YES + * \ref MIB_PUBLIC_NETWORK | YES | YES + * \ref MIB_REPEATER_SUPPORT | YES | YES + * \ref MIB_CHANNELS | YES | NO + * \ref MIB_RX2_CHANNEL | YES | YES + * \ref MIB_CHANNELS_MASK | YES | YES + * \ref MIB_CHANNELS_DEFAULT_MASK | YES | YES + * \ref MIB_CHANNELS_NB_REP | YES | YES + * \ref MIB_MAX_RX_WINDOW_DURATION | YES | YES + * \ref MIB_RECEIVE_DELAY_1 | YES | YES + * \ref MIB_RECEIVE_DELAY_2 | YES | YES + * \ref MIB_JOIN_ACCEPT_DELAY_1 | YES | YES + * \ref MIB_JOIN_ACCEPT_DELAY_2 | YES | YES + * \ref MIB_CHANNELS_DATARATE | YES | YES + * \ref MIB_CHANNELS_DEFAULT_DATARATE| YES | YES + * \ref MIB_CHANNELS_TX_POWER | YES | YES + * \ref MIB_CHANNELS_DEFAULT_TX_POWER| YES | YES + * \ref MIB_UPLINK_COUNTER | YES | YES + * \ref MIB_DOWNLINK_COUNTER | YES | YES + * \ref MIB_MULTICAST_CHANNEL | YES | NO + * \ref MIB_SYSTEM_MAX_RX_ERROR | YES | YES + * \ref MIB_MIN_RX_SYMBOLS | YES | YES + * \ref MIB_ANTENNA_GAIN | YES | YES + * + * The following table provides links to the function implementations of the + * related MIB primitives: + * + * Primitive | Function + * ---------------- | :---------------------: + * MIB-Set | \ref LoRaMacMibSetRequestConfirm + * MIB-Get | \ref LoRaMacMibGetRequestConfirm + */ +typedef enum eMib +{ + /*! + * LoRaWAN device class + * + * LoRaWAN Specification V1.0.1 + */ + MIB_DEVICE_CLASS, + /*! + * LoRaWAN Network joined attribute + * + * LoRaWAN Specification V1.0.1 + */ + MIB_NETWORK_JOINED, + /*! + * Adaptive data rate + * + * LoRaWAN Specification V1.0.1, chapter 4.3.1.1 + * + * [true: ADR enabled, false: ADR disabled] + */ + MIB_ADR, + /*! + * Network identifier + * + * LoRaWAN Specification V1.0.1, chapter 6.1.1 + */ + MIB_NET_ID, + /*! + * End-device address + * + * LoRaWAN Specification V1.0.1, chapter 6.1.1 + */ + MIB_DEV_ADDR, + /*! + * Network session key + * + * LoRaWAN Specification V1.0.1, chapter 6.1.3 + */ + MIB_NWK_SKEY, + /*! + * Application session key + * + * LoRaWAN Specification V1.0.1, chapter 6.1.4 + */ + MIB_APP_SKEY, + /*! + * Set the network type to public or private + * + * LoRaWAN Specification V1.0.1, chapter 7 + * + * [true: public network, false: private network] + */ + MIB_PUBLIC_NETWORK, + /*! + * Support the operation with repeaters + * + * LoRaWAN Specification V1.0.1, chapter 7 + * + * [true: repeater support enabled, false: repeater support disabled] + */ + MIB_REPEATER_SUPPORT, + /*! + * Communication channels. A get request will return a + * pointer which references the first entry of the channel list. The + * list is of size LORA_MAX_NB_CHANNELS + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_CHANNELS, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0.1, chapter 3.3.2 + */ + MIB_RX2_CHANNEL, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0.1, chapter 3.3.2 + */ + MIB_RX2_DEFAULT_CHANNEL, + /*! + * LoRaWAN channels mask + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_CHANNELS_MASK, + /*! + * LoRaWAN default channels mask + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_CHANNELS_DEFAULT_MASK, + /*! + * Set the number of repetitions on a channel + * + * LoRaWAN Specification V1.0.1, chapter 5.2 + */ + MIB_CHANNELS_NB_REP, + /*! + * Maximum receive window duration in [ms] + * + * LoRaWAN Specification V1.0.1, chapter 3.3.3 + */ + MIB_MAX_RX_WINDOW_DURATION, + /*! + * Receive delay 1 in [ms] + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_RECEIVE_DELAY_1, + /*! + * Receive delay 2 in [ms] + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_RECEIVE_DELAY_2, + /*! + * Join accept delay 1 in [ms] + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_JOIN_ACCEPT_DELAY_1, + /*! + * Join accept delay 2 in [ms] + * + * LoRaWAN Specification V1.0.1, chapter 7 + */ + MIB_JOIN_ACCEPT_DELAY_2, + /*! + * Default Data rate of a channel + * + * LoRaWAN Specification V1.0.1, chapter 7 + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13] + */ + MIB_CHANNELS_DEFAULT_DATARATE, + /*! + * Data rate of a channel + * + * LoRaWAN Specification V1.0.1, chapter 7 + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13] + */ + MIB_CHANNELS_DATARATE, + /*! + * Transmission power of a channel + * + * LoRaWAN Specification V1.0.1, chapter 7 + * + * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM, + * TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM] + * + * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM, + * TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM, + * TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM, + * TX_POWER_10_DBM] + */ + MIB_CHANNELS_TX_POWER, + /*! + * Transmission power of a channel + * + * LoRaWAN Specification V1.0.1, chapter 7 + * + * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM, + * TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM] + * + * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM, + * TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM, + * TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM, + * TX_POWER_10_DBM] + */ + MIB_CHANNELS_DEFAULT_TX_POWER, + /*! + * LoRaWAN Up-link counter + * + * LoRaWAN Specification V1.0.1, chapter 4.3.1.5 + */ + MIB_UPLINK_COUNTER, + /*! + * LoRaWAN Down-link counter + * + * LoRaWAN Specification V1.0.1, chapter 4.3.1.5 + */ + MIB_DOWNLINK_COUNTER, + /*! + * Multicast channels. A get request will return a pointer to the first + * entry of the multicast channel linked list. If the pointer is equal to + * NULL, the list is empty. + */ + MIB_MULTICAST_CHANNEL, + /*! + * System overall timing error in milliseconds. + * [-SystemMaxRxError : +SystemMaxRxError] + * Default: +/-10 ms + */ + MIB_SYSTEM_MAX_RX_ERROR, + /*! + * Minimum required number of symbols to detect an Rx frame + * Default: 6 symbols + */ + MIB_MIN_RX_SYMBOLS, + /*! + * Antenna gain of the node. Default value is region specific. + * The antenna gain is used to calculate the TX power of the node. + * The formula is: + * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) + */ + MIB_ANTENNA_GAIN +}Mib_t; + +/*! + * LoRaMAC MIB parameters + */ +typedef union uMibParam +{ + /*! + * LoRaWAN device class + * + * Related MIB type: \ref MIB_DEVICE_CLASS + */ + DeviceClass_t Class; + /*! + * LoRaWAN network joined attribute + * + * Related MIB type: \ref MIB_NETWORK_JOINED + */ + bool IsNetworkJoined; + /*! + * Activation state of ADR + * + * Related MIB type: \ref MIB_ADR + */ + bool AdrEnable; + /*! + * Network identifier + * + * Related MIB type: \ref MIB_NET_ID + */ + uint32_t NetID; + /*! + * End-device address + * + * Related MIB type: \ref MIB_DEV_ADDR + */ + uint32_t DevAddr; + /*! + * Network session key + * + * Related MIB type: \ref MIB_NWK_SKEY + */ + uint8_t *NwkSKey; + /*! + * Application session key + * + * Related MIB type: \ref MIB_APP_SKEY + */ + uint8_t *AppSKey; + /*! + * Enable or disable a public network + * + * Related MIB type: \ref MIB_PUBLIC_NETWORK + */ + bool EnablePublicNetwork; + /*! + * Enable or disable repeater support + * + * Related MIB type: \ref MIB_REPEATER_SUPPORT + */ + bool EnableRepeaterSupport; + /*! + * LoRaWAN Channel + * + * Related MIB type: \ref MIB_CHANNELS + */ + ChannelParams_t* ChannelList; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_CHANNEL + */ + Rx2ChannelParams_t Rx2Channel; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_DEFAULT_CHANNEL + */ + Rx2ChannelParams_t Rx2DefaultChannel; + /*! + * Channel mask + * + * Related MIB type: \ref MIB_CHANNELS_MASK + */ + uint16_t* ChannelsMask; + /*! + * Default channel mask + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_MASK + */ + uint16_t* ChannelsDefaultMask; + /*! + * Number of frame repetitions + * + * Related MIB type: \ref MIB_CHANNELS_NB_REP + */ + uint8_t ChannelNbRep; + /*! + * Maximum receive window duration + * + * Related MIB type: \ref MIB_MAX_RX_WINDOW_DURATION + */ + uint32_t MaxRxWindow; + /*! + * Receive delay 1 + * + * Related MIB type: \ref MIB_RECEIVE_DELAY_1 + */ + uint32_t ReceiveDelay1; + /*! + * Receive delay 2 + * + * Related MIB type: \ref MIB_RECEIVE_DELAY_2 + */ + uint32_t ReceiveDelay2; + /*! + * Join accept delay 1 + * + * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_1 + */ + uint32_t JoinAcceptDelay1; + /*! + * Join accept delay 2 + * + * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_2 + */ + uint32_t JoinAcceptDelay2; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE + */ + int8_t ChannelsDefaultDatarate; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DATARATE + */ + int8_t ChannelsDatarate; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_TX_POWER + */ + int8_t ChannelsDefaultTxPower; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_TX_POWER + */ + int8_t ChannelsTxPower; + /*! + * LoRaWAN Up-link counter + * + * Related MIB type: \ref MIB_UPLINK_COUNTER + */ + uint32_t UpLinkCounter; + /*! + * LoRaWAN Down-link counter + * + * Related MIB type: \ref MIB_DOWNLINK_COUNTER + */ + uint32_t DownLinkCounter; + /*! + * Multicast channel + * + * Related MIB type: \ref MIB_MULTICAST_CHANNEL + */ + MulticastParams_t* MulticastList; + /*! + * System overall timing error in milliseconds. + * + * Related MIB type: \ref MIB_SYSTEM_MAX_RX_ERROR + */ + uint32_t SystemMaxRxError; + /*! + * Minimum required number of symbols to detect an Rx frame + * + * Related MIB type: \ref MIB_MIN_RX_SYMBOLS + */ + uint8_t MinRxSymbols; + /*! + * Antenna gain + * + * Related MIB type: \ref MIB_ANTENNA_GAIN + */ + float AntennaGain; +}MibParam_t; + +/*! + * LoRaMAC MIB-RequestConfirm structure + */ +typedef struct eMibRequestConfirm +{ + /*! + * MIB-Request type + */ + Mib_t Type; + + /*! + * MLME-RequestConfirm parameters + */ + MibParam_t Param; +}MibRequestConfirm_t; + +/*! + * LoRaMAC tx information + */ +typedef struct sLoRaMacTxInfo +{ + /*! + * Defines the size of the applicative payload which can be processed + */ + uint8_t MaxPossiblePayload; + /*! + * The current payload size, dependent on the current datarate + */ + uint8_t CurrentPayloadSize; +}LoRaMacTxInfo_t; + +/*! + * LoRaMAC Status + */ +typedef enum eLoRaMacStatus +{ + /*! + * Service started successfully + */ + LORAMAC_STATUS_OK, + /*! + * Service not started - LoRaMAC is busy + */ + LORAMAC_STATUS_BUSY, + /*! + * Service unknown + */ + LORAMAC_STATUS_SERVICE_UNKNOWN, + /*! + * Service not started - invalid parameter + */ + LORAMAC_STATUS_PARAMETER_INVALID, + /*! + * Service not started - invalid frequency + */ + LORAMAC_STATUS_FREQUENCY_INVALID, + /*! + * Service not started - invalid datarate + */ + LORAMAC_STATUS_DATARATE_INVALID, + /*! + * Service not started - invalid frequency and datarate + */ + LORAMAC_STATUS_FREQ_AND_DR_INVALID, + /*! + * Service not started - the device is not in a LoRaWAN + */ + LORAMAC_STATUS_NO_NETWORK_JOINED, + /*! + * Service not started - payload length error + */ + LORAMAC_STATUS_LENGTH_ERROR, + /*! + * Service not started - the device is switched off + */ + LORAMAC_STATUS_DEVICE_OFF, + /*! + * Service not started - the specified region is not supported + * or not activated with preprocessor definitions. + */ + LORAMAC_STATUS_REGION_NOT_SUPPORTED +}LoRaMacStatus_t; + +/*! + * LoRaMAC region enumeration + */ +typedef enum eLoRaMacRegion_t +{ + /*! + * AS band on 923MHz + */ + LORAMAC_REGION_AS923, + /*! + * Australian band on 915MHz + */ + LORAMAC_REGION_AU915, + /*! + * Chinese band on 470MHz + */ + LORAMAC_REGION_CN470, + /*! + * Chinese band on 779MHz + */ + LORAMAC_REGION_CN779, + /*! + * European band on 433MHz + */ + LORAMAC_REGION_EU433, + /*! + * European band on 868MHz + */ + LORAMAC_REGION_EU868, + /*! + * South korean band on 920MHz + */ + LORAMAC_REGION_KR920, + /*! + * India band on 865MHz + */ + LORAMAC_REGION_IN865, + /*! + * North american band on 915MHz + */ + LORAMAC_REGION_US915, + /*! + * North american band on 915MHz with a maximum of 16 channels + */ + LORAMAC_REGION_US915_HYBRID, +}LoRaMacRegion_t; + +/*! + * LoRaMAC events structure + * Used to notify upper layers of MAC events + */ +typedef struct sLoRaMacPrimitives +{ + /*! + * \brief MCPS-Confirm primitive + * + * \param [OUT] MCPS-Confirm parameters + */ + void ( *MacMcpsConfirm )( McpsConfirm_t *McpsConfirm ); + /*! + * \brief MCPS-Indication primitive + * + * \param [OUT] MCPS-Indication parameters + */ + void ( *MacMcpsIndication )( McpsIndication_t *McpsIndication ); + /*! + * \brief MLME-Confirm primitive + * + * \param [OUT] MLME-Confirm parameters + */ + void ( *MacMlmeConfirm )( MlmeConfirm_t *MlmeConfirm ); +}LoRaMacPrimitives_t; + +/*! + * LoRaMAC callback structure + */ +typedef struct sLoRaMacCallback +{ + /*! + * \brief Measures the battery level + * + * \retval Battery level [0: node is connected to an external + * power source, 1..254: battery level, where 1 is the minimum + * and 254 is the maximum value, 255: the node was not able + * to measure the battery level] + */ + uint8_t ( *GetBatteryLevel )( void ); +}LoRaMacCallback_t; + +/*! + * LoRaMAC Max EIRP (dBm) table + */ +static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 }; + + + +/*! + * \brief LoRaMAC layer initialization + * + * \details In addition to the initialization of the LoRaMAC layer, this + * function initializes the callback primitives of the MCPS and + * MLME services. Every data field of \ref LoRaMacPrimitives_t must be + * set to a valid callback function. + * + * \param [IN] primitives - Pointer to a structure defining the LoRaMAC + * event functions. Refer to \ref LoRaMacPrimitives_t. + * + * \param [IN] events - Pointer to a structure defining the LoRaMAC + * callback functions. Refer to \ref LoRaMacCallback_t. + * + * \param [IN] region - The region to start. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_REGION_NOT_SUPPORTED. + */ +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks, LoRaMacRegion_t region, uint8_t dr, uint8_t txPower ); + +/*! + * \brief Queries the LoRaMAC if it is possible to send the next frame with + * a given payload size. The LoRaMAC takes scheduled MAC commands into + * account and reports, when the frame can be send or not. + * + * \param [IN] size - Size of applicative payload to be send next + * + * \param [OUT] txInfo - The structure \ref LoRaMacTxInfo_t contains + * information about the actual maximum payload possible + * ( according to the configured datarate or the next + * datarate according to ADR ), and the maximum frame + * size, taking the scheduled MAC commands into account. + * + * \retval LoRaMacStatus_t Status of the operation. When the parameters are + * not valid, the function returns \ref LORAMAC_STATUS_PARAMETER_INVALID. + * In case of a length error caused by the applicative payload size, the + * function returns LORAMAC_STATUS_LENGTH_ERROR. In case of a length error + * due to additional MAC commands in the queue, the function returns + * LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR. In case the query is valid, and + * the LoRaMAC is able to send the frame, the function returns LORAMAC_STATUS_OK. * + */ +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ); + +/*! + * \brief LoRaMAC channel add service + * + * \details Adds a new channel to the channel list and activates the id in + * the channel mask. For the US915 band, all channels are enabled + * by default. It is not possible to activate less than 6 125 kHz + * channels. + * + * \param [IN] id - Id of the channel. Possible values are: + * + * 0-15 for EU868 + * 0-72 for US915 + * + * \param [IN] params - Channel parameters to set. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ); + +/*! + * \brief LoRaMAC channel remove service + * + * \details Deactivates the id in the channel mask. + * + * \param [IN] id - Id of the channel. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ); + +/*! + * \brief LoRaMAC multicast channel link service + * + * \details Links a multicast channel into the linked list. + * + * \param [IN] channelParam - Multicast channel parameters to link. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam ); + +/*! + * \brief LoRaMAC multicast channel unlink service + * + * \details Unlinks a multicast channel from the linked list. + * + * \param [IN] channelParam - Multicast channel parameters to unlink. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ); + +/*! + * \brief LoRaMAC MIB-Get + * + * \details The mac information base service to get attributes of the LoRaMac + * layer. + * + * The following code-snippet shows how to use the API to get the + * parameter AdrEnable, defined by the enumeration type + * \ref MIB_ADR. + * \code + * MibRequestConfirm_t mibReq; + * mibReq.Type = MIB_ADR; + * + * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + * { + * // LoRaMAC updated the parameter mibParam.AdrEnable + * } + * \endcode + * + * \param [IN] mibRequest - MIB-GET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ); + +/*! + * \brief LoRaMAC MIB-Set + * + * \details The mac information base service to set attributes of the LoRaMac + * layer. + * + * The following code-snippet shows how to use the API to set the + * parameter AdrEnable, defined by the enumeration type + * \ref MIB_ADR. + * + * \code + * MibRequestConfirm_t mibReq; + * mibReq.Type = MIB_ADR; + * mibReq.Param.AdrEnable = true; + * + * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + * { + * // LoRaMAC updated the parameter + * } + * \endcode + * + * \param [IN] mibRequest - MIB-SET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ); + +/*! + * \brief LoRaMAC MLME-Request + * + * \details The Mac layer management entity handles management services. The + * following code-snippet shows how to use the API to perform a + * network join request. + * + * \code + * static uint8_t DevEui[] = + * { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * static uint8_t AppEui[] = + * { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * static uint8_t AppKey[] = + * { + * 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + * 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C + * }; + * + * MlmeReq_t mlmeReq; + * mlmeReq.Type = MLME_JOIN; + * mlmeReq.Req.Join.DevEui = DevEui; + * mlmeReq.Req.Join.AppEui = AppEui; + * mlmeReq.Req.Join.AppKey = AppKey; + * + * if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) + * { + * // Service started successfully. Waiting for the Mlme-Confirm event + * } + * \endcode + * + * \param [IN] mlmeRequest - MLME-Request to perform. Refer to \ref MlmeReq_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_NO_NETWORK_JOINED, + * \ref LORAMAC_STATUS_LENGTH_ERROR, + * \ref LORAMAC_STATUS_DEVICE_OFF. + */ +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ); + +/*! + * \brief LoRaMAC MCPS-Request + * + * \details The Mac Common Part Sublayer handles data services. The following + * code-snippet shows how to use the API to send an unconfirmed + * LoRaMAC frame. + * + * \code + * uint8_t myBuffer[] = { 1, 2, 3 }; + * + * McpsReq_t mcpsReq; + * mcpsReq.Type = MCPS_UNCONFIRMED; + * mcpsReq.Req.Unconfirmed.fPort = 1; + * mcpsReq.Req.Unconfirmed.fBuffer = myBuffer; + * mcpsReq.Req.Unconfirmed.fBufferSize = sizeof( myBuffer ); + * + * if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) + * { + * // Service started successfully. Waiting for the MCPS-Confirm event + * } + * \endcode + * + * \param [IN] mcpsRequest - MCPS-Request to perform. Refer to \ref McpsReq_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_NO_NETWORK_JOINED, + * \ref LORAMAC_STATUS_LENGTH_ERROR, + * \ref LORAMAC_STATUS_DEVICE_OFF. + */ +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ); + +/*! \} defgroup LORAMAC */ + +#endif // __LORAMAC_H__ diff --git a/libraries/LoRa_Node/src/mac/LoRaMacCrypto.c b/libraries/LoRa_Node/src/mac/LoRaMacCrypto.c new file mode 100644 index 0000000..f55a391 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/LoRaMacCrypto.c @@ -0,0 +1,209 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE ) +*/ +#include +#include +//modified------------------ +// #include "utilities.h" +#include "boards/mcu/arduino/utilities.h" + + +// #include "aes.h" +// #include "cmac.h" +#include "system/crypto/aes.h" +#include "system/crypto/cmac.h" +//--------------------------- + + +#include "LoRaMacCrypto.h" + +/*! + * CMAC/AES Message Integrity Code (MIC) Block B0 size + */ +#define LORAMAC_MIC_BLOCK_B0_SIZE 16 + +/*! + * MIC field computation initial data + */ +static uint8_t MicBlockB0[] = { 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +/*! + * Contains the computed MIC field. + * + * \remark Only the 4 first bytes are used + */ +static uint8_t Mic[16]; + +/*! + * Encryption aBlock and sBlock + */ +static uint8_t aBlock[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; +static uint8_t sBlock[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +/*! + * AES computation context variable + */ +static aes_context AesContext; + +/*! + * CMAC computation context variable + */ +static AES_CMAC_CTX AesCmacCtx[1]; + +/*! + * \brief Computes the LoRaMAC frame MIC field + * + * \param [IN] buffer Data buffer + * \param [IN] size Data buffer size + * \param [IN] key AES key to be used + * \param [IN] address Frame address + * \param [IN] dir Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter Frame sequence counter + * \param [OUT] mic Computed MIC field + */ +void LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic ) +{ + MicBlockB0[5] = dir; + + MicBlockB0[6] = ( address ) & 0xFF; + MicBlockB0[7] = ( address >> 8 ) & 0xFF; + MicBlockB0[8] = ( address >> 16 ) & 0xFF; + MicBlockB0[9] = ( address >> 24 ) & 0xFF; + + MicBlockB0[10] = ( sequenceCounter ) & 0xFF; + MicBlockB0[11] = ( sequenceCounter >> 8 ) & 0xFF; + MicBlockB0[12] = ( sequenceCounter >> 16 ) & 0xFF; + MicBlockB0[13] = ( sequenceCounter >> 24 ) & 0xFF; + + MicBlockB0[15] = size & 0xFF; + + AES_CMAC_Init( AesCmacCtx ); + + AES_CMAC_SetKey( AesCmacCtx, key ); + + AES_CMAC_Update( AesCmacCtx, MicBlockB0, LORAMAC_MIC_BLOCK_B0_SIZE ); + + AES_CMAC_Update( AesCmacCtx, buffer, size & 0xFF ); + + AES_CMAC_Final( Mic, AesCmacCtx ); + + *mic = ( uint32_t )( ( uint32_t )Mic[3] << 24 | ( uint32_t )Mic[2] << 16 | ( uint32_t )Mic[1] << 8 | ( uint32_t )Mic[0] ); +} + +void LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer ) +{ + uint16_t i; + uint8_t bufferIndex = 0; + uint16_t ctr = 1; + + memset1( AesContext.ksch, '\0', 240 ); + aes_set_key( key, 16, &AesContext ); + + aBlock[5] = dir; + + aBlock[6] = ( address ) & 0xFF; + aBlock[7] = ( address >> 8 ) & 0xFF; + aBlock[8] = ( address >> 16 ) & 0xFF; + aBlock[9] = ( address >> 24 ) & 0xFF; + + aBlock[10] = ( sequenceCounter ) & 0xFF; + aBlock[11] = ( sequenceCounter >> 8 ) & 0xFF; + aBlock[12] = ( sequenceCounter >> 16 ) & 0xFF; + aBlock[13] = ( sequenceCounter >> 24 ) & 0xFF; + + while( size >= 16 ) + { + aBlock[15] = ( ( ctr ) & 0xFF ); + ctr++; + aes_encrypt( aBlock, sBlock, &AesContext ); + for( i = 0; i < 16; i++ ) + { + encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + } + size -= 16; + bufferIndex += 16; + } + + if( size > 0 ) + { + aBlock[15] = ( ( ctr ) & 0xFF ); + aes_encrypt( aBlock, sBlock, &AesContext ); + for( i = 0; i < size; i++ ) + { + encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + } + } +} + +void LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer ) +{ + LoRaMacPayloadEncrypt( buffer, size, key, address, dir, sequenceCounter, decBuffer ); +} + +void LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic ) +{ + AES_CMAC_Init( AesCmacCtx ); + + AES_CMAC_SetKey( AesCmacCtx, key ); + + AES_CMAC_Update( AesCmacCtx, buffer, size & 0xFF ); + + AES_CMAC_Final( Mic, AesCmacCtx ); + + *mic = ( uint32_t )( ( uint32_t )Mic[3] << 24 | ( uint32_t )Mic[2] << 16 | ( uint32_t )Mic[1] << 8 | ( uint32_t )Mic[0] ); +} + +void LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer ) +{ + memset1( AesContext.ksch, '\0', 240 ); + aes_set_key( key, 16, &AesContext ); + aes_encrypt( buffer, decBuffer, &AesContext ); + // Check if optional CFList is included + if( size >= 16 ) + { + aes_encrypt( buffer + 16, decBuffer + 16, &AesContext ); + } +} + +void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey ) +{ + uint8_t nonce[16]; + uint8_t *pDevNonce = ( uint8_t * )&devNonce; + + memset1( AesContext.ksch, '\0', 240 ); + aes_set_key( key, 16, &AesContext ); + + memset1( nonce, 0, sizeof( nonce ) ); + nonce[0] = 0x01; + memcpy1( nonce + 1, appNonce, 6 ); + memcpy1( nonce + 7, pDevNonce, 2 ); + aes_encrypt( nonce, nwkSKey, &AesContext ); + + memset1( nonce, 0, sizeof( nonce ) ); + nonce[0] = 0x02; + memcpy1( nonce + 1, appNonce, 6 ); + memcpy1( nonce + 7, pDevNonce, 2 ); + aes_encrypt( nonce, appSKey, &AesContext ); +} diff --git a/libraries/LoRa_Node/src/mac/LoRaMacCrypto.h b/libraries/LoRa_Node/src/mac/LoRaMacCrypto.h new file mode 100644 index 0000000..d2399d5 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/LoRaMacCrypto.h @@ -0,0 +1,111 @@ +/*! + * \file LoRaMacCrypto.h + * + * \brief LoRa MAC layer cryptography implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jäckle ( STACKFORCE ) + * + * \defgroup LORAMAC_CRYPTO LoRa MAC layer cryptography implementation + * This module covers the implementation of cryptographic functions + * of the LoRaMAC layer. + * \{ + */ +#ifndef __LORAMAC_CRYPTO_H__ +#define __LORAMAC_CRYPTO_H__ + +/*! + * Computes the LoRaMAC frame MIC field + * + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [IN] address - Frame address + * \param [IN] dir - Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter - Frame sequence counter + * \param [OUT] mic - Computed MIC field + */ +void LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic ); + +/*! + * Computes the LoRaMAC payload encryption + * + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [IN] address - Frame address + * \param [IN] dir - Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter - Frame sequence counter + * \param [OUT] encBuffer - Encrypted buffer + */ +void LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer ); + +/*! + * Computes the LoRaMAC payload decryption + * + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [IN] address - Frame address + * \param [IN] dir - Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter - Frame sequence counter + * \param [OUT] decBuffer - Decrypted buffer + */ +void LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer ); + +/*! + * Computes the LoRaMAC Join Request frame MIC field + * + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [OUT] mic - Computed MIC field + */ +void LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic ); + +/*! + * Computes the LoRaMAC join frame decryption + * + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [OUT] decBuffer - Decrypted buffer + */ +void LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer ); + +/*! + * Computes the LoRaMAC join frame decryption + * + * \param [IN] key - AES key to be used + * \param [IN] appNonce - Application nonce + * \param [IN] devNonce - Device nonce + * \param [OUT] nwkSKey - Network session key + * \param [OUT] appSKey - Application session key + */ +void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey ); + +/*! \} defgroup LORAMAC */ + +#endif // __LORAMAC_CRYPTO_H__ diff --git a/libraries/LoRa_Node/src/mac/LoRaMacTest.h b/libraries/LoRa_Node/src/mac/LoRaMacTest.h new file mode 100644 index 0000000..86f18bf --- /dev/null +++ b/libraries/LoRa_Node/src/mac/LoRaMacTest.h @@ -0,0 +1,81 @@ +/*! + * \file LoRaMacTest.h + * + * \brief LoRa MAC layer test function implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jäckle ( STACKFORCE ) + * + * \defgroup LORAMACTEST LoRa MAC layer test function implementation + * This module specifies the API implementation of test function of the LoRaMAC layer. + * The functions in this file are only for testing purposes only. + * \{ + */ +#ifndef __LORAMACTEST_H__ +#define __LORAMACTEST_H__ + +/*! + * \brief Enabled or disables the reception windows + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] enable - Enabled or disables the reception windows + */ +void LoRaMacTestRxWindowsOn( bool enable ); + +/*! + * \brief Enables the MIC field test + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] txPacketCounter - Fixed Tx packet counter value + */ +void LoRaMacTestSetMic( uint16_t txPacketCounter ); + +/*! + * \brief Enabled or disables the duty cycle + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] enable - Enabled or disables the duty cycle + */ +void LoRaMacTestSetDutyCycleOn( bool enable ); + +/*! + * \brief Sets the channel index + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] channel - Channel index + */ +void LoRaMacTestSetChannel( uint8_t channel ); + +/*! \} defgroup LORAMACTEST */ + +#endif // __LORAMACTEST_H__ diff --git a/libraries/LoRa_Node/src/mac/region/Region.c b/libraries/LoRa_Node/src/mac/region/Region.c new file mode 100644 index 0000000..dc6f146 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/Region.c @@ -0,0 +1,1041 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include + +//modified---------------------- +//#include "timer.h" +// #include "LoRaMac.h" // included in Region.h +#include "system/timer.h" +#include "config.h" +//------------------------------ + + + +// Regional includes +#include "Region.h" + + + +// Setup regions +#ifdef REGION_AS923 +#include "RegionAS923.h" +#define AS923_CASE case LORAMAC_REGION_AS923: +#define AS923_IS_ACTIVE( ) AS923_CASE { return true; } +#define AS923_GET_PHY_PARAM( ) AS923_CASE { return RegionAS923GetPhyParam( getPhy ); } +#define AS923_SET_BAND_TX_DONE( ) AS923_CASE { RegionAS923SetBandTxDone( txDone ); break; } +#define AS923_INIT_DEFAULTS( ) AS923_CASE { RegionAS923InitDefaults( type ); break; } +#define AS923_VERIFY( ) AS923_CASE { return RegionAS923Verify( verify, phyAttribute ); } +#define AS923_APPLY_CF_LIST( ) AS923_CASE { RegionAS923ApplyCFList( applyCFList ); break; } +#define AS923_CHAN_MASK_SET( ) AS923_CASE { return RegionAS923ChanMaskSet( chanMaskSet ); } +#define AS923_ADR_NEXT( ) AS923_CASE { return RegionAS923AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) AS923_CASE { RegionAS923ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define AS923_RX_CONFIG( ) AS923_CASE { return RegionAS923RxConfig( rxConfig, datarate ); } +#define AS923_TX_CONFIG( ) AS923_CASE { return RegionAS923TxConfig( txConfig, txPower, txTimeOnAir ); } +#define AS923_LINK_ADR_REQ( ) AS923_CASE { return RegionAS923LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define AS923_RX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923RxParamSetupReq( rxParamSetupReq ); } +#define AS923_NEW_CHANNEL_REQ( ) AS923_CASE { return RegionAS923NewChannelReq( newChannelReq ); } +#define AS923_TX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923TxParamSetupReq( txParamSetupReq ); } +#define AS923_DL_CHANNEL_REQ( ) AS923_CASE { return RegionAS923DlChannelReq( dlChannelReq ); } +#define AS923_ALTERNATE_DR( ) AS923_CASE { return RegionAS923AlternateDr( alternateDr ); } +#define AS923_CALC_BACKOFF( ) AS923_CASE { RegionAS923CalcBackOff( calcBackOff ); break; } +#define AS923_NEXT_CHANNEL( ) AS923_CASE { return RegionAS923NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define AS923_CHANNEL_ADD( ) AS923_CASE { return RegionAS923ChannelAdd( channelAdd ); } +#define AS923_CHANNEL_REMOVE( ) AS923_CASE { return RegionAS923ChannelsRemove( channelRemove ); } +#define AS923_SET_CONTINUOUS_WAVE( ) AS923_CASE { RegionAS923SetContinuousWave( continuousWave ); break; } +#define AS923_APPLY_DR_OFFSET( ) AS923_CASE { return RegionAS923ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define AS923_IS_ACTIVE( ) +#define AS923_GET_PHY_PARAM( ) +#define AS923_SET_BAND_TX_DONE( ) +#define AS923_INIT_DEFAULTS( ) +#define AS923_VERIFY( ) +#define AS923_APPLY_CF_LIST( ) +#define AS923_CHAN_MASK_SET( ) +#define AS923_ADR_NEXT( ) +#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define AS923_RX_CONFIG( ) +#define AS923_TX_CONFIG( ) +#define AS923_LINK_ADR_REQ( ) +#define AS923_RX_PARAM_SETUP_REQ( ) +#define AS923_NEW_CHANNEL_REQ( ) +#define AS923_TX_PARAM_SETUP_REQ( ) +#define AS923_DL_CHANNEL_REQ( ) +#define AS923_ALTERNATE_DR( ) +#define AS923_CALC_BACKOFF( ) +#define AS923_NEXT_CHANNEL( ) +#define AS923_CHANNEL_ADD( ) +#define AS923_CHANNEL_REMOVE( ) +#define AS923_SET_CONTINUOUS_WAVE( ) +#define AS923_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_AU915 +#include "RegionAU915.h" +#define AU915_CASE case LORAMAC_REGION_AU915: +#define AU915_IS_ACTIVE( ) AU915_CASE { return true; } +#define AU915_GET_PHY_PARAM( ) AU915_CASE { return RegionAU915GetPhyParam( getPhy ); } +#define AU915_SET_BAND_TX_DONE( ) AU915_CASE { RegionAU915SetBandTxDone( txDone ); break; } +#define AU915_INIT_DEFAULTS( ) AU915_CASE { RegionAU915InitDefaults( type ); break; } +#define AU915_VERIFY( ) AU915_CASE { return RegionAU915Verify( verify, phyAttribute ); } +#define AU915_APPLY_CF_LIST( ) AU915_CASE { RegionAU915ApplyCFList( applyCFList ); break; } +#define AU915_CHAN_MASK_SET( ) AU915_CASE { return RegionAU915ChanMaskSet( chanMaskSet ); } +#define AU915_ADR_NEXT( ) AU915_CASE { return RegionAU915AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) AU915_CASE { RegionAU915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define AU915_RX_CONFIG( ) AU915_CASE { return RegionAU915RxConfig( rxConfig, datarate ); } +#define AU915_TX_CONFIG( ) AU915_CASE { return RegionAU915TxConfig( txConfig, txPower, txTimeOnAir ); } +#define AU915_LINK_ADR_REQ( ) AU915_CASE { return RegionAU915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define AU915_RX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915RxParamSetupReq( rxParamSetupReq ); } +#define AU915_NEW_CHANNEL_REQ( ) AU915_CASE { return RegionAU915NewChannelReq( newChannelReq ); } +#define AU915_TX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915TxParamSetupReq( txParamSetupReq ); } +#define AU915_DL_CHANNEL_REQ( ) AU915_CASE { return RegionAU915DlChannelReq( dlChannelReq ); } +#define AU915_ALTERNATE_DR( ) AU915_CASE { return RegionAU915AlternateDr( alternateDr ); } +#define AU915_CALC_BACKOFF( ) AU915_CASE { RegionAU915CalcBackOff( calcBackOff ); break; } +#define AU915_NEXT_CHANNEL( ) AU915_CASE { return RegionAU915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define AU915_CHANNEL_ADD( ) AU915_CASE { return RegionAU915ChannelAdd( channelAdd ); } +#define AU915_CHANNEL_REMOVE( ) AU915_CASE { return RegionAU915ChannelsRemove( channelRemove ); } +#define AU915_SET_CONTINUOUS_WAVE( ) AU915_CASE { RegionAU915SetContinuousWave( continuousWave ); break; } +#define AU915_APPLY_DR_OFFSET( ) AU915_CASE { return RegionAU915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define AU915_IS_ACTIVE( ) +#define AU915_GET_PHY_PARAM( ) +#define AU915_SET_BAND_TX_DONE( ) +#define AU915_INIT_DEFAULTS( ) +#define AU915_VERIFY( ) +#define AU915_APPLY_CF_LIST( ) +#define AU915_CHAN_MASK_SET( ) +#define AU915_ADR_NEXT( ) +#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define AU915_RX_CONFIG( ) +#define AU915_TX_CONFIG( ) +#define AU915_LINK_ADR_REQ( ) +#define AU915_RX_PARAM_SETUP_REQ( ) +#define AU915_NEW_CHANNEL_REQ( ) +#define AU915_TX_PARAM_SETUP_REQ( ) +#define AU915_DL_CHANNEL_REQ( ) +#define AU915_ALTERNATE_DR( ) +#define AU915_CALC_BACKOFF( ) +#define AU915_NEXT_CHANNEL( ) +#define AU915_CHANNEL_ADD( ) +#define AU915_CHANNEL_REMOVE( ) +#define AU915_SET_CONTINUOUS_WAVE( ) +#define AU915_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_CN470 +#include "RegionCN470.h" +#define CN470_CASE case LORAMAC_REGION_CN470: +#define CN470_IS_ACTIVE( ) CN470_CASE { return true; } +#define CN470_GET_PHY_PARAM( ) CN470_CASE { return RegionCN470GetPhyParam( getPhy ); } +#define CN470_SET_BAND_TX_DONE( ) CN470_CASE { RegionCN470SetBandTxDone( txDone ); break; } +#define CN470_INIT_DEFAULTS( ) CN470_CASE { RegionCN470InitDefaults( type ); break; } +#define CN470_VERIFY( ) CN470_CASE { return RegionCN470Verify( verify, phyAttribute ); } +#define CN470_APPLY_CF_LIST( ) CN470_CASE { RegionCN470ApplyCFList( applyCFList ); break; } +#define CN470_CHAN_MASK_SET( ) CN470_CASE { return RegionCN470ChanMaskSet( chanMaskSet ); } +#define CN470_ADR_NEXT( ) CN470_CASE { return RegionCN470AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) CN470_CASE { RegionCN470ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN470_RX_CONFIG( ) CN470_CASE { return RegionCN470RxConfig( rxConfig, datarate ); } +#define CN470_TX_CONFIG( ) CN470_CASE { return RegionCN470TxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN470_LINK_ADR_REQ( ) CN470_CASE { return RegionCN470LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN470_RX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470RxParamSetupReq( rxParamSetupReq ); } +#define CN470_NEW_CHANNEL_REQ( ) CN470_CASE { return RegionCN470NewChannelReq( newChannelReq ); } +#define CN470_TX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470TxParamSetupReq( txParamSetupReq ); } +#define CN470_DL_CHANNEL_REQ( ) CN470_CASE { return RegionCN470DlChannelReq( dlChannelReq ); } +#define CN470_ALTERNATE_DR( ) CN470_CASE { return RegionCN470AlternateDr( alternateDr ); } +#define CN470_CALC_BACKOFF( ) CN470_CASE { RegionCN470CalcBackOff( calcBackOff ); break; } +#define CN470_NEXT_CHANNEL( ) CN470_CASE { return RegionCN470NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN470_CHANNEL_ADD( ) CN470_CASE { return RegionCN470ChannelAdd( channelAdd ); } +#define CN470_CHANNEL_REMOVE( ) CN470_CASE { return RegionCN470ChannelsRemove( channelRemove ); } +#define CN470_SET_CONTINUOUS_WAVE( ) CN470_CASE { RegionCN470SetContinuousWave( continuousWave ); break; } +#define CN470_APPLY_DR_OFFSET( ) CN470_CASE { return RegionCN470ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define CN470_IS_ACTIVE( ) +#define CN470_GET_PHY_PARAM( ) +#define CN470_SET_BAND_TX_DONE( ) +#define CN470_INIT_DEFAULTS( ) +#define CN470_VERIFY( ) +#define CN470_APPLY_CF_LIST( ) +#define CN470_CHAN_MASK_SET( ) +#define CN470_ADR_NEXT( ) +#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN470_RX_CONFIG( ) +#define CN470_TX_CONFIG( ) +#define CN470_LINK_ADR_REQ( ) +#define CN470_RX_PARAM_SETUP_REQ( ) +#define CN470_NEW_CHANNEL_REQ( ) +#define CN470_TX_PARAM_SETUP_REQ( ) +#define CN470_DL_CHANNEL_REQ( ) +#define CN470_ALTERNATE_DR( ) +#define CN470_CALC_BACKOFF( ) +#define CN470_NEXT_CHANNEL( ) +#define CN470_CHANNEL_ADD( ) +#define CN470_CHANNEL_REMOVE( ) +#define CN470_SET_CONTINUOUS_WAVE( ) +#define CN470_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_CN779 +#include "RegionCN779.h" +#define CN779_CASE case LORAMAC_REGION_CN779: +#define CN779_IS_ACTIVE( ) CN779_CASE { return true; } +#define CN779_GET_PHY_PARAM( ) CN779_CASE { return RegionCN779GetPhyParam( getPhy ); } +#define CN779_SET_BAND_TX_DONE( ) CN779_CASE { RegionCN779SetBandTxDone( txDone ); break; } +#define CN779_INIT_DEFAULTS( ) CN779_CASE { RegionCN779InitDefaults( type ); break; } +#define CN779_VERIFY( ) CN779_CASE { return RegionCN779Verify( verify, phyAttribute ); } +#define CN779_APPLY_CF_LIST( ) CN779_CASE { RegionCN779ApplyCFList( applyCFList ); break; } +#define CN779_CHAN_MASK_SET( ) CN779_CASE { return RegionCN779ChanMaskSet( chanMaskSet ); } +#define CN779_ADR_NEXT( ) CN779_CASE { return RegionCN779AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) CN779_CASE { RegionCN779ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN779_RX_CONFIG( ) CN779_CASE { return RegionCN779RxConfig( rxConfig, datarate ); } +#define CN779_TX_CONFIG( ) CN779_CASE { return RegionCN779TxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN779_LINK_ADR_REQ( ) CN779_CASE { return RegionCN779LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN779_RX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779RxParamSetupReq( rxParamSetupReq ); } +#define CN779_NEW_CHANNEL_REQ( ) CN779_CASE { return RegionCN779NewChannelReq( newChannelReq ); } +#define CN779_TX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779TxParamSetupReq( txParamSetupReq ); } +#define CN779_DL_CHANNEL_REQ( ) CN779_CASE { return RegionCN779DlChannelReq( dlChannelReq ); } +#define CN779_ALTERNATE_DR( ) CN779_CASE { return RegionCN779AlternateDr( alternateDr ); } +#define CN779_CALC_BACKOFF( ) CN779_CASE { RegionCN779CalcBackOff( calcBackOff ); break; } +#define CN779_NEXT_CHANNEL( ) CN779_CASE { return RegionCN779NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN779_CHANNEL_ADD( ) CN779_CASE { return RegionCN779ChannelAdd( channelAdd ); } +#define CN779_CHANNEL_REMOVE( ) CN779_CASE { return RegionCN779ChannelsRemove( channelRemove ); } +#define CN779_SET_CONTINUOUS_WAVE( ) CN779_CASE { RegionCN779SetContinuousWave( continuousWave ); break; } +#define CN779_APPLY_DR_OFFSET( ) CN779_CASE { return RegionCN779ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define CN779_IS_ACTIVE( ) +#define CN779_GET_PHY_PARAM( ) +#define CN779_SET_BAND_TX_DONE( ) +#define CN779_INIT_DEFAULTS( ) +#define CN779_VERIFY( ) +#define CN779_APPLY_CF_LIST( ) +#define CN779_CHAN_MASK_SET( ) +#define CN779_ADR_NEXT( ) +#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN779_RX_CONFIG( ) +#define CN779_TX_CONFIG( ) +#define CN779_LINK_ADR_REQ( ) +#define CN779_RX_PARAM_SETUP_REQ( ) +#define CN779_NEW_CHANNEL_REQ( ) +#define CN779_TX_PARAM_SETUP_REQ( ) +#define CN779_DL_CHANNEL_REQ( ) +#define CN779_ALTERNATE_DR( ) +#define CN779_CALC_BACKOFF( ) +#define CN779_NEXT_CHANNEL( ) +#define CN779_CHANNEL_ADD( ) +#define CN779_CHANNEL_REMOVE( ) +#define CN779_SET_CONTINUOUS_WAVE( ) +#define CN779_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_EU433 +#include "RegionEU433.h" +#define EU433_CASE case LORAMAC_REGION_EU433: +#define EU433_IS_ACTIVE( ) EU433_CASE { return true; } +#define EU433_GET_PHY_PARAM( ) EU433_CASE { return RegionEU433GetPhyParam( getPhy ); } +#define EU433_SET_BAND_TX_DONE( ) EU433_CASE { RegionEU433SetBandTxDone( txDone ); break; } +#define EU433_INIT_DEFAULTS( ) EU433_CASE { RegionEU433InitDefaults( type ); break; } +#define EU433_VERIFY( ) EU433_CASE { return RegionEU433Verify( verify, phyAttribute ); } +#define EU433_APPLY_CF_LIST( ) EU433_CASE { RegionEU433ApplyCFList( applyCFList ); break; } +#define EU433_CHAN_MASK_SET( ) EU433_CASE { return RegionEU433ChanMaskSet( chanMaskSet ); } +#define EU433_ADR_NEXT( ) EU433_CASE { return RegionEU433AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) EU433_CASE { RegionEU433ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define EU433_RX_CONFIG( ) EU433_CASE { return RegionEU433RxConfig( rxConfig, datarate ); } +#define EU433_TX_CONFIG( ) EU433_CASE { return RegionEU433TxConfig( txConfig, txPower, txTimeOnAir ); } +#define EU433_LINK_ADR_REQ( ) EU433_CASE { return RegionEU433LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define EU433_RX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433RxParamSetupReq( rxParamSetupReq ); } +#define EU433_NEW_CHANNEL_REQ( ) EU433_CASE { return RegionEU433NewChannelReq( newChannelReq ); } +#define EU433_TX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433TxParamSetupReq( txParamSetupReq ); } +#define EU433_DL_CHANNEL_REQ( ) EU433_CASE { return RegionEU433DlChannelReq( dlChannelReq ); } +#define EU433_ALTERNATE_DR( ) EU433_CASE { return RegionEU433AlternateDr( alternateDr ); } +#define EU433_CALC_BACKOFF( ) EU433_CASE { RegionEU433CalcBackOff( calcBackOff ); break; } +#define EU433_NEXT_CHANNEL( ) EU433_CASE { return RegionEU433NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define EU433_CHANNEL_ADD( ) EU433_CASE { return RegionEU433ChannelAdd( channelAdd ); } +#define EU433_CHANNEL_REMOVE( ) EU433_CASE { return RegionEU433ChannelsRemove( channelRemove ); } +#define EU433_SET_CONTINUOUS_WAVE( ) EU433_CASE { RegionEU433SetContinuousWave( continuousWave ); break; } +#define EU433_APPLY_DR_OFFSET( ) EU433_CASE { return RegionEU433ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define EU433_IS_ACTIVE( ) +#define EU433_GET_PHY_PARAM( ) +#define EU433_SET_BAND_TX_DONE( ) +#define EU433_INIT_DEFAULTS( ) +#define EU433_VERIFY( ) +#define EU433_APPLY_CF_LIST( ) +#define EU433_CHAN_MASK_SET( ) +#define EU433_ADR_NEXT( ) +#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define EU433_RX_CONFIG( ) +#define EU433_TX_CONFIG( ) +#define EU433_LINK_ADR_REQ( ) +#define EU433_RX_PARAM_SETUP_REQ( ) +#define EU433_NEW_CHANNEL_REQ( ) +#define EU433_TX_PARAM_SETUP_REQ( ) +#define EU433_DL_CHANNEL_REQ( ) +#define EU433_ALTERNATE_DR( ) +#define EU433_CALC_BACKOFF( ) +#define EU433_NEXT_CHANNEL( ) +#define EU433_CHANNEL_ADD( ) +#define EU433_CHANNEL_REMOVE( ) +#define EU433_SET_CONTINUOUS_WAVE( ) +#define EU433_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_EU868 +#include "RegionEU868.h" +#define EU868_CASE case LORAMAC_REGION_EU868: +#define EU868_IS_ACTIVE( ) EU868_CASE { return true; } +#define EU868_GET_PHY_PARAM( ) EU868_CASE { return RegionEU868GetPhyParam( getPhy ); } +#define EU868_SET_BAND_TX_DONE( ) EU868_CASE { RegionEU868SetBandTxDone( txDone ); break; } +#define EU868_INIT_DEFAULTS( ) EU868_CASE { RegionEU868InitDefaults( type ); break; } +#define EU868_VERIFY( ) EU868_CASE { return RegionEU868Verify( verify, phyAttribute ); } +#define EU868_APPLY_CF_LIST( ) EU868_CASE { RegionEU868ApplyCFList( applyCFList ); break; } +#define EU868_CHAN_MASK_SET( ) EU868_CASE { return RegionEU868ChanMaskSet( chanMaskSet ); } +#define EU868_ADR_NEXT( ) EU868_CASE { return RegionEU868AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) EU868_CASE { RegionEU868ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define EU868_RX_CONFIG( ) EU868_CASE { return RegionEU868RxConfig( rxConfig, datarate ); } +#define EU868_TX_CONFIG( ) EU868_CASE { return RegionEU868TxConfig( txConfig, txPower, txTimeOnAir ); } +#define EU868_LINK_ADR_REQ( ) EU868_CASE { return RegionEU868LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define EU868_RX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868RxParamSetupReq( rxParamSetupReq ); } +#define EU868_NEW_CHANNEL_REQ( ) EU868_CASE { return RegionEU868NewChannelReq( newChannelReq ); } +#define EU868_TX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868TxParamSetupReq( txParamSetupReq ); } +#define EU868_DL_CHANNEL_REQ( ) EU868_CASE { return RegionEU868DlChannelReq( dlChannelReq ); } +#define EU868_ALTERNATE_DR( ) EU868_CASE { return RegionEU868AlternateDr( alternateDr ); } +#define EU868_CALC_BACKOFF( ) EU868_CASE { RegionEU868CalcBackOff( calcBackOff ); break; } +#define EU868_NEXT_CHANNEL( ) EU868_CASE { return RegionEU868NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define EU868_CHANNEL_ADD( ) EU868_CASE { return RegionEU868ChannelAdd( channelAdd ); } +#define EU868_CHANNEL_REMOVE( ) EU868_CASE { return RegionEU868ChannelsRemove( channelRemove ); } +#define EU868_SET_CONTINUOUS_WAVE( ) EU868_CASE { RegionEU868SetContinuousWave( continuousWave ); break; } +#define EU868_APPLY_DR_OFFSET( ) EU868_CASE { return RegionEU868ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define EU868_IS_ACTIVE( ) +#define EU868_GET_PHY_PARAM( ) +#define EU868_SET_BAND_TX_DONE( ) +#define EU868_INIT_DEFAULTS( ) +#define EU868_VERIFY( ) +#define EU868_APPLY_CF_LIST( ) +#define EU868_CHAN_MASK_SET( ) +#define EU868_ADR_NEXT( ) +#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define EU868_RX_CONFIG( ) +#define EU868_TX_CONFIG( ) +#define EU868_LINK_ADR_REQ( ) +#define EU868_RX_PARAM_SETUP_REQ( ) +#define EU868_NEW_CHANNEL_REQ( ) +#define EU868_TX_PARAM_SETUP_REQ( ) +#define EU868_DL_CHANNEL_REQ( ) +#define EU868_ALTERNATE_DR( ) +#define EU868_CALC_BACKOFF( ) +#define EU868_NEXT_CHANNEL( ) +#define EU868_CHANNEL_ADD( ) +#define EU868_CHANNEL_REMOVE( ) +#define EU868_SET_CONTINUOUS_WAVE( ) +#define EU868_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_KR920 +#include "RegionKR920.h" +#define KR920_CASE case LORAMAC_REGION_KR920: +#define KR920_IS_ACTIVE( ) KR920_CASE { return true; } +#define KR920_GET_PHY_PARAM( ) KR920_CASE { return RegionKR920GetPhyParam( getPhy ); } +#define KR920_SET_BAND_TX_DONE( ) KR920_CASE { RegionKR920SetBandTxDone( txDone ); break; } +#define KR920_INIT_DEFAULTS( ) KR920_CASE { RegionKR920InitDefaults( type ); break; } +#define KR920_VERIFY( ) KR920_CASE { return RegionKR920Verify( verify, phyAttribute ); } +#define KR920_APPLY_CF_LIST( ) KR920_CASE { RegionKR920ApplyCFList( applyCFList ); break; } +#define KR920_CHAN_MASK_SET( ) KR920_CASE { return RegionKR920ChanMaskSet( chanMaskSet ); } +#define KR920_ADR_NEXT( ) KR920_CASE { return RegionKR920AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) KR920_CASE { RegionKR920ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define KR920_RX_CONFIG( ) KR920_CASE { return RegionKR920RxConfig( rxConfig, datarate ); } +#define KR920_TX_CONFIG( ) KR920_CASE { return RegionKR920TxConfig( txConfig, txPower, txTimeOnAir ); } +#define KR920_LINK_ADR_REQ( ) KR920_CASE { return RegionKR920LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define KR920_RX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920RxParamSetupReq( rxParamSetupReq ); } +#define KR920_NEW_CHANNEL_REQ( ) KR920_CASE { return RegionKR920NewChannelReq( newChannelReq ); } +#define KR920_TX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920TxParamSetupReq( txParamSetupReq ); } +#define KR920_DL_CHANNEL_REQ( ) KR920_CASE { return RegionKR920DlChannelReq( dlChannelReq ); } +#define KR920_ALTERNATE_DR( ) KR920_CASE { return RegionKR920AlternateDr( alternateDr ); } +#define KR920_CALC_BACKOFF( ) KR920_CASE { RegionKR920CalcBackOff( calcBackOff ); break; } +#define KR920_NEXT_CHANNEL( ) KR920_CASE { return RegionKR920NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define KR920_CHANNEL_ADD( ) KR920_CASE { return RegionKR920ChannelAdd( channelAdd ); } +#define KR920_CHANNEL_REMOVE( ) KR920_CASE { return RegionKR920ChannelsRemove( channelRemove ); } +#define KR920_SET_CONTINUOUS_WAVE( ) KR920_CASE { RegionKR920SetContinuousWave( continuousWave ); break; } +#define KR920_APPLY_DR_OFFSET( ) KR920_CASE { return RegionKR920ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define KR920_IS_ACTIVE( ) +#define KR920_GET_PHY_PARAM( ) +#define KR920_SET_BAND_TX_DONE( ) +#define KR920_INIT_DEFAULTS( ) +#define KR920_VERIFY( ) +#define KR920_APPLY_CF_LIST( ) +#define KR920_CHAN_MASK_SET( ) +#define KR920_ADR_NEXT( ) +#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define KR920_RX_CONFIG( ) +#define KR920_TX_CONFIG( ) +#define KR920_LINK_ADR_REQ( ) +#define KR920_RX_PARAM_SETUP_REQ( ) +#define KR920_NEW_CHANNEL_REQ( ) +#define KR920_TX_PARAM_SETUP_REQ( ) +#define KR920_DL_CHANNEL_REQ( ) +#define KR920_ALTERNATE_DR( ) +#define KR920_CALC_BACKOFF( ) +#define KR920_NEXT_CHANNEL( ) +#define KR920_CHANNEL_ADD( ) +#define KR920_CHANNEL_REMOVE( ) +#define KR920_SET_CONTINUOUS_WAVE( ) +#define KR920_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_IN865 +#include "RegionIN865.h" +#define IN865_CASE case LORAMAC_REGION_IN865: +#define IN865_IS_ACTIVE( ) IN865_CASE { return true; } +#define IN865_GET_PHY_PARAM( ) IN865_CASE { return RegionIN865GetPhyParam( getPhy ); } +#define IN865_SET_BAND_TX_DONE( ) IN865_CASE { RegionIN865SetBandTxDone( txDone ); break; } +#define IN865_INIT_DEFAULTS( ) IN865_CASE { RegionIN865InitDefaults( type ); break; } +#define IN865_VERIFY( ) IN865_CASE { return RegionIN865Verify( verify, phyAttribute ); } +#define IN865_APPLY_CF_LIST( ) IN865_CASE { RegionIN865ApplyCFList( applyCFList ); break; } +#define IN865_CHAN_MASK_SET( ) IN865_CASE { return RegionIN865ChanMaskSet( chanMaskSet ); } +#define IN865_ADR_NEXT( ) IN865_CASE { return RegionIN865AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) IN865_CASE { RegionIN865ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define IN865_RX_CONFIG( ) IN865_CASE { return RegionIN865RxConfig( rxConfig, datarate ); } +#define IN865_TX_CONFIG( ) IN865_CASE { return RegionIN865TxConfig( txConfig, txPower, txTimeOnAir ); } +#define IN865_LINK_ADR_REQ( ) IN865_CASE { return RegionIN865LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define IN865_RX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865RxParamSetupReq( rxParamSetupReq ); } +#define IN865_NEW_CHANNEL_REQ( ) IN865_CASE { return RegionIN865NewChannelReq( newChannelReq ); } +#define IN865_TX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865TxParamSetupReq( txParamSetupReq ); } +#define IN865_DL_CHANNEL_REQ( ) IN865_CASE { return RegionIN865DlChannelReq( dlChannelReq ); } +#define IN865_ALTERNATE_DR( ) IN865_CASE { return RegionIN865AlternateDr( alternateDr ); } +#define IN865_CALC_BACKOFF( ) IN865_CASE { RegionIN865CalcBackOff( calcBackOff ); break; } +#define IN865_NEXT_CHANNEL( ) IN865_CASE { return RegionIN865NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define IN865_CHANNEL_ADD( ) IN865_CASE { return RegionIN865ChannelAdd( channelAdd ); } +#define IN865_CHANNEL_REMOVE( ) IN865_CASE { return RegionIN865ChannelsRemove( channelRemove ); } +#define IN865_SET_CONTINUOUS_WAVE( ) IN865_CASE { RegionIN865SetContinuousWave( continuousWave ); break; } +#define IN865_APPLY_DR_OFFSET( ) IN865_CASE { return RegionIN865ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define IN865_IS_ACTIVE( ) +#define IN865_GET_PHY_PARAM( ) +#define IN865_SET_BAND_TX_DONE( ) +#define IN865_INIT_DEFAULTS( ) +#define IN865_VERIFY( ) +#define IN865_APPLY_CF_LIST( ) +#define IN865_CHAN_MASK_SET( ) +#define IN865_ADR_NEXT( ) +#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define IN865_RX_CONFIG( ) +#define IN865_TX_CONFIG( ) +#define IN865_LINK_ADR_REQ( ) +#define IN865_RX_PARAM_SETUP_REQ( ) +#define IN865_NEW_CHANNEL_REQ( ) +#define IN865_TX_PARAM_SETUP_REQ( ) +#define IN865_DL_CHANNEL_REQ( ) +#define IN865_ALTERNATE_DR( ) +#define IN865_CALC_BACKOFF( ) +#define IN865_NEXT_CHANNEL( ) +#define IN865_CHANNEL_ADD( ) +#define IN865_CHANNEL_REMOVE( ) +#define IN865_SET_CONTINUOUS_WAVE( ) +#define IN865_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_US915 +#include "RegionUS915.h" +#define US915_CASE case LORAMAC_REGION_US915: +#define US915_IS_ACTIVE( ) US915_CASE { return true; } +#define US915_GET_PHY_PARAM( ) US915_CASE { return RegionUS915GetPhyParam( getPhy ); } +#define US915_SET_BAND_TX_DONE( ) US915_CASE { RegionUS915SetBandTxDone( txDone ); break; } +#define US915_INIT_DEFAULTS( ) US915_CASE { RegionUS915InitDefaults( type ); break; } +#define US915_VERIFY( ) US915_CASE { return RegionUS915Verify( verify, phyAttribute ); } +#define US915_APPLY_CF_LIST( ) US915_CASE { RegionUS915ApplyCFList( applyCFList ); break; } +#define US915_CHAN_MASK_SET( ) US915_CASE { return RegionUS915ChanMaskSet( chanMaskSet ); } +#define US915_ADR_NEXT( ) US915_CASE { return RegionUS915AdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) US915_CASE { RegionUS915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define US915_RX_CONFIG( ) US915_CASE { return RegionUS915RxConfig( rxConfig, datarate ); } +#define US915_TX_CONFIG( ) US915_CASE { return RegionUS915TxConfig( txConfig, txPower, txTimeOnAir ); } +#define US915_LINK_ADR_REQ( ) US915_CASE { return RegionUS915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define US915_RX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915RxParamSetupReq( rxParamSetupReq ); } +#define US915_NEW_CHANNEL_REQ( ) US915_CASE { return RegionUS915NewChannelReq( newChannelReq ); } +#define US915_TX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915TxParamSetupReq( txParamSetupReq ); } +#define US915_DL_CHANNEL_REQ( ) US915_CASE { return RegionUS915DlChannelReq( dlChannelReq ); } +#define US915_ALTERNATE_DR( ) US915_CASE { return RegionUS915AlternateDr( alternateDr ); } +#define US915_CALC_BACKOFF( ) US915_CASE { RegionUS915CalcBackOff( calcBackOff ); break; } +#define US915_NEXT_CHANNEL( ) US915_CASE { return RegionUS915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define US915_CHANNEL_ADD( ) US915_CASE { return RegionUS915ChannelAdd( channelAdd ); } +#define US915_CHANNEL_REMOVE( ) US915_CASE { return RegionUS915ChannelsRemove( channelRemove ); } +#define US915_SET_CONTINUOUS_WAVE( ) US915_CASE { RegionUS915SetContinuousWave( continuousWave ); break; } +#define US915_APPLY_DR_OFFSET( ) US915_CASE { return RegionUS915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define US915_IS_ACTIVE( ) +#define US915_GET_PHY_PARAM( ) +#define US915_SET_BAND_TX_DONE( ) +#define US915_INIT_DEFAULTS( ) +#define US915_VERIFY( ) +#define US915_APPLY_CF_LIST( ) +#define US915_CHAN_MASK_SET( ) +#define US915_ADR_NEXT( ) +#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define US915_RX_CONFIG( ) +#define US915_TX_CONFIG( ) +#define US915_LINK_ADR_REQ( ) +#define US915_RX_PARAM_SETUP_REQ( ) +#define US915_NEW_CHANNEL_REQ( ) +#define US915_TX_PARAM_SETUP_REQ( ) +#define US915_DL_CHANNEL_REQ( ) +#define US915_ALTERNATE_DR( ) +#define US915_CALC_BACKOFF( ) +#define US915_NEXT_CHANNEL( ) +#define US915_CHANNEL_ADD( ) +#define US915_CHANNEL_REMOVE( ) +#define US915_SET_CONTINUOUS_WAVE( ) +#define US915_APPLY_DR_OFFSET( ) +#endif + +#ifdef REGION_US915_HYBRID +#include "RegionUS915-Hybrid.h" +#define US915_HYBRID_CASE case LORAMAC_REGION_US915_HYBRID: +#define US915_HYBRID_IS_ACTIVE( ) US915_HYBRID_CASE { return true; } +#define US915_HYBRID_GET_PHY_PARAM( ) US915_HYBRID_CASE { return RegionUS915HybridGetPhyParam( getPhy ); } +#define US915_HYBRID_SET_BAND_TX_DONE( ) US915_HYBRID_CASE { RegionUS915HybridSetBandTxDone( txDone ); break; } +#define US915_HYBRID_INIT_DEFAULTS( ) US915_HYBRID_CASE { RegionUS915HybridInitDefaults( type ); break; } +#define US915_HYBRID_VERIFY( ) US915_HYBRID_CASE { return RegionUS915HybridVerify( verify, phyAttribute ); } +#define US915_HYBRID_APPLY_CF_LIST( ) US915_HYBRID_CASE { RegionUS915HybridApplyCFList( applyCFList ); break; } +#define US915_HYBRID_CHAN_MASK_SET( ) US915_HYBRID_CASE { return RegionUS915HybridChanMaskSet( chanMaskSet ); } +#define US915_HYBRID_ADR_NEXT( ) US915_HYBRID_CASE { return RegionUS915HybridAdrNext( adrNext, drOut, txPowOut, adrAckCounter ); } +#define US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( ) US915_HYBRID_CASE { RegionUS915HybridComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define US915_HYBRID_RX_CONFIG( ) US915_HYBRID_CASE { return RegionUS915HybridRxConfig( rxConfig, datarate ); } +#define US915_HYBRID_TX_CONFIG( ) US915_HYBRID_CASE { return RegionUS915HybridTxConfig( txConfig, txPower, txTimeOnAir ); } +#define US915_HYBRID_LINK_ADR_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridLinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define US915_HYBRID_RX_PARAM_SETUP_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridRxParamSetupReq( rxParamSetupReq ); } +#define US915_HYBRID_NEW_CHANNEL_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridNewChannelReq( newChannelReq ); } +#define US915_HYBRID_TX_PARAM_SETUP_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridTxParamSetupReq( txParamSetupReq ); } +#define US915_HYBRID_DL_CHANNEL_REQ( ) US915_HYBRID_CASE { return RegionUS915HybridDlChannelReq( dlChannelReq ); } +#define US915_HYBRID_ALTERNATE_DR( ) US915_HYBRID_CASE { return RegionUS915HybridAlternateDr( alternateDr ); } +#define US915_HYBRID_CALC_BACKOFF( ) US915_HYBRID_CASE { RegionUS915HybridCalcBackOff( calcBackOff ); break; } +#define US915_HYBRID_NEXT_CHANNEL( ) US915_HYBRID_CASE { return RegionUS915HybridNextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define US915_HYBRID_CHANNEL_ADD( ) US915_HYBRID_CASE { return RegionUS915HybridChannelAdd( channelAdd ); } +#define US915_HYBRID_CHANNEL_REMOVE( ) US915_HYBRID_CASE { return RegionUS915HybridChannelsRemove( channelRemove ); } +#define US915_HYBRID_SET_CONTINUOUS_WAVE( ) US915_HYBRID_CASE { RegionUS915HybridSetContinuousWave( continuousWave ); break; } +#define US915_HYBRID_APPLY_DR_OFFSET( ) US915_HYBRID_CASE { return RegionUS915HybridApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#else +#define US915_HYBRID_IS_ACTIVE( ) +#define US915_HYBRID_GET_PHY_PARAM( ) +#define US915_HYBRID_SET_BAND_TX_DONE( ) +#define US915_HYBRID_INIT_DEFAULTS( ) +#define US915_HYBRID_VERIFY( ) +#define US915_HYBRID_APPLY_CF_LIST( ) +#define US915_HYBRID_CHAN_MASK_SET( ) +#define US915_HYBRID_ADR_NEXT( ) +#define US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define US915_HYBRID_RX_CONFIG( ) +#define US915_HYBRID_TX_CONFIG( ) +#define US915_HYBRID_LINK_ADR_REQ( ) +#define US915_HYBRID_RX_PARAM_SETUP_REQ( ) +#define US915_HYBRID_NEW_CHANNEL_REQ( ) +#define US915_HYBRID_TX_PARAM_SETUP_REQ( ) +#define US915_HYBRID_DL_CHANNEL_REQ( ) +#define US915_HYBRID_ALTERNATE_DR( ) +#define US915_HYBRID_CALC_BACKOFF( ) +#define US915_HYBRID_NEXT_CHANNEL( ) +#define US915_HYBRID_CHANNEL_ADD( ) +#define US915_HYBRID_CHANNEL_REMOVE( ) +#define US915_HYBRID_SET_CONTINUOUS_WAVE( ) +#define US915_HYBRID_APPLY_DR_OFFSET( ) +#endif + +bool RegionIsActive( LoRaMacRegion_t region ) +{ + switch( region ) + { + AS923_IS_ACTIVE( ); + AU915_IS_ACTIVE( ); + CN470_IS_ACTIVE( ); + CN779_IS_ACTIVE( ); + EU433_IS_ACTIVE( ); + EU868_IS_ACTIVE( ); + KR920_IS_ACTIVE( ); + IN865_IS_ACTIVE( ); + US915_IS_ACTIVE( ); + US915_HYBRID_IS_ACTIVE( ); + default: + { + return false; + } + } +} + +PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + switch( region ) + { + AS923_GET_PHY_PARAM( ); + AU915_GET_PHY_PARAM( ); + CN470_GET_PHY_PARAM( ); + CN779_GET_PHY_PARAM( ); + EU433_GET_PHY_PARAM( ); + EU868_GET_PHY_PARAM( ); + KR920_GET_PHY_PARAM( ); + IN865_GET_PHY_PARAM( ); + US915_GET_PHY_PARAM( ); + US915_HYBRID_GET_PHY_PARAM( ); + default: + { + return phyParam; + } + } +} + +void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone ) +{ + switch( region ) + { + AS923_SET_BAND_TX_DONE( ); + AU915_SET_BAND_TX_DONE( ); + CN470_SET_BAND_TX_DONE( ); + CN779_SET_BAND_TX_DONE( ); + EU433_SET_BAND_TX_DONE( ); + EU868_SET_BAND_TX_DONE( ); + KR920_SET_BAND_TX_DONE( ); + IN865_SET_BAND_TX_DONE( ); + US915_SET_BAND_TX_DONE( ); + US915_HYBRID_SET_BAND_TX_DONE( ); + default: + { + return; + } + } +} + +void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type ) +{ + switch( region ) + { + AS923_INIT_DEFAULTS( ); + AU915_INIT_DEFAULTS( ); + CN470_INIT_DEFAULTS( ); + CN779_INIT_DEFAULTS( ); + EU433_INIT_DEFAULTS( ); + EU868_INIT_DEFAULTS( ); + KR920_INIT_DEFAULTS( ); + IN865_INIT_DEFAULTS( ); + US915_INIT_DEFAULTS( ); + US915_HYBRID_INIT_DEFAULTS( ); + default: + { + break; + } + } +} + +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( region ) + { + AS923_VERIFY( ); + AU915_VERIFY( ); + CN470_VERIFY( ); + CN779_VERIFY( ); + EU433_VERIFY( ); + EU868_VERIFY( ); + KR920_VERIFY( ); + IN865_VERIFY( ); + US915_VERIFY( ); + US915_HYBRID_VERIFY( ); + default: + { + return false; + } + } +} + +void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList ) +{ + switch( region ) + { + AS923_APPLY_CF_LIST( ); + AU915_APPLY_CF_LIST( ); + CN470_APPLY_CF_LIST( ); + CN779_APPLY_CF_LIST( ); + EU433_APPLY_CF_LIST( ); + EU868_APPLY_CF_LIST( ); + KR920_APPLY_CF_LIST( ); + IN865_APPLY_CF_LIST( ); + US915_APPLY_CF_LIST( ); + US915_HYBRID_APPLY_CF_LIST( ); + default: + { + break; + } + } +} + +bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet ) +{ + switch( region ) + { + AS923_CHAN_MASK_SET( ); + AU915_CHAN_MASK_SET( ); + CN470_CHAN_MASK_SET( ); + CN779_CHAN_MASK_SET( ); + EU433_CHAN_MASK_SET( ); + EU868_CHAN_MASK_SET( ); + KR920_CHAN_MASK_SET( ); + IN865_CHAN_MASK_SET( ); + US915_CHAN_MASK_SET( ); + US915_HYBRID_CHAN_MASK_SET( ); + default: + { + return false; + } + } +} + +bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + switch( region ) + { + AS923_ADR_NEXT( ); + AU915_ADR_NEXT( ); + CN470_ADR_NEXT( ); + CN779_ADR_NEXT( ); + EU433_ADR_NEXT( ); + EU868_ADR_NEXT( ); + KR920_ADR_NEXT( ); + IN865_ADR_NEXT( ); + US915_ADR_NEXT( ); + US915_HYBRID_ADR_NEXT( ); + default: + { + return false; + } + } +} + +void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + switch( region ) + { + AS923_COMPUTE_RX_WINDOW_PARAMETERS( ); + AU915_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN470_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN779_COMPUTE_RX_WINDOW_PARAMETERS( ); + EU433_COMPUTE_RX_WINDOW_PARAMETERS( ); + EU868_COMPUTE_RX_WINDOW_PARAMETERS( ); + KR920_COMPUTE_RX_WINDOW_PARAMETERS( ); + IN865_COMPUTE_RX_WINDOW_PARAMETERS( ); + US915_COMPUTE_RX_WINDOW_PARAMETERS( ); + US915_HYBRID_COMPUTE_RX_WINDOW_PARAMETERS( ); + default: + { + break; + } + } +} + +bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + switch( region ) + { + AS923_RX_CONFIG( ); + AU915_RX_CONFIG( ); + CN470_RX_CONFIG( ); + CN779_RX_CONFIG( ); + EU433_RX_CONFIG( ); + EU868_RX_CONFIG( ); + KR920_RX_CONFIG( ); + IN865_RX_CONFIG( ); + US915_RX_CONFIG( ); + US915_HYBRID_RX_CONFIG( ); + default: + { + return false; + } + } +} + +bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + switch( region ) + { + AS923_TX_CONFIG( ); + AU915_TX_CONFIG( ); + CN470_TX_CONFIG( ); + CN779_TX_CONFIG( ); + EU433_TX_CONFIG( ); + EU868_TX_CONFIG( ); + KR920_TX_CONFIG( ); + IN865_TX_CONFIG( ); + US915_TX_CONFIG( ); + US915_HYBRID_TX_CONFIG( ); + default: + { + return false; + } + } +} + +uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + switch( region ) + { + AS923_LINK_ADR_REQ( ); + AU915_LINK_ADR_REQ( ); + CN470_LINK_ADR_REQ( ); + CN779_LINK_ADR_REQ( ); + EU433_LINK_ADR_REQ( ); + EU868_LINK_ADR_REQ( ); + KR920_LINK_ADR_REQ( ); + IN865_LINK_ADR_REQ( ); + US915_LINK_ADR_REQ( ); + US915_HYBRID_LINK_ADR_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq ) +{ + switch( region ) + { + AS923_RX_PARAM_SETUP_REQ( ); + AU915_RX_PARAM_SETUP_REQ( ); + CN470_RX_PARAM_SETUP_REQ( ); + CN779_RX_PARAM_SETUP_REQ( ); + EU433_RX_PARAM_SETUP_REQ( ); + EU868_RX_PARAM_SETUP_REQ( ); + KR920_RX_PARAM_SETUP_REQ( ); + IN865_RX_PARAM_SETUP_REQ( ); + US915_RX_PARAM_SETUP_REQ( ); + US915_HYBRID_RX_PARAM_SETUP_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ) +{ + switch( region ) + { + AS923_NEW_CHANNEL_REQ( ); + AU915_NEW_CHANNEL_REQ( ); + CN470_NEW_CHANNEL_REQ( ); + CN779_NEW_CHANNEL_REQ( ); + EU433_NEW_CHANNEL_REQ( ); + EU868_NEW_CHANNEL_REQ( ); + KR920_NEW_CHANNEL_REQ( ); + IN865_NEW_CHANNEL_REQ( ); + US915_NEW_CHANNEL_REQ( ); + US915_HYBRID_NEW_CHANNEL_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq ) +{ + switch( region ) + { + AS923_TX_PARAM_SETUP_REQ( ); + AU915_TX_PARAM_SETUP_REQ( ); + CN470_TX_PARAM_SETUP_REQ( ); + CN779_TX_PARAM_SETUP_REQ( ); + EU433_TX_PARAM_SETUP_REQ( ); + EU868_TX_PARAM_SETUP_REQ( ); + KR920_TX_PARAM_SETUP_REQ( ); + IN865_TX_PARAM_SETUP_REQ( ); + US915_TX_PARAM_SETUP_REQ( ); + US915_HYBRID_TX_PARAM_SETUP_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ) +{ + switch( region ) + { + AS923_DL_CHANNEL_REQ( ); + AU915_DL_CHANNEL_REQ( ); + CN470_DL_CHANNEL_REQ( ); + CN779_DL_CHANNEL_REQ( ); + EU433_DL_CHANNEL_REQ( ); + EU868_DL_CHANNEL_REQ( ); + KR920_DL_CHANNEL_REQ( ); + IN865_DL_CHANNEL_REQ( ); + US915_DL_CHANNEL_REQ( ); + US915_HYBRID_DL_CHANNEL_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternateDr ) +{ + switch( region ) + { + AS923_ALTERNATE_DR( ); + AU915_ALTERNATE_DR( ); + CN470_ALTERNATE_DR( ); + CN779_ALTERNATE_DR( ); + EU433_ALTERNATE_DR( ); + EU868_ALTERNATE_DR( ); + KR920_ALTERNATE_DR( ); + IN865_ALTERNATE_DR( ); + US915_ALTERNATE_DR( ); + US915_HYBRID_ALTERNATE_DR( ); + default: + { + return 0; + } + } +} + +void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff ) +{ + switch( region ) + { + AS923_CALC_BACKOFF( ); + AU915_CALC_BACKOFF( ); + CN470_CALC_BACKOFF( ); + CN779_CALC_BACKOFF( ); + EU433_CALC_BACKOFF( ); + EU868_CALC_BACKOFF( ); + KR920_CALC_BACKOFF( ); + IN865_CALC_BACKOFF( ); + US915_CALC_BACKOFF( ); + US915_HYBRID_CALC_BACKOFF( ); + default: + { + break; + } + } +} + +bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + switch( region ) + { + AS923_NEXT_CHANNEL( ); + AU915_NEXT_CHANNEL( ); + CN470_NEXT_CHANNEL( ); + CN779_NEXT_CHANNEL( ); + EU433_NEXT_CHANNEL( ); + EU868_NEXT_CHANNEL( ); + KR920_NEXT_CHANNEL( ); + IN865_NEXT_CHANNEL( ); + US915_NEXT_CHANNEL( ); + US915_HYBRID_NEXT_CHANNEL( ); + default: + { + return false; + } + } +} + +LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd ) +{ + switch( region ) + { + AS923_CHANNEL_ADD( ); + AU915_CHANNEL_ADD( ); + CN470_CHANNEL_ADD( ); + CN779_CHANNEL_ADD( ); + EU433_CHANNEL_ADD( ); + EU868_CHANNEL_ADD( ); + KR920_CHANNEL_ADD( ); + IN865_CHANNEL_ADD( ); + US915_CHANNEL_ADD( ); + US915_HYBRID_CHANNEL_ADD( ); + default: + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } +} + +bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove ) +{ + switch( region ) + { + AS923_CHANNEL_REMOVE( ); + AU915_CHANNEL_REMOVE( ); + CN470_CHANNEL_REMOVE( ); + CN779_CHANNEL_REMOVE( ); + EU433_CHANNEL_REMOVE( ); + EU868_CHANNEL_REMOVE( ); + KR920_CHANNEL_REMOVE( ); + IN865_CHANNEL_REMOVE( ); + US915_CHANNEL_REMOVE( ); + US915_HYBRID_CHANNEL_REMOVE( ); + default: + { + return false; + } + } +} + +void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave ) +{ + switch( region ) + { + AS923_SET_CONTINUOUS_WAVE( ); + AU915_SET_CONTINUOUS_WAVE( ); + CN470_SET_CONTINUOUS_WAVE( ); + CN779_SET_CONTINUOUS_WAVE( ); + EU433_SET_CONTINUOUS_WAVE( ); + EU868_SET_CONTINUOUS_WAVE( ); + KR920_SET_CONTINUOUS_WAVE( ); + IN865_SET_CONTINUOUS_WAVE( ); + US915_SET_CONTINUOUS_WAVE( ); + US915_HYBRID_SET_CONTINUOUS_WAVE( ); + default: + { + break; + } + } +} + +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + switch( region ) + { + AS923_APPLY_DR_OFFSET( ); + AU915_APPLY_DR_OFFSET( ); + CN470_APPLY_DR_OFFSET( ); + CN779_APPLY_DR_OFFSET( ); + EU433_APPLY_DR_OFFSET( ); + EU868_APPLY_DR_OFFSET( ); + KR920_APPLY_DR_OFFSET( ); + IN865_APPLY_DR_OFFSET( ); + US915_APPLY_DR_OFFSET( ); + US915_HYBRID_APPLY_DR_OFFSET( ); + default: + { + return dr; + } + } +} diff --git a/libraries/LoRa_Node/src/mac/region/Region.h b/libraries/LoRa_Node/src/mac/region/Region.h new file mode 100644 index 0000000..8f27663 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/Region.h @@ -0,0 +1,1458 @@ +/*! + * \file Region.h + * + * \brief Region implementation. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGION Region implementation + * This is the common API to access the specific + * regional implementations. + * + * Preprocessor options: + * - LoRaWAN regions can be activated by defining the related preprocessor + * definition. It is possible to define more than one region. + * The following regions are supported: + * - #define REGION_AS923 + * - #define REGION_AU915 + * - #define REGION_CN470 + * - #define REGION_CN779 + * - #define REGION_EU433 + * - #define REGION_EU868 + * - #define REGION_KR920 + * - #define REGION_IN865 + * - #define REGION_US915 + * - #define REGION_US915_HYBRID + * + * \{ + */ +#ifndef __REGION_H__ +#define __REGION_H__ + +//modified---------------------- +#include "mac/LoRaMac.h" +//------------------------------ + + +/*! + * Macro to compute bit of a channel index. + */ +#define LC( channelIndex ) ( uint16_t )( 1 << ( channelIndex - 1 ) ) + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF12 - BW125 + * AU915 | SF10 - BW125 + * CN470 | SF12 - BW125 + * CN779 | SF12 - BW125 + * EU433 | SF12 - BW125 + * EU868 | SF12 - BW125 + * IN865 | SF12 - BW125 + * KR920 | SF12 - BW125 + * US915 | SF10 - BW125 + * US915_HYBRID | SF10 - BW125 + */ +#define DR_0 0 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF11 - BW125 + * AU915 | SF9 - BW125 + * CN470 | SF11 - BW125 + * CN779 | SF11 - BW125 + * EU433 | SF11 - BW125 + * EU868 | SF11 - BW125 + * IN865 | SF11 - BW125 + * KR920 | SF11 - BW125 + * US915 | SF9 - BW125 + * US915_HYBRID | SF9 - BW125 + */ +#define DR_1 1 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF10 - BW125 + * AU915 | SF8 - BW125 + * CN470 | SF10 - BW125 + * CN779 | SF10 - BW125 + * EU433 | SF10 - BW125 + * EU868 | SF10 - BW125 + * IN865 | SF10 - BW125 + * KR920 | SF10 - BW125 + * US915 | SF8 - BW125 + * US915_HYBRID | SF8 - BW125 + */ +#define DR_2 2 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF9 - BW125 + * AU915 | SF7 - BW125 + * CN470 | SF9 - BW125 + * CN779 | SF9 - BW125 + * EU433 | SF9 - BW125 + * EU868 | SF9 - BW125 + * IN865 | SF9 - BW125 + * KR920 | SF9 - BW125 + * US915 | SF7 - BW125 + * US915_HYBRID | SF7 - BW125 + */ +#define DR_3 3 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF8 - BW125 + * AU915 | SF8 - BW500 + * CN470 | SF8 - BW125 + * CN779 | SF8 - BW125 + * EU433 | SF8 - BW125 + * EU868 | SF8 - BW125 + * IN865 | SF8 - BW125 + * KR920 | SF8 - BW125 + * US915 | SF8 - BW500 + * US915_HYBRID | SF8 - BW500 + */ +#define DR_4 4 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF7 - BW125 + * AU915 | RFU + * CN470 | SF7 - BW125 + * CN779 | SF7 - BW125 + * EU433 | SF7 - BW125 + * EU868 | SF7 - BW125 + * IN865 | SF7 - BW125 + * KR920 | SF7 - BW125 + * US915 | RFU + * US915_HYBRID | RFU + */ +#define DR_5 5 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF7 - BW250 + * AU915 | RFU + * CN470 | SF12 - BW125 + * CN779 | SF7 - BW250 + * EU433 | SF7 - BW250 + * EU868 | SF7 - BW250 + * IN865 | SF7 - BW250 + * KR920 | RFU + * US915 | RFU + * US915_HYBRID | RFU + */ +#define DR_6 6 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | FSK + * AU915 | RFU + * CN470 | SF12 - BW125 + * CN779 | FSK + * EU433 | FSK + * EU868 | FSK + * IN865 | FSK + * KR920 | RFU + * US915 | RFU + * US915_HYBRID | RFU + */ +#define DR_7 7 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF12 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF12 - BW500 + * US915_HYBRID | SF12 - BW500 + */ +#define DR_8 8 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF11 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF11 - BW500 + * US915_HYBRID | SF11 - BW500 + */ +#define DR_9 9 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF10 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF10 - BW500 + * US915_HYBRID | SF10 - BW500 + */ +#define DR_10 10 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF9 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF9 - BW500 + * US915_HYBRID | SF9 - BW500 + */ +#define DR_11 11 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF8 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF8 - BW500 + * US915_HYBRID | SF8 - BW500 + */ +#define DR_12 12 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF7 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF7 - BW500 + * US915_HYBRID | SF7 - BW500 + */ +#define DR_13 13 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | RFU + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | RFU + * US915_HYBRID | RFU + */ +#define DR_14 14 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | RFU + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | RFU + * US915_HYBRID | RFU + */ +#define DR_15 15 + + + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP + * AU915 | Max EIRP + * CN470 | Max EIRP + * CN779 | Max EIRP + * EU433 | Max EIRP + * EU868 | Max EIRP + * IN865 | Max EIRP + * KR920 | Max EIRP + * US915 | Max ERP + * US915_HYBRID | Max ERP + */ +#define TX_POWER_0 0 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 2 + * AU915 | Max EIRP - 2 + * CN470 | Max EIRP - 2 + * CN779 | Max EIRP - 2 + * EU433 | Max EIRP - 2 + * EU868 | Max EIRP - 2 + * IN865 | Max EIRP - 2 + * KR920 | Max EIRP - 2 + * US915 | Max ERP - 2 + * US915_HYBRID | Max ERP - 2 + */ +#define TX_POWER_1 1 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 4 + * AU915 | Max EIRP - 4 + * CN470 | Max EIRP - 4 + * CN779 | Max EIRP - 4 + * EU433 | Max EIRP - 4 + * EU868 | Max EIRP - 4 + * IN865 | Max EIRP - 4 + * KR920 | Max EIRP - 4 + * US915 | Max ERP - 4 + * US915_HYBRID | Max ERP - 4 + */ +#define TX_POWER_2 2 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 6 + * AU915 | Max EIRP - 6 + * CN470 | Max EIRP - 6 + * CN779 | Max EIRP - 6 + * EU433 | Max EIRP - 6 + * EU868 | Max EIRP - 6 + * IN865 | Max EIRP - 6 + * KR920 | Max EIRP - 6 + * US915 | Max ERP - 6 + * US915_HYBRID | Max ERP - 6 + */ +#define TX_POWER_3 3 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 8 + * AU915 | Max EIRP - 8 + * CN470 | Max EIRP - 8 + * CN779 | Max EIRP - 8 + * EU433 | Max EIRP - 8 + * EU868 | Max EIRP - 8 + * IN865 | Max EIRP - 8 + * KR920 | Max EIRP - 8 + * US915 | Max ERP - 8 + * US915_HYBRID | Max ERP - 8 + */ +#define TX_POWER_4 4 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 10 + * AU915 | Max EIRP - 10 + * CN470 | Max EIRP - 10 + * CN779 | Max EIRP - 10 + * EU433 | Max EIRP - 10 + * EU868 | Max EIRP - 10 + * IN865 | Max EIRP - 10 + * KR920 | Max EIRP - 10 + * US915 | Max ERP - 10 + * US915_HYBRID | Max ERP - 10 + */ +#define TX_POWER_5 5 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 12 + * AU915 | Max EIRP - 12 + * CN470 | Max EIRP - 12 + * CN779 | - + * EU433 | - + * EU868 | Max EIRP - 12 + * IN865 | Max EIRP - 12 + * KR920 | Max EIRP - 12 + * US915 | Max ERP - 12 + * US915_HYBRID | Max ERP - 12 + */ +#define TX_POWER_6 6 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 14 + * AU915 | Max EIRP - 14 + * CN470 | Max EIRP - 14 + * CN779 | - + * EU433 | - + * EU868 | Max EIRP - 14 + * IN865 | Max EIRP - 14 + * KR920 | Max EIRP - 14 + * US915 | Max ERP - 14 + * US915_HYBRID | Max ERP - 14 + */ +#define TX_POWER_7 7 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 16 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 16 + * KR920 | Max EIRP - 16 + * US915 | Max ERP - 16 + * US915_HYBRID | Max ERP -16 + */ +#define TX_POWER_8 8 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 18 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 18 + * KR920 | Max EIRP - 18 + * US915 | Max ERP - 16 + * US915_HYBRID | Max ERP - 16 + */ +#define TX_POWER_9 9 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 20 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 20 + * KR920 | Max EIRP - 20 + * US915 | Max ERP - 10 + * US915_HYBRID | Max ERP - 10 + */ +#define TX_POWER_10 10 + +/*! + * RFU + */ +#define TX_POWER_11 11 + +/*! + * RFU + */ +#define TX_POWER_12 12 + +/*! + * RFU + */ +#define TX_POWER_13 13 + +/*! + * RFU + */ +#define TX_POWER_14 14 + +/*! + * RFU + */ +#define TX_POWER_15 15 + + + +/*! + * Enumeration of phy attributes. + */ +typedef enum ePhyAttribute +{ + /*! + * Minimum RX datarate. + */ + PHY_MIN_RX_DR, + /*! + * Minimum TX datarate. + */ + PHY_MIN_TX_DR, + /*! + * Maximum RX datarate. + */ + PHY_MAX_RX_DR, + /*! + * Maximum TX datarate. + */ + PHY_MAX_TX_DR, + /*! + * TX datarate. + */ + PHY_TX_DR, + /*! + * Default TX datarate. + */ + PHY_DEF_TX_DR, + /*! + * RX datarate. + */ + PHY_RX_DR, + /*! + * TX power. + */ + PHY_TX_POWER, + /*! + * Default TX power. + */ + PHY_DEF_TX_POWER, + /*! + * Maximum payload possible. + */ + PHY_MAX_PAYLOAD, + /*! + * Maximum payload possible when repeater support is enabled. + */ + PHY_MAX_PAYLOAD_REPEATER, + /*! + * Duty cycle. + */ + PHY_DUTY_CYCLE, + /*! + * Maximum receive window duration. + */ + PHY_MAX_RX_WINDOW, + /*! + * Receive delay for window 1. + */ + PHY_RECEIVE_DELAY1, + /*! + * Receive delay for window 2. + */ + PHY_RECEIVE_DELAY2, + /*! + * Join accept delay for window 1. + */ + PHY_JOIN_ACCEPT_DELAY1, + /*! + * Join accept delay for window 2. + */ + PHY_JOIN_ACCEPT_DELAY2, + /*! + * Maximum frame counter gap. + */ + PHY_MAX_FCNT_GAP, + /*! + * Acknowledgement time out. + */ + PHY_ACK_TIMEOUT, + /*! + * Default datarate offset for window 1. + */ + PHY_DEF_DR1_OFFSET, + /*! + * Default receive window 2 frequency. + */ + PHY_DEF_RX2_FREQUENCY, + /*! + * Default receive window 2 datarate. + */ + PHY_DEF_RX2_DR, + /*! + * Channels mask. + */ + PHY_CHANNELS_MASK, + /*! + * Channels default mask. + */ + PHY_CHANNELS_DEFAULT_MASK, + /*! + * Maximum number of supported channels + */ + PHY_MAX_NB_CHANNELS, + /*! + * Channels. + */ + PHY_CHANNELS, + /*! + * Default value of the uplink dwell time. + */ + PHY_DEF_UPLINK_DWELL_TIME, + /*! + * Default value of the downlink dwell time. + */ + PHY_DEF_DOWNLINK_DWELL_TIME, + /*! + * Default value of the MaxEIRP. + */ + PHY_DEF_MAX_EIRP, + /*! + * Default value of the antenna gain. + */ + PHY_DEF_ANTENNA_GAIN, + /*! + * Value for the number of join trials. + */ + PHY_NB_JOIN_TRIALS, + /*! + * Default value for the number of join trials. + */ + PHY_DEF_NB_JOIN_TRIALS, + /*! + * Next lower datarate. + */ + PHY_NEXT_LOWER_TX_DR +}PhyAttribute_t; + +/*! + * Enumeration of initialization types. + */ +typedef enum eInitType +{ + /*! + * Performs an initialization and overwrites all existing data. + */ + INIT_TYPE_INIT, + /*! + * Restores default channels only. + */ + INIT_TYPE_RESTORE +}InitType_t; + +typedef enum eChannelsMask +{ + /*! + * The channels mask. + */ + CHANNELS_MASK, + /*! + * The channels default mask. + */ + CHANNELS_DEFAULT_MASK +}ChannelsMask_t; + +/*! + * Union for the structure uGetPhyParams + */ +typedef union uPhyParam +{ + /*! + * A parameter value. + */ + uint32_t Value; + /*! + * A floating point value. + */ + float fValue; + /*! + * Pointer to the channels mask. + */ + uint16_t* ChannelsMask; + /*! + * Pointer to the channels. + */ + ChannelParams_t* Channels; +}PhyParam_t; + +/*! + * Parameter structure for the function RegionGetPhyParam. + */ +typedef struct sGetPhyParams +{ + /*! + * Setup the parameter to get. + */ + PhyAttribute_t Attribute; + /*! + * Datarate. + * The parameter is needed for the following queries: + * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR. + */ + int8_t Datarate; + /*! + * Uplink dwell time. + * The parameter is needed for the following queries: + * PHY_MIN_TX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR. + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time. + * The parameter is needed for the following queries: + * PHY_MIN_RX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER. + */ + uint8_t DownlinkDwellTime; +}GetPhyParams_t; + +/*! + * Parameter structure for the function RegionSetBandTxDone. + */ +typedef struct sSetBandTxDoneParams +{ + /*! + * Channel to update. + */ + uint8_t Channel; + /*! + * Last TX done time. + */ + TimerTime_t LastTxDoneTime; +}SetBandTxDoneParams_t; + +/*! + * Parameter structure for the function RegionVerify. + */ +typedef union uVerifyParams +{ + /*! + * TX power to verify. + */ + int8_t TxPower; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycle; + /*! + * The number of join trials. + */ + uint8_t NbJoinTrials; + /*! + * Datarate to verify. + */ + struct sDatarateParams + { + /*! + * Datarate to verify. + */ + int8_t Datarate; + /*! + * The downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * The up link dwell time. + */ + uint8_t UplinkDwellTime; + }DatarateParams; +}VerifyParams_t; + +/*! + * Parameter structure for the function RegionApplyCFList. + */ +typedef struct sApplyCFListParams +{ + /*! + * Payload which contains the CF list. + */ + uint8_t* Payload; + /*! + * Size of the payload. + */ + uint8_t Size; +}ApplyCFListParams_t; + +/*! + * Parameter structure for the function RegionChanMaskSet. + */ +typedef struct sChanMaskSetParams +{ + /*! + * Pointer to the channels mask which should be set. + */ + uint16_t* ChannelsMaskIn; + /*! + * Pointer to the channels mask which should be set. + */ + ChannelsMask_t ChannelsMaskType; +}ChanMaskSetParams_t; + +/*! + * Parameter structure for the function RegionAdrNext. + */ +typedef struct sAdrNextParams +{ + /*! + * Set to true, if the function should update the channels mask. + */ + bool UpdateChanMask; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * ADR ack counter. + */ + uint32_t AdrAckCounter; + /*! + * Datarate used currently. + */ + int8_t Datarate; + /*! + * TX power used currently. + */ + int8_t TxPower; + /*! + * UplinkDwellTime + */ + uint8_t UplinkDwellTime; +}AdrNextParams_t; + +/*! + * Parameter structure for the function RegionRxConfig. + */ +typedef struct sRxConfigParams +{ + /*! + * The RX channel. + */ + uint8_t Channel; + /*! + * RX datarate. + */ + int8_t Datarate; + /*! + * RX bandwidth. + */ + uint8_t Bandwidth; + /*! + * RX datarate offset. + */ + int8_t DrOffset; + /*! + * RX frequency. + */ + uint32_t Frequency; + /*! + * RX window timeout + */ + uint32_t WindowTimeout; + /*! + * RX window offset + */ + int32_t WindowOffset; + /*! + * Downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * Set to true, if a repeater is supported. + */ + bool RepeaterSupport; + /*! + * Set to true, if RX should be continuous. + */ + bool RxContinuous; + /*! + * Sets the RX window. 0: RX window 1, 1: RX window 2. + */ + bool Window; +}RxConfigParams_t; + +/*! + * Parameter structure for the function RegionTxConfig. + */ +typedef struct sTxConfigParams +{ + /*! + * The TX channel. + */ + uint8_t Channel; + /*! + * The TX datarate. + */ + int8_t Datarate; + /*! + * The TX power. + */ + int8_t TxPower; + /*! + * The Max EIRP, if applicable. + */ + float MaxEirp; + /*! + * The antenna gain, if applicable. + */ + float AntennaGain; + /*! + * Frame length to setup. + */ + uint16_t PktLen; +}TxConfigParams_t; + +/*! + * Parameter structure for the function RegionLinkAdrReq. + */ +typedef struct sLinkAdrReqParams +{ + /*! + * Pointer to the payload which contains the MAC commands. + */ + uint8_t* Payload; + /*! + * Size of the payload. + */ + uint8_t PayloadSize; +}LinkAdrReqParams_t; + +/*! + * Parameter structure for the function RegionRxParamSetupReq. + */ +typedef struct sRxParamSetupReqParams +{ + /*! + * The datarate to setup. + */ + int8_t Datarate; + /*! + * Datarate offset. + */ + int8_t DrOffset; + /*! + * The frequency to setup. + */ + uint32_t Frequency; +}RxParamSetupReqParams_t; + +/*! + * Parameter structure for the function RegionNewChannelReq. + */ +typedef struct sNewChannelReqParams +{ + /*! + * Pointer to the new channels. + */ + ChannelParams_t* NewChannel; + /*! + * Channel id. + */ + int8_t ChannelId; +}NewChannelReqParams_t; + +/*! + * Parameter structure for the function RegionTxParamSetupReq. + */ +typedef struct sTxParamSetupReqParams +{ + /*! + * Uplink dwell time. + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * Max EIRP. + */ + uint8_t MaxEirp; +}TxParamSetupReqParams_t; + +/*! + * Parameter structure for the function RegionDlChannelReq. + */ +typedef struct sDlChannelReqParams +{ + /*! + * Channel Id to add the frequency. + */ + uint8_t ChannelId; + /*! + * Alternative frequency for the Rx1 window. + */ + uint32_t Rx1Frequency; +}DlChannelReqParams_t; + +/*! + * Parameter structure for the function RegionAlternateDr. + */ +typedef struct sAlternateDrParams +{ + /*! + * Number of trials. + */ + uint16_t NbTrials; +}AlternateDrParams_t; + +/*! + * Parameter structure for the function RegionCalcBackOff. + */ +typedef struct sCalcBackOffParams +{ + /*! + * Set to true, if the node has already joined a network, otherwise false. + */ + bool Joined; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; + /*! + * Current channel index. + */ + uint8_t Channel; + /*! + * Elapsed time since the start of the node. + */ + TimerTime_t ElapsedTime; + /*! + * Time-on-air of the last transmission. + */ + TimerTime_t TxTimeOnAir; +}CalcBackOffParams_t; + +/*! + * Parameter structure for the function RegionNextChannel. + */ +typedef struct sNextChanParams +{ + /*! + * Aggregated time-off time. + */ + TimerTime_t AggrTimeOff; + /*! + * Time of the last aggregated TX. + */ + TimerTime_t LastAggrTx; + /*! + * Current datarate. + */ + int8_t Datarate; + /*! + * Set to true, if the node has already joined a network, otherwise false. + */ + bool Joined; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; +}NextChanParams_t; + +/*! + * Parameter structure for the function RegionChannelsAdd. + */ +typedef struct sChannelAddParams +{ + /*! + * Pointer to the new channel to add. + */ + ChannelParams_t* NewChannel; + /*! + * Channel id to add. + */ + uint8_t ChannelId; +}ChannelAddParams_t; + +/*! + * Parameter structure for the function RegionChannelsRemove. + */ +typedef struct sChannelRemoveParams +{ + /*! + * Channel id to remove. + */ + uint8_t ChannelId; +}ChannelRemoveParams_t; + +/*! + * Parameter structure for the function RegionContinuousWave. + */ +typedef struct sContinuousWaveParams +{ + /*! + * Current channel index. + */ + uint8_t Channel; + /*! + * Datarate. Used to limit the TX power. + */ + int8_t Datarate; + /*! + * The TX power to setup. + */ + int8_t TxPower; + /*! + * Max EIRP, if applicable. + */ + float MaxEirp; + /*! + * The antenna gain, if applicable. + */ + float AntennaGain; + /*! + * Specifies the time the radio will stay in CW mode. + */ + uint16_t Timeout; +}ContinuousWaveParams_t; + + + +/*! + * \brief The function verifies if a region is active or not. If a region + * is not active, it cannot be used. + * + * \param [IN] region LoRaWAN region. + * + * \retval Return true, if the region is supported. + */ +bool RegionIsActive( LoRaMacRegion_t region ); + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] type Sets the initialization type. + */ +void RegionInitDefaults( LoRaMacRegion_t region, InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionAdrNext( LoRaMacRegion_t region, AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate ); + +/* + * Rx window precise timing + * + * For more details please consult the following document, chapter 3.1.2. + * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf + * or + * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf + * + * Downlink start: T = Tx + 1s (+/- 20 us) + * | + * TRxEarly | TRxLate + * | | | + * | | +---+---+---+---+---+---+---+---+ + * | | | Latest Rx window | + * | | +---+---+---+---+---+---+---+---+ + * | | | + * +---+---+---+---+---+---+---+---+ + * | Earliest Rx window | + * +---+---+---+---+---+---+---+---+ + * | + * +---+---+---+---+---+---+---+---+ + *Downlink preamble 8 symbols | | | | | | | | | + * +---+---+---+---+---+---+---+---+ + * + * Worst case Rx window timings + * + * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME + * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME + * + * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR + * + * RxOffset = ( TRxLate + TRxEarly ) / 2 + * + * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR + * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME + * + * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol + */ +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief TX configuration. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a New Channel Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall ignore the command. + */ +int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionAlternateDr( LoRaMacRegion_t region, AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCalcBackOff( LoRaMacRegion_t region, CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [IN] region LoRaWAN region. + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. + */ +bool RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionSetContinuousWave( LoRaMacRegion_t region, ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGION */ + +#endif // __REGION_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionAS923.c b/libraries/LoRa_Node/src/mac/region/RegionAS923.c new file mode 100644 index 0000000..b7e9ae0 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionAS923.c @@ -0,0 +1,1122 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region AS923 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionAS923.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[AS923_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[AS923_MAX_NB_BANDS] = +{ + AS923_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsAS923[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyTxFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 915000000 ) || ( freq > 928000000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < AS923_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( AS923_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + if( getPhy->DownlinkDwellTime == 0 ) + { + phyParam.Value = AS923_RX_MIN_DATARATE; + } + else + { + phyParam.Value = AS923_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_MIN_TX_DR: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = AS923_TX_MIN_DATARATE; + } + else + { + phyParam.Value = AS923_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = AS923_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_TX_MIN_DATARATE ); + } + else + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_DWELL_LIMIT_DATARATE ); + } + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = AS923_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate]; + } + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate]; + } + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = AS923_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = AS923_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = AS923_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = AS923_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = AS923_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = AS923_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = AS923_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( AS923_ACKTIMEOUT + randr( -AS923_ACK_TIMEOUT_RND, AS923_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = AS923_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AS923_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AS923_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AS923_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = AS923_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = AS923_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = AS923_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = AS923_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 1; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionAS923InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) AS923_LC1; + Channels[1] = ( ChannelParams_t ) AS923_LC2; + + // Initialize the channels default mask + ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + ChannelsMask[0] |= ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_TX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + if( verify->DatarateParams.DownlinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return AS923_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + return true; + } + default: + return false; + } +} + +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = AS923_NUMB_DEFAULT_CHANNELS; i < 15; i+=3, chanIdx++ ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionAS923ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionAS923ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionAS923AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t minTxDatarate = 0; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionAS923GetPhyParam( &getPhy ); + minTxDatarate = phyParam.Value; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + // Apply the minimum possible datarate. + datarate = MAX( datarate, minTxDatarate ); + + if( adrNext->AdrEnabled == true ) + { + if( datarate == minTxDatarate ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= AS923_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = AS923_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( AS923_ADR_ACK_LIMIT + AS923_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % AS923_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionAS923GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == minTxDatarate ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesAS923[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAS923[datarate], BandwidthsAS923[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesAS923[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50e3, phyDr * 1e3, 0, 83.333e3, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + // Check the downlink dwell time + if( rxConfig->DownlinkDwellTime == 0 ) + { + // Check for repeater support + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterDwell0AS923[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateDwell0AS923[dr]; + } + } + else + { + // Same table for repeater and non-repeater support for dwell time + // configuration 1. + maxPayload = MaxPayloadOfDatarateDwell1DownAS923[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesAS923[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25e3, bandwidth, phyDr * 1e3, 0, 5, false, true, 0, 0, false, 3e3 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + } + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < AS923_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( AS923_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( AS923_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = AS923_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Set the channels mask to a default value + memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); + // Update the channels mask + ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AS923_MIN_RX1_DR_OFFSET, AS923_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionAS923ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionAS923ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Accept the request + return 0; +} + +uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionAS923AlternateDr( AlternateDrParams_t* alternateDr ) +{ + // Only AS923_DWELL_LIMIT_DATARATE is supported + return AS923_DWELL_LIMIT_DATARATE; +} + +void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + Bands[Channels[channel].Band].TimeOff = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t channelNext = 0; + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + TimerTime_t carrierSenseTime = 0; + bool channelFree = false; + + if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->DutyCycleEnabled, Bands, AS923_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < AS923_MAX_NB_CHANNELS; i++ ) + { + channelNext = enabledChannels[j]; + j = ( j + 1 ) % nbEnabledChannels; + + carrierSenseTime = TimerGetCurrentTime( ); + channelFree = true; + + // Perform carrier sense for 6ms + while( TimerGetElapsedTime( carrierSenseTime ) < AS923_CARRIER_SENSE_TIME ) + { + if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, AS923_RSSI_FREE_TH ) == false ) + { + channelFree = false; + break; + } + } + + // If the channel is free, we can stop the LBT mechanism + if( channelFree == true ) + { + // Free channel found + *channel = channelNext; + *time = 0; + return true; + } + } + return false; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel, restore defaults + ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= AS923_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Default channels don't accept all values + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + // Validate the datarate range for min: must be DR_0 + if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) + { + drInvalid = true; + } + // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + // We are not allowed to change the frequency + if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) + { + freqInvalid = true; + } + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( ChannelsMask, id, AS923_MAX_NB_CHANNELS ); +} + +void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + // Initialize minDr for a downlink dwell time configuration of 0 + int8_t minDr = DR_0; + + // Update the minDR for a downlink dwell time configuration of 1 + if( downlinkDwellTime == 1 ) + { + minDr = AS923_DWELL_LIMIT_DATARATE; + } + + // Apply offset formula + return MIN( DR_5, MAX( minDr, dr - EffectiveRx1DrOffsetAS923[drOffset] ) ); +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionAS923.h b/libraries/LoRa_Node/src/mac/region/RegionAS923.h new file mode 100644 index 0000000..3306bb4 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionAS923.h @@ -0,0 +1,498 @@ +/*! + * \file RegionAS923.h + * + * \brief Region definition for AS923 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONAS923 Region AS923 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_AS923_H__ +#define __REGION_AS923_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define AS923_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define AS923_NUMB_DEFAULT_CHANNELS 2 + +/*! + * Minimal datarate that can be used by the node + */ +#define AS923_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AS923_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define AS923_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AS923_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define AS923_DEFAULT_DATARATE DR_2 + +/*! + * The minimum datarate which is used when the + * dwell time is limited. + */ +#define AS923_DWELL_LIMIT_DATARATE DR_2 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define AS923_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define AS923_MAX_RX1_DR_OFFSET 7 + +/*! + * Default Rx1 receive datarate offset + */ +#define AS923_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define AS923_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define AS923_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define AS923_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default uplink dwell time configuration + */ +#define AS923_DEFAULT_UPLINK_DWELL_TIME 1 + +/*! + * Default downlink dwell time configuration + */ +#define AS923_DEFAULT_DOWNLINK_DWELL_TIME 1 + +/*! + * Default Max EIRP + */ +#define AS923_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define AS923_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define AS923_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define AS923_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define AS923_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AS923_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define AS923_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define AS923_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define AS923_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define AS923_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define AS923_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define AS923_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define AS923_ACK_TIMEOUT_RND 1000 + +#if ( AS923_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define AS923_RX_WND_2_FREQ 923200000 + +/*! + * Second reception window channel datarate definition. + */ +#define AS923_RX_WND_2_DR DR_2 + +/*! + * Maximum number of bands + */ +#define AS923_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define AS923_LC1 { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define AS923_LC2 { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define AS923_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) ) + +/*! + * RSSI threshold for a free channel [dBm] + */ +#define AS923_RSSI_FREE_TH -85 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define AS923_CARRIER_SENSE_TIME 6 + +/*! + * Data rates table definition + */ +static const uint8_t DataratesAS923[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsAS923[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. The table provides + * repeater support. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with and without repeater. + * The table proides repeater support. The table is only valid for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1UpAS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with and without repeater. + * The table proides repeater support. The table is only valid for downlinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1DownAS923[] = { 0, 0, 11, 53, 126, 242, 242, 242 }; + +/*! + * Effective datarate offsets for receive window 1. + */ +static const int8_t EffectiveRx1DrOffsetAS923[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionAS923InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionAS923AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionAS923AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionAS923CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionAS923SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONAS923 */ + +#endif // __REGION_AS923_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionAU915.c b/libraries/LoRa_Node/src/mac/region/RegionAU915.c new file mode 100644 index 0000000..6ab7c94 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionAU915.c @@ -0,0 +1,860 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region AU915 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionAU915.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[AU915_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[AU915_MAX_NB_BANDS] = +{ + AU915_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels remaining + */ +static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsAU915[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < AU915_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = AU915_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = AU915_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = AU915_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AU915_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = AU915_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateAU915[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterAU915[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = AU915_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = AU915_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = AU915_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = AU915_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = AU915_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = AU915_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = AU915_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( AU915_ACKTIMEOUT + randr( -AU915_ACK_TIMEOUT_RND, AU915_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = AU915_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AU915_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AU915_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AU915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = AU915_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 2; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionAU915InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ ) + { + Channels[i].Frequency = 915.2e6 + i * 200e3; + Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + Channels[i].Band = 0; + } + // 500 kHz channels + for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ ) + { + Channels[i].Frequency = 915.9e6 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1.6e6; + Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + Channels[i].Band = 0; + } + + // Initialize channels default mask + ChannelsDefaultMask[0] = 0xFFFF; + ChannelsDefaultMask[1] = 0xFFFF; + ChannelsDefaultMask[2] = 0xFFFF; + ChannelsDefaultMask[3] = 0xFFFF; + ChannelsDefaultMask[4] = 0x00FF; + ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return AU915_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 2 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + return; +} + +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 ); + + // Check the number of active channels + // According to ACMA regulation, we require at least 20 125KHz channels, if + // the node shall utilize 125KHz channels. + if( ( nbChannels < 20 ) && + ( nbChannels > 0 ) ) + { + return false; + } + + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionAU915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == AU915_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= AU915_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = AU915_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( AU915_ADR_ACK_LIMIT + AU915_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % AU915_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionAU915GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == AU915_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0x00FF; + ChannelsMask[5] = 0x0000; + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesAU915[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[datarate], BandwidthsAU915[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesAU915[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterAU915[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateAU915[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesAU915[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + // Verify datarate + if( RegionCommonChanVerifyDr( AU915_MAX_NB_CHANNELS, channelsMask, linkAdrParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( AU915_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = AU915_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Copy Mask + RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 ); + + ChannelsMaskRemaining[0] &= ChannelsMask[0]; + ChannelsMaskRemaining[1] &= ChannelsMask[1]; + ChannelsMaskRemaining[2] &= ChannelsMask[2]; + ChannelsMaskRemaining[3] &= ChannelsMask[3]; + ChannelsMaskRemaining[4] = ChannelsMask[4]; + ChannelsMaskRemaining[5] = ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + uint32_t freq = rxParamSetupReq->Frequency; + + // Verify radio frequency + if( ( Radio.CheckRfFrequency( freq ) == false ) || + ( freq < AU915_FIRST_RX1_CHANNEL ) || + ( freq > AU915_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionAU915AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + // Re-enable 500 kHz default channels + ChannelsMask[4] = 0x00FF; + + if( ( alternateDr->NbTrials & 0x01 ) == 0x01 ) + { + datarate = DR_4; + } + else + { + datarate = DR_0; + } + return datarate; +} + +void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t joinDutyCycle = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * joinDutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + Bands[Channels[channel].Band].TimeOff = 0; + } +} + +bool RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4 ); + } + // Check other channels + if( nextChanParams->Datarate >= DR_4 ) + { + if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) + { + ChannelsMaskRemaining[4] = ChannelsMask[4]; + } + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + ChannelsMaskRemaining, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + // Disable the channel in the mask + RegionCommonChanDisable( ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS - 8 ); + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsAU915[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionAU915.h b/libraries/LoRa_Node/src/mac/region/RegionAU915.h new file mode 100644 index 0000000..40a1610 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionAU915.h @@ -0,0 +1,453 @@ +/*! + * \file RegionAU915.h + * + * \brief Region definition for AU915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONAU915 Region AU915 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_AU915_H__ +#define __REGION_AU915_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define AU915_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define AU915_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_TX_MAX_DATARATE DR_6 + +/*! + * Minimal datarate that can be used by the node + */ +#define AU915_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define AU915_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define AU915_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define AU915_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define AU915_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define AU915_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define AU915_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define AU915_DEFAULT_TX_POWER TX_POWER_5 + +/*! + * Default Max EIRP + */ +#define AU915_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define AU915_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define AU915_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define AU915_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define AU915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AU915_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define AU915_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define AU915_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define AU915_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define AU915_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define AU915_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define AU915_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define AU915_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define AU915_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define AU915_RX_WND_2_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define AU915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define AU915_FIRST_RX1_CHANNEL ( (uint32_t) 923.3e6 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define AU915_LAST_RX1_CHANNEL ( (uint32_t) 927.5e6 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define AU915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600e3 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesAU915[] = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsAU915[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 500e3, 0, 500e3, 500e3, 500e3, 500e3, 500e3, 500e3, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsAU915[5][4] = +{ + { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 + { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 + { DR_12, DR_11, DR_10, DR_9 }, // DR_2 + { DR_13, DR_12, DR_11, DR_10 }, // DR_3 + { DR_13, DR_13, DR_12, DR_11 }, // DR_4 +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateAU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterAU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionAU915InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionAU915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionAU915AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONAU915 */ + +#endif // __REGION_AU915_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionCN470.c b/libraries/LoRa_Node/src/mac/region/RegionCN470.c new file mode 100644 index 0000000..13907d5 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionCN470.c @@ -0,0 +1,813 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region CN470 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionCN470.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[CN470_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[CN470_MAX_NB_BANDS] = +{ + CN470_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsCN470[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < CN470_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN470_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN470_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN470_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN470_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN470_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN470[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN470_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN470_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = CN470_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = CN470_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = CN470_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = CN470_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = CN470_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( CN470_ACKTIMEOUT + randr( -CN470_ACK_TIMEOUT_RND, CN470_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = CN470_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = CN470_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN470_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN470_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN470_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionCN470InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ ) + { + Channels[i].Frequency = 470.3e6 + i * 200e3; + Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + Channels[i].Band = 0; + } + + // Initialize the channels default mask + ChannelsDefaultMask[0] = 0xFFFF; + ChannelsDefaultMask[1] = 0xFFFF; + ChannelsDefaultMask[2] = 0xFFFF; + ChannelsDefaultMask[3] = 0xFFFF; + ChannelsDefaultMask[4] = 0xFFFF; + ChannelsDefaultMask[5] = 0xFFFF; + + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + break; + } + default: + { + break; + } + } +} + +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN470_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + return; +} + +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionCN470AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == CN470_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= CN470_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = CN470_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( CN470_ADR_ACK_LIMIT + CN470_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % CN470_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionCN470GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == CN470_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0xFFFF; + ChannelsMask[5] = 0xFFFF; + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesCN470[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[datarate], BandwidthsCN470[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN470[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN470[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN470[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesCN470[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0xFFFF; + channelsMask[5] = 0xFFFF; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) && + ( Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( CN470_MAX_NB_CHANNELS, channelsMask, linkAdrParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( CN470_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = CN470_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Copy Mask + RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 ); + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + uint32_t freq = rxParamSetupReq->Frequency; + + // Verify radio frequency + if( ( Radio.CheckRfFrequency( freq ) == false ) || + ( freq < CN470_FIRST_RX1_CHANNEL ) || + ( freq > CN470_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) CN470_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionCN470AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + if( ( alternateDr->NbTrials % 48 ) == 0 ) + { + datarate = DR_0; + } + else if( ( alternateDr->NbTrials % 32 ) == 0 ) + { + datarate = DR_1; + } + else if( ( alternateDr->NbTrials % 24 ) == 0 ) + { + datarate = DR_2; + } + else if( ( alternateDr->NbTrials % 16 ) == 0 ) + { + datarate = DR_3; + } + else if( ( alternateDr->NbTrials % 8 ) == 0 ) + { + datarate = DR_4; + } + else + { + datarate = DR_5; + } + return datarate; +} + +void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t joinDutyCycle = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * joinDutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + Bands[Channels[channel].Band].TimeOff = 0; + } +} + +bool RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( ChannelsMask, 0, 6 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0xFFFF; + ChannelsMask[5] = 0xFFFF; + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionCN470.h b/libraries/LoRa_Node/src/mac/region/RegionCN470.h new file mode 100644 index 0000000..186de85 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionCN470.h @@ -0,0 +1,441 @@ +/*! + * \file RegionCN470.h + * + * \brief Region definition for CN470 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 Region CN470 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN470_H__ +#define __REGION_CN470_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define CN470_MAX_NB_CHANNELS 96 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ +#define CN470_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN470_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN470_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define CN470_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN470_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN470_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN470_DEFAULT_TX_POWER TX_POWER_2 + +/*! + * Default Max EIRP + */ +#define CN470_DEFAULT_MAX_EIRP 19.15f + +/*! + * Default antenna gain + */ +#define CN470_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define CN470_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define CN470_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN470_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define CN470_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define CN470_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define CN470_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define CN470_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define CN470_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define CN470_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define CN470_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define CN470_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define CN470_RX_WND_2_FREQ 505300000 + +/*! + * Second reception window channel datarate definition. + */ +#define CN470_RX_WND_2_DR DR_0 + +/*! + * LoRaMac maximum number of bands + */ +#define CN470_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for CN470 band + */ +#define CN470_FIRST_RX1_CHANNEL ( (uint32_t) 500.3e6 ) + +/*! + * Defines the last channel for RX window 1 for CN470 band + */ +#define CN470_LAST_RX1_CHANNEL ( (uint32_t) 509.7e6 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define CN470_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200e3 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN470[] = { 12, 11, 10, 9, 8, 7 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN470[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateCN470[] = { 51, 51, 51, 115, 222, 222 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterCN470[] = { 51, 51, 51, 115, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN470InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionCN470AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionCN470AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCN470CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN470SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONCN470 */ + +#endif // __REGION_CN470_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionCN779.c b/libraries/LoRa_Node/src/mac/region/RegionCN779.c new file mode 100644 index 0000000..42d058e --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionCN779.c @@ -0,0 +1,1046 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region CN779 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionCN779.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[CN779_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[CN779_MAX_NB_BANDS] = +{ + CN779_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsCN779[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyTxFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 779500000 ) || ( freq > 786500000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < CN779_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( CN779_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN779_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN779_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN779_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN779_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN779_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterCN779[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN779_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN779_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = CN779_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = CN779_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = CN779_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = CN779_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = CN779_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( CN779_ACKTIMEOUT + randr( -CN779_ACK_TIMEOUT_RND, CN779_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = CN779_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = CN779_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN779_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN779_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN779_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN779_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionCN779InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) CN779_LC1; + Channels[1] = ( ChannelParams_t ) CN779_LC2; + Channels[2] = ( ChannelParams_t ) CN779_LC3; + + // Initialize the channels default mask + ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + ChannelsMask[0] |= ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN779_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = CN779_NUMB_DEFAULT_CHANNELS; i < 15; i+=3, chanIdx++ ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionCN779ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionCN779ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionCN779AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == CN779_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= CN779_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = CN779_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( CN779_ADR_ACK_LIMIT + CN779_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % CN779_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionCN779GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == CN779_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesCN779[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesCN779[datarate], BandwidthsCN779[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN779[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50e3, phyDr * 1e3, 0, 83.333e3, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterCN779[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateCN779[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesCN779[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25e3, bandwidth, phyDr * 1e3, 0, 5, false, true, 0, 0, false, 3e3 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + } + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < CN779_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( CN779_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( CN779_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = CN779_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Set the channels mask to a default value + memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); + // Update the channels mask + ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN779_MIN_RX1_DR_OFFSET, CN779_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionCN779ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionCN779ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionCN779AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + if( ( alternateDr->NbTrials % 48 ) == 0 ) + { + datarate = DR_0; + } + else if( ( alternateDr->NbTrials % 32 ) == 0 ) + { + datarate = DR_1; + } + else if( ( alternateDr->NbTrials % 24 ) == 0 ) + { + datarate = DR_2; + } + else if( ( alternateDr->NbTrials % 16 ) == 0 ) + { + datarate = DR_3; + } + else if( ( alternateDr->NbTrials % 8 ) == 0 ) + { + datarate = DR_4; + } + else + { + datarate = DR_5; + } + return datarate; +} + +void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + Bands[Channels[channel].Band].TimeOff = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->DutyCycleEnabled, Bands, CN779_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel, restore defaults + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= CN779_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Default channels don't accept all values + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + // Validate the datarate range for min: must be DR_0 + if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) + { + drInvalid = true; + } + // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + // We are not allowed to change the frequency + if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) + { + freqInvalid = true; + } + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( ChannelsMask, id, CN779_MAX_NB_CHANNELS ); +} + +void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionCN779.h b/libraries/LoRa_Node/src/mac/region/RegionCN779.h new file mode 100644 index 0000000..e1e695b --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionCN779.h @@ -0,0 +1,460 @@ +/*! + * \file RegionCN779.h + * + * \brief Region definition for CN779 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN779 Region CN779 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN779_H__ +#define __REGION_CN779_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define CN779_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define CN779_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN779_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN779_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN779_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN779_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define CN779_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN779_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN779_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define CN779_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN779_MIN_TX_POWER TX_POWER_5 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN779_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN779_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define CN779_DEFAULT_MAX_EIRP 12.15f + +/*! + * Default antenna gain + */ +#define CN779_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define CN779_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define CN779_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define CN779_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define CN779_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define CN779_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define CN779_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define CN779_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define CN779_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define CN779_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define CN779_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define CN779_ACK_TIMEOUT_RND 1000 + +/*! + * Verification of default datarate + */ +#if ( CN779_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define CN779_RX_WND_2_FREQ 786000000 + +/*! + * Second reception window channel datarate definition. + */ +#define CN779_RX_WND_2_DR DR_0 + +/*! + * LoRaMac maximum number of bands + */ +#define CN779_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC1 { 779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC2 { 779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC3 { 779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define CN779_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN779[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN779[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateCN779[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterCN779[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN779InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionCN779AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionCN779AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionCN779CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionCN779SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONCN779 */ + +#endif // __REGION_CN779_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionCommon.c b/libraries/LoRa_Node/src/mac/region/RegionCommon.c new file mode 100644 index 0000000..7c22bdf --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionCommon.c @@ -0,0 +1,235 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC common region implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ + +#include +#include +#include +#include + +//modified---------------------- +//#include "timer.h" +//#include "utilities.h" +//#include "LoRaMac.h" +#include "system/timer.h" +#include "boards/mcu/arduino/utilities.h" +#include "mac/LoRaMac.h" +//------------------------------ +#include "RegionCommon.h" + + + +#define BACKOFF_DC_1_HOUR 100 +#define BACKOFF_DC_10_HOURS 1000 +#define BACKOFF_DC_24_HOURS 10000 + + + +static uint8_t CountChannels( uint16_t mask, uint8_t nbBits ) +{ + uint8_t nbActiveBits = 0; + + for( uint8_t j = 0; j < nbBits; j++ ) + { + if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) + { + nbActiveBits++; + } + } + return nbActiveBits; +} + + + +uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime ) +{ + uint16_t dutyCycle = 0; + + if( elapsedTime < 3600000 ) + { + dutyCycle = BACKOFF_DC_1_HOUR; + } + else if( elapsedTime < ( 3600000 + 36000000 ) ) + { + dutyCycle = BACKOFF_DC_10_HOURS; + } + else + { + dutyCycle = BACKOFF_DC_24_HOURS; + } + return dutyCycle; +} + +bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels ) +{ + if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 ) + { + return false; + } + + for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) ) + {// Check datarate validity for enabled channels + if( RegionCommonValueInRange( dr, channels[i + j].DrRange.Fields.Min, channels[i + j].DrRange.Fields.Max ) == 1 ) + { + // At least 1 channel has been found we can return OK. + return true; + } + } + } + } + return false; +} + +uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max ) +{ + if( ( value >= min ) && ( value <= max ) ) + { + return 1; + } + return 0; +} + +bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ) +{ + uint8_t index = id / 16; + + if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) ) + { + return false; + } + + // Deactivate channel + channelsMask[index] &= ~( 1 << ( id % 16 ) ); + + return true; +} + +uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ) +{ + uint8_t nbChannels = 0; + + if( channelsMask == NULL ) + { + return 0; + } + + for( uint8_t i = startIdx; i < stopIdx; i++ ) + { + nbChannels += CountChannels( channelsMask[i], 16 ); + } + + return nbChannels; +} + +void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ) +{ + if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) ) + { + for( uint8_t i = 0; i < len; i++ ) + { + channelsMaskDest[i] = channelsMaskSrc[i]; + } + } +} + +void RegionCommonSetBandTxDone( Band_t* band, TimerTime_t lastTxDone ) +{ + band->LastTxDoneTime = lastTxDone; +} + +TimerTime_t RegionCommonUpdateBandTimeOff( bool dutyCycle, Band_t* bands, uint8_t nbBands ) +{ + TimerTime_t nextTxDelay = ( TimerTime_t )( -1 ); + + // Update bands Time OFF + for( uint8_t i = 0; i < nbBands; i++ ) + { + if( dutyCycle == true ) + { + if( bands[i].TimeOff <= TimerGetElapsedTime( bands[i].LastTxDoneTime ) ) + { + bands[i].TimeOff = 0; + } + if( bands[i].TimeOff != 0 ) + { + nextTxDelay = MIN( bands[i].TimeOff - TimerGetElapsedTime( bands[i].LastTxDoneTime ), + nextTxDelay ); + } + } + else + { + nextTxDelay = 0; + bands[i].TimeOff = 0; + } + } + return nextTxDelay; +} + +uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, LinkAdrParams_t* linkAdrParams ) +{ + uint8_t retIndex = 0; + + if( payload[0] == SRV_MAC_LINK_ADR_REQ ) + { + // Parse datarate and tx power + linkAdrParams->Datarate = payload[1]; + linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F; + linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F; + // Parse ChMask + linkAdrParams->ChMask = ( uint16_t )payload[2]; + linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8; + // Parse ChMaskCtrl and nbRep + linkAdrParams->NbRep = payload[4]; + linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07; + linkAdrParams->NbRep &= 0x0F; + + // LinkAdrReq has 4 bytes length + 1 byte CMD + retIndex = 5; + } + return retIndex; +} + +double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth ) +{ + return ( ( double )( 1 << phyDr ) / ( double )bandwidth ) * 1e3; +} + +double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr ) +{ + return ( 8.0 / ( double )phyDr ); // 1 symbol equals 1 byte +} + +void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset ) +{ + *windowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * minRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), minRxSymbols ); // Computed number of symbols + *windowOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( *windowTimeout * tSymbol ) / 2.0 ) - wakeUpTime ); +} + +int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain ) +{ + int8_t phyTxPower = 0; + + phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain ); + + return phyTxPower; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionCommon.h b/libraries/LoRa_Node/src/mac/region/RegionCommon.h new file mode 100644 index 0000000..d937b64 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionCommon.h @@ -0,0 +1,240 @@ +/*! + * \file RegionCommon.h + * + * \brief Region independent implementations which are common to all regions. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCOMMON Common region implementation + * Region independent implementations which are common to all regions. + * \{ + */ +#ifndef __REGIONCOMMON_H__ +#define __REGIONCOMMON_H__ + +typedef struct sLinkAdrParams +{ + /*! + * Number of repetitions. + */ + uint8_t NbRep; + /*! + * Datarate. + */ + int8_t Datarate; + /*! + * Tx power. + */ + int8_t TxPower; + /*! + * Channels mask control field. + */ + uint8_t ChMaskCtrl; + /*! + * Channels mask field. + */ + uint16_t ChMask; +}LinkAdrParams_t; + +/*! + * \brief Calculates the join duty cycle. + * This is a generic function and valid for all regions. + * + * \param [IN] elapsedTime Elapsed time since the start of the device. + * + * \retval Duty cycle restriction. + */ +uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime ); + +/*! + * \brief Verifies, if a value is in a given range. + * This is a generic function and valid for all regions. + * + * \param [IN] value Value to verify, if it is in range. + * + * \param [IN] min Minimum possible value. + * + * \param [IN] max Maximum possible value. + * + * \retval Returns 1 if the value is in range, otherwise 0. + */ +uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max ); + +/*! + * \brief Verifies, if a datarate is available on an active channel. + * This is a generic function and valid for all regions. + * + * \param [IN] nbChannels Number of channels. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] dr The datarate to verify. + * + * \param [IN] minDr Minimum datarate. + * + * \param [IN] maxDr Maximum datarate. + * + * \param [IN] channels The channels of the region. + * + * \retval Returns true if the datarate is supported, false if not. + */ +bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, + int8_t minDr, int8_t maxDr, ChannelParams_t* channels ); + +/*! + * \brief Disables a channel in a given channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] id The id of the channels mask to disable. + * + * \param [IN] maxChannels Maximum number of channels. + * + * \retval Returns true if the channel could be disabled, false if not. + */ +bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ); + +/*! + * \brief Counts the number of active channels in a given channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] startIdx Start index. + * + * \param [IN] stopIdx Stop index ( the channels of this index will not be counted ). + * + * \retval Returns the number of active channels. + */ +uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ); + +/*! + * \brief Copy a channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMaskDest The destination channels mask. + * + * \param [IN] channelsMaskSrc The source channels mask. + * + * \param [IN] len The index length to copy. + */ +void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ); + +/*! + * \brief Sets the last tx done property. + * This is a generic function and valid for all regions. + * + * \param [IN] band The band to be updated. + * + * \param [IN] lastTxDone The time of the last TX done. + */ +void RegionCommonSetBandTxDone( Band_t* band, TimerTime_t lastTxDone ); + +/*! + * \brief Updates the time-offs of the bands. + * This is a generic function and valid for all regions. + * + * \param [IN] dutyCycle Set to true, if the duty cycle is enabled. + * + * \param [IN] bands A pointer to the bands. + * + * \param [IN] nbBands The number of bands available. + * + * \retval Returns the time which must be waited to perform the next uplink. + */ +TimerTime_t RegionCommonUpdateBandTimeOff( bool dutyCycle, Band_t* bands, uint8_t nbBands ); + +/*! + * \brief Parses the parameter of an LinkAdrRequest. + * This is a generic function and valid for all regions. + * + * \param [IN] payload Pointer to the payload containing the MAC commands. The payload + * must contain the CMD identifier, following by the parameters. + * + * \param [OUT] parseLinkAdr The function fills the structure with the ADR parameters. + * + * \retval Returns the length of the ADR request, if a request was found. Otherwise, the + * function returns 0. + */ +uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, LinkAdrParams_t* parseLinkAdr ); + +/*! + * \brief Computes the symbol time for LoRa modulation. + * + * \param [IN] phyDr Physical datarate to use. + * + * \param [IN] bandwidth Bandwidth to use. + * + * \retval Returns the symbol time. + */ +double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth ); + +/*! + * \brief Computes the symbol time for FSK modulation. + * + * \param [IN] phyDr Physical datarate to use. + * + * \param [IN] bandwidth Bandwidth to use. + * + * \retval Returns the symbol time. + */ +double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr ); + +/*! + * \brief Computes the RX window timeout and the RX window offset. + * + * \param [IN] tSymbol Symbol timeout. + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms interval around RxOffset. + * + * \param [IN] wakeUpTime Wakeup time of the system. + * + * \param [OUT] windowTimeout RX window timeout. + * + * \param [OUT] windowOffset RX window time offset to be applied to the RX delay. + */ +void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset ); + +/*! + * \brief Computes the txPower, based on the max EIRP and the antenna gain. + * + * \param [IN] txPower TX power index. + * + * \param [IN] maxEirp Maximum EIRP. + * + * \param [IN] antennaGain Antenna gain. + * + * \retval Returns the physical TX power. + */ +int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain ); + +/*! \} defgroup REGIONCOMMON */ + +#endif // __REGIONCOMMON_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionEU433.c b/libraries/LoRa_Node/src/mac/region/RegionEU433.c new file mode 100644 index 0000000..7a9094b --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionEU433.c @@ -0,0 +1,1046 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region EU433 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionEU433.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[EU433_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[EU433_MAX_NB_BANDS] = +{ + EU433_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsEU433[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyTxFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 433175000 ) || ( freq > 434665000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < EU433_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( EU433_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = EU433_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = EU433_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = EU433_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU433_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = EU433_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterEU433[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = EU433_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = EU433_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = EU433_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = EU433_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = EU433_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = EU433_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = EU433_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( EU433_ACKTIMEOUT + randr( -EU433_ACK_TIMEOUT_RND, EU433_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = EU433_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = EU433_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = EU433_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU433_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = EU433_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = EU433_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionEU433InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) EU433_LC1; + Channels[1] = ( ChannelParams_t ) EU433_LC2; + Channels[2] = ( ChannelParams_t ) EU433_LC3; + + // Initialize the channels default mask + ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + ChannelsMask[0] |= ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return EU433_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = EU433_NUMB_DEFAULT_CHANNELS; i < 15; i+=3, chanIdx++ ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionEU433ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionEU433ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionEU433AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == EU433_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= EU433_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = EU433_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( EU433_ADR_ACK_LIMIT + EU433_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % EU433_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionEU433GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == EU433_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU433[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU433[datarate], BandwidthsEU433[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesEU433[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50e3, phyDr * 1e3, 0, 83.333e3, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterEU433[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateEU433[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesEU433[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25e3, bandwidth, phyDr * 1e3, 0, 5, false, true, 0, 0, false, 3e3 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + } + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < EU433_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( EU433_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( EU433_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = EU433_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Set the channels mask to a default value + memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); + // Update the channels mask + ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU433_MIN_RX1_DR_OFFSET, EU433_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionEU433ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionEU433ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionEU433AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + if( ( alternateDr->NbTrials % 48 ) == 0 ) + { + datarate = DR_0; + } + else if( ( alternateDr->NbTrials % 32 ) == 0 ) + { + datarate = DR_1; + } + else if( ( alternateDr->NbTrials % 24 ) == 0 ) + { + datarate = DR_2; + } + else if( ( alternateDr->NbTrials % 16 ) == 0 ) + { + datarate = DR_3; + } + else if( ( alternateDr->NbTrials % 8 ) == 0 ) + { + datarate = DR_4; + } + else + { + datarate = DR_5; + } + return datarate; +} + +void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + Bands[Channels[channel].Band].TimeOff = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->DutyCycleEnabled, Bands, EU433_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel, restore defaults + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= EU433_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Default channels don't accept all values + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + // Validate the datarate range for min: must be DR_0 + if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) + { + drInvalid = true; + } + // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + // We are not allowed to change the frequency + if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) + { + freqInvalid = true; + } + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( ChannelsMask, id, EU433_MAX_NB_CHANNELS ); +} + +void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionEU433.h b/libraries/LoRa_Node/src/mac/region/RegionEU433.h new file mode 100644 index 0000000..8e99f1e --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionEU433.h @@ -0,0 +1,461 @@ +/*! + * \file RegionEU433.h + * + * \brief Region definition for EU433 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONEU433 Region EU433 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_EU433_H__ +#define __REGION_EU433_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define EU433_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define EU433_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU433_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU433_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU433_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU433_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define EU433_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define EU433_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define EU433_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define EU433_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define EU433_MIN_TX_POWER TX_POWER_5 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define EU433_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define EU433_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define EU433_DEFAULT_MAX_EIRP 12.15f + +/*! + * Default antenna gain + */ +#define EU433_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define EU433_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define EU433_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define EU433_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU433_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define EU433_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define EU433_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define EU433_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define EU433_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define EU433_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define EU433_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define EU433_ACK_TIMEOUT_RND 1000 + +/*! + * Verification of default datarate + */ +#if ( EU433_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define EU433_RX_WND_2_FREQ 434665000 + +/*! + * Second reception window channel datarate definition. + */ +#define EU433_RX_WND_2_DR DR_0 + +/*! + * LoRaMac maximum number of bands + */ +#define EU433_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC1 { 433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC2 { 433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC3 { 433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define EU433_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesEU433[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsEU433[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateEU433[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterEU433[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionEU433InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionEU433AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionEU433AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionEU433CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionEU433SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONEU433 */ + +#endif // __REGION_EU433_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionEU868.c b/libraries/LoRa_Node/src/mac/region/RegionEU868.c new file mode 100644 index 0000000..10c9593 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionEU868.c @@ -0,0 +1,1078 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region EU868 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionEU868.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[EU868_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[EU868_MAX_NB_BANDS] = +{ + EU868_BAND0, + EU868_BAND1, + EU868_BAND2, + EU868_BAND3, + EU868_BAND4, +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsEU868[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyTxFreq( uint32_t freq, uint8_t *band ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Check frequency bands + if( ( freq >= 863000000 ) && ( freq < 865000000 ) ) + { + *band = 2; + } + else if( ( freq >= 865000000 ) && ( freq <= 868000000 ) ) + { + *band = 0; + } + else if( ( freq > 868000000 ) && ( freq <= 868600000 ) ) + { + *band = 1; + } + else if( ( freq >= 868700000 ) && ( freq <= 869200000 ) ) + { + *band = 2; + } + else if( ( freq >= 869400000 ) && ( freq <= 869650000 ) ) + { + *band = 3; + } + else if( ( freq >= 869700000 ) && ( freq <= 870000000 ) ) + { + *band = 4; + } + else + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < EU868_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( EU868_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = EU868_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = EU868_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = EU868_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU868_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = EU868_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterEU868[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = EU868_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = EU868_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = EU868_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = EU868_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = EU868_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = EU868_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = EU868_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( EU868_ACKTIMEOUT + randr( -EU868_ACK_TIMEOUT_RND, EU868_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = EU868_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = EU868_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = EU868_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU868_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = EU868_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = EU868_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionEU868InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) EU868_LC1; + Channels[1] = ( ChannelParams_t ) EU868_LC2; + Channels[2] = ( ChannelParams_t ) EU868_LC3; + + // Initialize the channels default mask + ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + ChannelsMask[0] |= ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return EU868_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = EU868_NUMB_DEFAULT_CHANNELS; i < 15; i+=3, chanIdx++ ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionEU868ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionEU868ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionEU868AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == EU868_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= EU868_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = EU868_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( EU868_ADR_ACK_LIMIT + EU868_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % EU868_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionEU868GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == EU868_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesEU868[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesEU868[datarate], BandwidthsEU868[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesEU868[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50e3, phyDr * 1e3, 0, 83.333e3, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterEU868[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateEU868[dr]; + } + + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesEU868[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25e3, bandwidth, phyDr * 1e3, 0, 5, false, true, 0, 0, false, 3e3 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + } + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < EU868_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( EU868_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( EU868_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = EU868_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Set the channels mask to a default value + memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); + // Update the channels mask + ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU868_MIN_RX1_DR_OFFSET, EU868_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionEU868ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionEU868ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + uint8_t band = 0; + + // Verify if the frequency is supported + if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionEU868AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + if( ( alternateDr->NbTrials % 48 ) == 0 ) + { + datarate = DR_0; + } + else if( ( alternateDr->NbTrials % 32 ) == 0 ) + { + datarate = DR_1; + } + else if( ( alternateDr->NbTrials % 24 ) == 0 ) + { + datarate = DR_2; + } + else if( ( alternateDr->NbTrials % 16 ) == 0 ) + { + datarate = DR_3; + } + else if( ( alternateDr->NbTrials % 8 ) == 0 ) + { + datarate = DR_4; + } + else + { + datarate = DR_5; + } + return datarate; +} + +void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + Bands[Channels[channel].Band].TimeOff = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->DutyCycleEnabled, Bands, EU868_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel, restore defaults + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= EU868_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Default channels don't accept all values + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + // Validate the datarate range for min: must be DR_0 + if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) + { + drInvalid = true; + } + // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + // We are not allowed to change the frequency + if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) + { + freqInvalid = true; + } + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( ChannelsMask, id, EU868_MAX_NB_CHANNELS ); +} + +void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionEU868.h b/libraries/LoRa_Node/src/mac/region/RegionEU868.h new file mode 100644 index 0000000..9fbe67f --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionEU868.h @@ -0,0 +1,482 @@ +/*! + * \file RegionEU868.h + * + * \brief Region definition for EU868 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONEU868 Region EU868 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_EU868_H__ +#define __REGION_EU868_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define EU868_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define EU868_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU868_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU868_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU868_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU868_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define EU868_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define EU868_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define EU868_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define EU868_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define EU868_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define EU868_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define EU868_DEFAULT_TX_POWER TX_POWER_1 + +/*! + * Default Max EIRP + */ +#define EU868_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define EU868_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define EU868_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define EU868_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define EU868_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU868_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define EU868_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define EU868_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define EU868_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define EU868_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define EU868_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define EU868_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define EU868_ACK_TIMEOUT_RND 1000 + +#if ( EU868_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define EU868_RX_WND_2_FREQ 869525000 + +/*! + * Second reception window channel datarate definition. + */ +#define EU868_RX_WND_2_DR DR_0 + +/*! + * Maximum number of bands + */ +#define EU868_MAX_NB_BANDS 5 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * Band 1 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * Band 2 definition + * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0 } // 0.1 % + +/*! + * Band 2 definition + * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0 } // 10.0 % + +/*! + * Band 2 definition + * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC1 { 868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC2 { 868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC3 { 868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define EU868_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesEU868[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsEU868[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateEU868[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterEU868[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionEU868InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionEU868AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionEU868AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionEU868CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionEU868SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONEU868 */ + +#endif // __REGION_EU868_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionIN865.c b/libraries/LoRa_Node/src/mac/region/RegionIN865.c new file mode 100644 index 0000000..1940938 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionIN865.c @@ -0,0 +1,1046 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region IN865 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionIN865.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[IN865_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[IN865_MAX_NB_BANDS] = +{ + IN865_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else if( dr == DR_7 ) + { + nextLowerDr = DR_5; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsIN865[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyTxFreq( uint32_t freq, uint8_t *band ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 865000000 ) || ( freq > 867000000 ) ) + { + return false; + } + return true; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < IN865_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( IN865_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = IN865_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = IN865_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = IN865_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, IN865_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = IN865_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterIN865[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = IN865_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = IN865_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = IN865_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = IN865_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = IN865_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = IN865_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = IN865_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( IN865_ACKTIMEOUT + randr( -IN865_ACK_TIMEOUT_RND, IN865_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = IN865_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = IN865_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = IN865_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = IN865_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = IN865_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionIN865InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) IN865_LC1; + Channels[1] = ( ChannelParams_t ) IN865_LC2; + Channels[2] = ( ChannelParams_t ) IN865_LC3; + + // Initialize the channels default mask + ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + ChannelsMask[0] |= ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return IN865_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; i < 15; i+=3, chanIdx++ ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionIN865ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionIN865ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionIN865AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == IN865_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= IN865_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = IN865_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( IN865_ADR_ACK_LIMIT + IN865_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % IN865_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionIN865GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == IN865_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesIN865[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[datarate], BandwidthsIN865[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesIN865[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50e3, phyDr * 1e3, 0, 83.333e3, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterIN865[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateIN865[dr]; + } + Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesIN865[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25e3, bandwidth, phyDr * 1e3, 0, 5, false, true, 0, 0, false, 3e3 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + } + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( IN865_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( IN865_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = IN865_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Set the channels mask to a default value + memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); + // Update the channels mask + ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionIN865ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionIN865ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + uint8_t band = 0; + + // Verify if the frequency is supported + if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionIN865AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + if( ( alternateDr->NbTrials % 48 ) == 0 ) + { + datarate = DR_0; + } + else if( ( alternateDr->NbTrials % 32 ) == 0 ) + { + datarate = DR_1; + } + else if( ( alternateDr->NbTrials % 24 ) == 0 ) + { + datarate = DR_2; + } + else if( ( alternateDr->NbTrials % 16 ) == 0 ) + { + datarate = DR_3; + } + else if( ( alternateDr->NbTrials % 8 ) == 0 ) + { + datarate = DR_4; + } + else + { + datarate = DR_5; + } + return datarate; +} + +void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + Bands[Channels[channel].Band].TimeOff = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->DutyCycleEnabled, Bands, IN865_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel, restore defaults + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= IN865_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Default channels don't accept all values + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + // Validate the datarate range for min: must be DR_0 + if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) + { + drInvalid = true; + } + // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + // We are not allowed to change the frequency + if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) + { + freqInvalid = true; + } + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( ChannelsMask, id, IN865_MAX_NB_CHANNELS ); +} + +void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + // Apply offset formula + return MIN( DR_5, MAX( DR_0, dr - EffectiveRx1DrOffsetIN865[drOffset] ) ); +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionIN865.h b/libraries/LoRa_Node/src/mac/region/RegionIN865.h new file mode 100644 index 0000000..2e51208 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionIN865.h @@ -0,0 +1,463 @@ +/*! + * \file RegionIN865.h + * + * \brief Region definition for IN865 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONIN865 Region IN865 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_IN865_H__ +#define __REGION_IN865_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define IN865_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define IN865_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Minimal datarate that can be used by the node + */ +#define IN865_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define IN865_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define IN865_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define IN865_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define IN865_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define IN865_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define IN865_MAX_RX1_DR_OFFSET 7 + +/*! + * Default Rx1 receive datarate offset + */ +#define IN865_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define IN865_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define IN865_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define IN865_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define IN865_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define IN865_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define IN865_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define IN865_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define IN865_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define IN865_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define IN865_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define IN865_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define IN865_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define IN865_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define IN865_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define IN865_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define IN865_ACK_TIMEOUT_RND 1000 + +#if ( IN865_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define IN865_RX_WND_2_FREQ 866550000 + +/*! + * Second reception window channel datarate definition. + */ +#define IN865_RX_WND_2_DR DR_2 + +/*! + * Maximum number of bands + */ +#define IN865_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 0 } // 100.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC1 { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC2 { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC3 { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define IN865_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesIN865[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsIN865[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 250e3, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateIN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterIN865[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * Effective datarate offsets for receive window 1. + */ +static const int8_t EffectiveRx1DrOffsetIN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionIN865InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionIN865AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionIN865AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionIN865CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionIN865SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONIN865 */ + +#endif // __REGION_IN865_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionKR920.c b/libraries/LoRa_Node/src/mac/region/RegionKR920.c new file mode 100644 index 0000000..ef12c66 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionKR920.c @@ -0,0 +1,1064 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region KR920 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionKR920.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[KR920_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[KR920_MAX_NB_BANDS] = +{ + KR920_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static int8_t GetMaxEIRP( uint32_t freq ) +{ + if( freq >= 922100000 ) + {// Limit to 14dBm + return KR920_DEFAULT_MAX_EIRP_HIGH; + } + // Limit to 10dBm + return KR920_DEFAULT_MAX_EIRP_LOW; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsKR920[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + return txPowerResult; +} + +static bool VerifyTxFreq( uint32_t freq ) +{ + uint32_t tmpFreq = freq; + + // Check radio driver support + if( Radio.CheckRfFrequency( tmpFreq ) == false ) + { + return false; + } + + // Verify if the frequency is valid. The frequency must be in a specified + // range and can be set to specific values. + if( ( tmpFreq >= 920900000 ) && ( tmpFreq <=923300000 ) ) + { + // Range ok, check for specific value + tmpFreq -= 920900000; + if( ( tmpFreq % 200000 ) == 0 ) + { + return true; + } + } + return false; +} + +static uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < KR920_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( joined == false ) + { + if( ( KR920_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = KR920_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = KR920_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = KR920_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, KR920_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = KR920_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterKR920[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = KR920_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = KR920_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = KR920_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = KR920_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = KR920_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = KR920_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = KR920_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( KR920_ACKTIMEOUT + randr( -KR920_ACK_TIMEOUT_RND, KR920_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = KR920_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = KR920_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = KR920_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = KR920_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + { + // We set the lower maximum EIRP as default value. + // The reason for this is, that the frequency may + // change during a channel selection for the next uplink. + // The value has to be recalculated in the TX configuration. + phyParam.fValue = KR920_DEFAULT_MAX_EIRP_LOW; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = KR920_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 48; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionKR920InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + Channels[0] = ( ChannelParams_t ) KR920_LC1; + Channels[1] = ( ChannelParams_t ) KR920_LC2; + Channels[2] = ( ChannelParams_t ) KR920_LC3; + + // Initialize the channels default mask + ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Restore channels default mask + ChannelsMask[0] |= ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return KR920_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 48 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = KR920_NUMB_DEFAULT_CHANNELS; i < 15; i+=3, chanIdx++ ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionKR920ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionKR920ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionKR920AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == KR920_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= KR920_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = KR920_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( KR920_ADR_ACK_LIMIT + KR920_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % KR920_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionKR920GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == KR920_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesKR920[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesKR920[datarate], BandwidthsKR920[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesKR920[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + maxPayload = MaxPayloadOfDatarateKR920[dr]; + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesKR920[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + float maxEIRP = GetMaxEIRP( Channels[txConfig->Channel].Frequency ); + int8_t phyTxPower = 0; + + // Take the minimum between the maxEIRP and txConfig->MaxEirp. + // The value of txConfig->MaxEirp could have changed during runtime, e.g. due to a MAC command. + maxEIRP = MIN( txConfig->MaxEirp, maxEIRP ); + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4e3 ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Get the time-on-air of the next tx frame + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + + *txPower = txConfig->TxPower; + return true; +} + +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < KR920_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Verify datarate + if( RegionCommonChanVerifyDr( KR920_MAX_NB_CHANNELS, &chMask, linkAdrParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( KR920_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = KR920_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Set the channels mask to a default value + memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); + // Update the channels mask + ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( Radio.CheckRfFrequency( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, KR920_MIN_RX1_DR_OFFSET, KR920_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionKR920ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionKR920ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + // Verify if the frequency is supported + if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionKR920AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + if( ( alternateDr->NbTrials % 48 ) == 0 ) + { + datarate = DR_0; + } + else if( ( alternateDr->NbTrials % 32 ) == 0 ) + { + datarate = DR_1; + } + else if( ( alternateDr->NbTrials % 24 ) == 0 ) + { + datarate = DR_2; + } + else if( ( alternateDr->NbTrials % 16 ) == 0 ) + { + datarate = DR_3; + } + else if( ( alternateDr->NbTrials % 8 ) == 0 ) + { + datarate = DR_4; + } + else + { + datarate = DR_5; + } + return datarate; +} + +void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + uint16_t joinDutyCycle = 0; + + // Reset time-off to initial value. + Bands[Channels[channel].Band].TimeOff = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply the most restricting duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + if( calcBackOff->DutyCycleEnabled == true ) + { + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * dutyCycle - calcBackOff->TxTimeOnAir; + } + } +} + +bool RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t channelNext = 0; + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + TimerTime_t carrierSenseTime = 0; + bool channelFree = false; + + if( RegionCommonCountChannels( ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + nextTxDelay = RegionCommonUpdateBandTimeOff( nextChanParams->DutyCycleEnabled, Bands, KR920_MAX_NB_BANDS ); + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, + ChannelsMask, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < KR920_MAX_NB_CHANNELS; i++ ) + { + channelNext = enabledChannels[j]; + j = ( j + 1 ) % nbEnabledChannels; + + carrierSenseTime = TimerGetCurrentTime( ); + channelFree = true; + + // Perform carrier sense for 6ms + while( TimerGetElapsedTime( carrierSenseTime ) < KR920_CARRIER_SENSE_TIME ) + { + if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, KR920_RSSI_FREE_TH ) == false ) + { + channelFree = false; + break; + } + } + + // If the channel is free, we can stop the LBT mechanism + if( channelFree == true ) + { + // Free channel found + *channel = channelNext; + *time = 0; + return true; + } + } + return false; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel, restore defaults + ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id >= KR920_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Default channels don't accept all values + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + // All datarates are supported + // We are not allowed to change the frequency + if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) + { + freqInvalid = true; + } + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( ChannelsMask, id, KR920_MAX_NB_CHANNELS ); +} + +void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionKR920.h b/libraries/LoRa_Node/src/mac/region/RegionKR920.h new file mode 100644 index 0000000..db1a157 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionKR920.h @@ -0,0 +1,473 @@ +/*! + * \file RegionKR920.h + * + * \brief Region definition for KR920 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONKR920 Region KR920 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_KR920_H__ +#define __REGION_KR920_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define KR920_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define KR920_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Minimal datarate that can be used by the node + */ +#define KR920_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define KR920_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define KR920_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define KR920_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ +#define KR920_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define KR920_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define KR920_MAX_RX1_DR_OFFSET 5 + +/*! + * Default Rx1 receive datarate offset + */ +#define KR920_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define KR920_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define KR920_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define KR920_DEFAULT_TX_POWER TX_POWER_1 + +/*! + * Default Max EIRP for frequency 920.9 MHz - 921.9 MHz + */ +#define KR920_DEFAULT_MAX_EIRP_LOW 10.0f + +/*! + * Default Max EIRP for frequency 922.1 MHz - 923.3 MHz + */ +#define KR920_DEFAULT_MAX_EIRP_HIGH 14.0f + +/*! + * Default antenna gain + */ +#define KR920_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * ADR Ack limit + */ +#define KR920_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define KR920_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define KR920_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define KR920_MAX_RX_WINDOW 4000 + +/*! + * Receive delay 1 + */ +#define KR920_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define KR920_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define KR920_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define KR920_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define KR920_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define KR920_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define KR920_ACK_TIMEOUT_RND 1000 + +#if ( KR920_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define KR920_RX_WND_2_FREQ 921900000 + +/*! + * Second reception window channel datarate definition. + */ +#define KR920_RX_WND_2_DR DR_0 + +/*! + * Maximum number of bands + */ +#define KR920_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 0 } // 100.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC1 { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC2 { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC3 { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define KR920_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * RSSI threshold for a free channel [dBm] + */ +#define KR920_RSSI_FREE_TH -65 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define KR920_CARRIER_SENSE_TIME 6 + +/*! + * Data rates table definition + */ +static const uint8_t DataratesKR920[] = { 12, 11, 10, 9, 8, 7 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsKR920[] = { 125e3, 125e3, 125e3, 125e3, 125e3, 125e3 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with and without a repeater. + */ +static const uint8_t MaxPayloadOfDatarateKR920[] = { 51, 51, 51, 115, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterKR920[] = { 51, 51, 51, 115, 222, 222 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionKR920InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionKR920AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionKR920AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionKR920CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionKR920SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONKR920 */ + +#endif // __REGION_KR920_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionUS915-Hybrid.c b/libraries/LoRa_Node/src/mac/region/RegionUS915-Hybrid.c new file mode 100644 index 0000000..bdf3565 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionUS915-Hybrid.c @@ -0,0 +1,961 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region US915 Hybrid implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionUS915-Hybrid.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[US915_HYBRID_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[US915_HYBRID_MAX_NB_BANDS] = +{ + US915_HYBRID_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels remaining + */ +static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsUS915_HYBRID[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static void ReenableChannels( uint16_t mask, uint16_t* channelsMask ) +{ + uint16_t blockMask = mask; + + for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 ) + { + channelsMask[i] = 0; + if( ( blockMask & ( 1 << j ) ) != 0 ) + { + channelsMask[i] |= 0x00FF; + } + if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 ) + { + channelsMask[i] |= 0xFF00; + } + } + channelsMask[4] = blockMask; + channelsMask[5] = 0x0000; +} + +static uint8_t CountBits( uint16_t mask, uint8_t nbBits ) +{ + uint8_t nbActiveBits = 0; + + for( uint8_t j = 0; j < nbBits; j++ ) + { + if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) + { + nbActiveBits++; + } + } + return nbActiveBits; +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + if( datarate == DR_4 ) + {// Limit tx power to max 26dBm + txPowerResult = MAX( txPower, TX_POWER_2 ); + } + else + { + if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 ) + {// Limit tx power to max 21dBm + txPowerResult = MAX( txPower, TX_POWER_5 ); + } + } + return txPowerResult; +} + +static bool ValidateChannelsMask( uint16_t* channelsMask ) +{ + bool chanMaskState = false; + uint16_t block1 = 0; + uint16_t block2 = 0; + uint8_t index = 0; + uint16_t channelsMaskCpy[6]; + + // Copy channels mask to not change the input + for( uint8_t i = 0; i < 4; i++ ) + { + channelsMaskCpy[i] = channelsMask[i]; + } + + for( uint8_t i = 0; i < 4; i++ ) + { + block1 = channelsMaskCpy[i] & 0x00FF; + block2 = channelsMaskCpy[i] & 0xFF00; + + if( CountBits( block1, 16 ) > 5 ) + { + channelsMaskCpy[i] &= block1; + channelsMaskCpy[4] = 1 << ( i * 2 ); + chanMaskState = true; + index = i; + break; + } + else if( CountBits( block2, 16 ) > 5 ) + { + channelsMaskCpy[i] &= block2; + channelsMaskCpy[4] = 1 << ( i * 2 + 1 ); + chanMaskState = true; + index = i; + break; + } + } + + // Do only change the channel mask, if we have found a valid block. + if( chanMaskState == true ) + { + // Copy channels mask back again + for( uint8_t i = 0; i < 4; i++ ) + { + channelsMask[i] = channelsMaskCpy[i]; + + if( i != index ) + { + channelsMask[i] = 0; + } + } + channelsMask[4] = channelsMaskCpy[4]; + } + return chanMaskState; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < US915_HYBRID_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionUS915HybridGetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = US915_HYBRID_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = US915_HYBRID_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = US915_HYBRID_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_HYBRID_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = US915_HYBRID_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateUS915_HYBRID[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterUS915_HYBRID[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = US915_HYBRID_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = US915_HYBRID_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = US915_HYBRID_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = US915_HYBRID_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = US915_HYBRID_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( US915_HYBRID_ACKTIMEOUT + randr( -US915_HYBRID_ACK_TIMEOUT_RND, US915_HYBRID_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = US915_HYBRID_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = US915_HYBRID_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = US915_HYBRID_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = US915_HYBRID_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = 0; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 2; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionUS915HybridSetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionUS915HybridInitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < US915_HYBRID_MAX_NB_CHANNELS - 8; i++ ) + { + Channels[i].Frequency = 902.3e6 + i * 200e3; + Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + Channels[i].Band = 0; + } + // 500 kHz channels + for( uint8_t i = US915_HYBRID_MAX_NB_CHANNELS - 8; i < US915_HYBRID_MAX_NB_CHANNELS; i++ ) + { + Channels[i].Frequency = 903.0e6 + ( i - ( US915_HYBRID_MAX_NB_CHANNELS - 8 ) ) * 1.6e6; + Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + Channels[i].Band = 0; + } + + // ChannelsMask + ChannelsDefaultMask[0] = 0x00FF; + ChannelsDefaultMask[1] = 0x0000; + ChannelsDefaultMask[2] = 0x0000; + ChannelsDefaultMask[3] = 0x0000; + ChannelsDefaultMask[4] = 0x0001; + ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); + break; + } + case INIT_TYPE_RESTORE: + { + ReenableChannels( ChannelsDefaultMask[4], ChannelsMask ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + } + default: + { + break; + } + } +} + +bool RegionUS915HybridVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_HYBRID_TX_MIN_DATARATE, US915_HYBRID_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, US915_HYBRID_MAX_TX_POWER, US915_HYBRID_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return US915_HYBRID_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 2 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionUS915HybridApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + return; +} + +bool RegionUS915HybridChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 ); + + // Check the number of active channels + if( ( nbChannels < 2 ) && + ( nbChannels > 0 ) ) + { + return false; + } + + // Validate the channels mask + if( ValidateChannelsMask( chanMaskSet->ChannelsMaskIn ) == false ) + { + return false; + } + + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionUS915HybridAdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == US915_HYBRID_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= US915_HYBRID_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = US915_HYBRID_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( US915_HYBRID_ADR_ACK_LIMIT + US915_HYBRID_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % US915_HYBRID_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionUS915HybridGetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == US915_HYBRID_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ReenableChannels( ChannelsMask[4], ChannelsMask ); + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionUS915HybridComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesUS915_HYBRID[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915_HYBRID[datarate], BandwidthsUS915_HYBRID[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = US915_HYBRID_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesUS915_HYBRID[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterUS915_HYBRID[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateUS915_HYBRID[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionUS915HybridTxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesUS915_HYBRID[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 ); + + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionUS915HybridLinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + if( ValidateChannelsMask( channelsMask ) == false ) + { + status &= 0xFE; // Channel mask KO + } + + // Verify datarate + if( RegionCommonChanVerifyDr( US915_HYBRID_MAX_NB_CHANNELS, channelsMask, linkAdrParams.Datarate, US915_HYBRID_TX_MIN_DATARATE, US915_HYBRID_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, US915_HYBRID_MAX_TX_POWER, US915_HYBRID_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( US915_HYBRID_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = US915_HYBRID_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Copy Mask + RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 ); + + ChannelsMaskRemaining[0] &= ChannelsMask[0]; + ChannelsMaskRemaining[1] &= ChannelsMask[1]; + ChannelsMaskRemaining[2] &= ChannelsMask[2]; + ChannelsMaskRemaining[3] &= ChannelsMask[3]; + ChannelsMaskRemaining[4] = ChannelsMask[4]; + ChannelsMaskRemaining[5] = ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionUS915HybridRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + uint32_t freq = rxParamSetupReq->Frequency; + + // Verify radio frequency + if( ( Radio.CheckRfFrequency( freq ) == false ) || + ( freq < US915_HYBRID_FIRST_RX1_CHANNEL ) || + ( freq > US915_HYBRID_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) US915_HYBRID_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_HYBRID_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_HYBRID_MIN_RX1_DR_OFFSET, US915_HYBRID_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionUS915HybridNewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionUS915HybridTxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionUS915HybridDlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionUS915HybridAlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + // Re-enable 500 kHz default channels + ReenableChannels( ChannelsMask[4], ChannelsMask ); + + if( ( alternateDr->NbTrials & 0x01 ) == 0x01 ) + { + datarate = DR_4; + } + else + { + datarate = DR_0; + } + return datarate; +} + +void RegionUS915HybridCalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t joinDutyCycle = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * joinDutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + Bands[Channels[channel].Band].TimeOff = 0; + } +} + +bool RegionUS915HybridNextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[US915_HYBRID_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4 ); + } + // Check other channels + if( nextChanParams->Datarate >= DR_4 ) + { + if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) + { + ChannelsMaskRemaining[4] = ChannelsMask[4]; + } + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + ChannelsMaskRemaining, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + // Disable the channel in the mask + RegionCommonChanDisable( ChannelsMaskRemaining, *channel, US915_HYBRID_MAX_NB_CHANNELS - 8 ); + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionUS915HybridChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionUS915HybridChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionUS915HybridSetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionUS915HybridApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsUS915_HYBRID[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionUS915-Hybrid.h b/libraries/LoRa_Node/src/mac/region/RegionUS915-Hybrid.h new file mode 100644 index 0000000..3c61775 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionUS915-Hybrid.h @@ -0,0 +1,448 @@ +/*! + * \file RegionUS915Hybrid-Hybrid.h + * + * \brief Region definition for US915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONUS915HYB Region US915 in hybrid mode + * This is a hybrid implementation for US915, supporting 16 uplink channels only. + * \{ + */ +#ifndef __REGION_US915_HYBRID_H__ +#define __REGION_US915_HYBRID_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define US915_HYBRID_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_HYBRID_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_HYBRID_TX_MAX_DATARATE DR_4 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_HYBRID_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_HYBRID_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define US915_HYBRID_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define US915_HYBRID_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define US915_HYBRID_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define US915_HYBRID_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define US915_HYBRID_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define US915_HYBRID_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define US915_HYBRID_DEFAULT_TX_POWER TX_POWER_5 + +/*! + * Default Max ERP + */ +#define US915_HYBRID_DEFAULT_MAX_ERP 30.0f + +/*! + * ADR Ack limit + */ +#define US915_HYBRID_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define US915_HYBRID_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define US915_HYBRID_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define US915_HYBRID_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define US915_HYBRID_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define US915_HYBRID_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define US915_HYBRID_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define US915_HYBRID_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define US915_HYBRID_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define US915_HYBRID_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define US915_HYBRID_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define US915_HYBRID_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define US915_HYBRID_RX_WND_2_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define US915_HYBRID_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define US915_HYBRID_BAND0 { 1, US915_HYBRID_MAX_TX_POWER, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define US915_HYBRID_FIRST_RX1_CHANNEL ( (uint32_t) 923.3e6 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define US915_HYBRID_LAST_RX1_CHANNEL ( (uint32_t) 927.5e6 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define US915_HYBRID_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600e3 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesUS915_HYBRID[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsUS915_HYBRID[] = { 125e3, 125e3, 125e3, 125e3, 500e3, 0, 0, 0, 500e3, 500e3, 500e3, 500e3, 500e3, 500e3, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsUS915_HYBRID[5][4] = +{ + { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 + { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 + { DR_12, DR_11, DR_10, DR_9 }, // DR_2 + { DR_13, DR_12, DR_11, DR_10 }, // DR_3 + { DR_13, DR_13, DR_12, DR_11 }, // DR_4 +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionUS915HybridGetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionUS915HybridSetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionUS915HybridInitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionUS915HybridVerify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionUS915HybridApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionUS915HybridChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionUS915HybridAdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionUS915HybridComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915HybridTxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915HybridLinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915HybridRxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915HybridNewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionUS915HybridTxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915HybridDlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionUS915HybridAlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionUS915HybridCalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionUS915HybridNextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionUS915HybridChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionUS915HybridChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionUS915HybridSetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionUS915HybridApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONUS915HYB */ + +#endif // __REGION_US915_HYBRID_H__ diff --git a/libraries/LoRa_Node/src/mac/region/RegionUS915.c b/libraries/LoRa_Node/src/mac/region/RegionUS915.c new file mode 100644 index 0000000..c672202 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionUS915.c @@ -0,0 +1,868 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC region US915 implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include +#include + +//modified---------------------- +//#include "board.h" +//#include "LoRaMac.h" +#include "boards/arduino/board.h" +#include "mac/LoRaMac.h" + +//#include "utilities.h" +#include "boards/mcu/arduino/utilities.h" +//------------------------------ + +#include "Region.h" +#include "RegionCommon.h" +#include "RegionUS915.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// Global attributes +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[US915_MAX_NB_CHANNELS]; + +/*! + * LoRaMac bands + */ +static Band_t Bands[US915_MAX_NB_BANDS] = +{ + US915_BAND0 +}; + +/*! + * LoRaMac channels mask + */ +static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels remaining + */ +static uint16_t ChannelsMaskRemaining[CHANNELS_MASK_SIZE]; + +/*! + * LoRaMac channels default mask + */ +static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE]; + +// Static functions +static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +{ + uint8_t nextLowerDr = 0; + + if( dr == minDr ) + { + nextLowerDr = minDr; + } + else + { + nextLowerDr = dr - 1; + } + return nextLowerDr; +} + +static uint32_t GetBandwidth( uint32_t drIndex ) +{ + switch( BandwidthsUS915[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX( txPower, maxBandTxPower ); + + if( datarate == DR_4 ) + {// Limit tx power to max 26dBm + txPowerResult = MAX( txPower, TX_POWER_2 ); + } + else + { + if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 ) + {// Limit tx power to max 21dBm + txPowerResult = MAX( txPower, TX_POWER_5 ); + } + } + return txPowerResult; +} + +static uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTransmission = 0; + + for( uint8_t i = 0, k = 0; i < US915_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( RegionCommonValueInRange( datarate, channels[i + j].DrRange.Fields.Min, + channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( bands[channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTransmission++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + + *delayTx = delayTransmission; + return nbEnabledChannels; +} + +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = US915_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = US915_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = US915_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_TX_MIN_DATARATE ); + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = US915_DEFAULT_TX_POWER; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate]; + break; + } + case PHY_MAX_PAYLOAD_REPEATER: + { + phyParam.Value = MaxPayloadOfDatarateRepeaterUS915[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = US915_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = US915_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = US915_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = US915_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = US915_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = US915_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_MAX_FCNT_GAP: + { + phyParam.Value = US915_MAX_FCNT_GAP; + break; + } + case PHY_ACK_TIMEOUT: + { + phyParam.Value = ( US915_ACKTIMEOUT + randr( -US915_ACK_TIMEOUT_RND, US915_ACK_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = US915_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = US915_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = US915_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = US915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = 0; + break; + } + case PHY_DEF_MAX_EIRP: + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = 0; + break; + } + case PHY_NB_JOIN_TRIALS: + case PHY_DEF_NB_JOIN_TRIALS: + { + phyParam.Value = 2; + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); +} + +void RegionUS915InitDefaults( InitType_t type ) +{ + switch( type ) + { + case INIT_TYPE_INIT: + { + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ ) + { + Channels[i].Frequency = 902.3e6 + i * 200e3; + Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + Channels[i].Band = 0; + } + // 500 kHz channels + for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ ) + { + Channels[i].Frequency = 903.0e6 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1.6e6; + Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + Channels[i].Band = 0; + } + + // ChannelsMask + ChannelsDefaultMask[0] = 0xFFFF; + ChannelsDefaultMask[1] = 0xFFFF; + ChannelsDefaultMask[2] = 0xFFFF; + ChannelsDefaultMask[3] = 0xFFFF; + ChannelsDefaultMask[4] = 0x00FF; + ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); + break; + } + case INIT_TYPE_RESTORE: + { + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return US915_DUTY_CYCLE_ENABLED; + } + case PHY_NB_JOIN_TRIALS: + { + if( verify->NbJoinTrials < 2 ) + { + return false; + } + break; + } + default: + return false; + } + return true; +} + +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + return; +} + +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 ); + + // Check the number of active channels + if( ( nbChannels < 2 ) && + ( nbChannels > 0 ) ) + { + return false; + } + + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); + break; + } + default: + return false; + } + return true; +} + +bool RegionUS915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + if( datarate == US915_TX_MIN_DATARATE ) + { + *adrAckCounter = 0; + adrAckReq = false; + } + else + { + if( adrNext->AdrAckCounter >= US915_ADR_ACK_LIMIT ) + { + adrAckReq = true; + txPower = US915_MAX_TX_POWER; + } + else + { + adrAckReq = false; + } + if( adrNext->AdrAckCounter >= ( US915_ADR_ACK_LIMIT + US915_ADR_ACK_DELAY ) ) + { + if( ( adrNext->AdrAckCounter % US915_ADR_ACK_DELAY ) == 1 ) + { + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionUS915GetPhyParam( &getPhy ); + datarate = phyParam.Value; + + if( datarate == US915_TX_MIN_DATARATE ) + { + if( adrNext->UpdateChanMask == true ) + { + // Re-enable default channels + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0x00FF; + ChannelsMask[5] = 0x0000; + } + } + } + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + return adrAckReq; +} + +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + double tSymbol = 0.0; + + rxConfigParams->Datarate = datarate; + rxConfigParams->Bandwidth = GetBandwidth( datarate ); + + if( datarate == DR_7 ) + { // FSK + tSymbol = RegionCommonComputeSymbolTimeFsk( DataratesUS915[datarate] ); + } + else + { // LoRa + tSymbol = RegionCommonComputeSymbolTimeLoRa( DataratesUS915[datarate], BandwidthsUS915[datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + uint8_t maxPayload = 0; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->Window == 0 ) + { + // Apply window 1 frequency + frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesUS915[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + if( rxConfig->RepeaterSupport == true ) + { + maxPayload = MaxPayloadOfDatarateRepeaterUS915[dr]; + } + else + { + maxPayload = MaxPayloadOfDatarateUS915[dr]; + } + Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesUS915[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); + uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + Radio.SetChannel( Channels[txConfig->Channel].Frequency ); + + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3e3 ); + + *txTimeOnAir = Radio.TimeOnAir( MODEM_LORA, txConfig->PktLen ); + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + LinkAdrParams_t linkAdrParams; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + // Verify datarate + if( RegionCommonChanVerifyDr( US915_MAX_NB_CHANNELS, channelsMask, linkAdrParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE, Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( RegionCommonValueInRange( linkAdrParams.TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( US915_MAX_TX_POWER > linkAdrParams.TxPower ) + { // Apply maximum TX power. Accept TX power. + linkAdrParams.TxPower = US915_MAX_TX_POWER; + } + else + { + status &= 0xFB; // TxPower KO + } + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + if( linkAdrParams.NbRep == 0 ) + { // Value of 0 is not allowed, revert to default. + linkAdrParams.NbRep = 1; + } + + // Copy Mask + RegionCommonChanMaskCopy( ChannelsMask, channelsMask, 6 ); + + ChannelsMaskRemaining[0] &= ChannelsMask[0]; + ChannelsMaskRemaining[1] &= ChannelsMask[1]; + ChannelsMaskRemaining[2] &= ChannelsMask[2]; + ChannelsMaskRemaining[3] &= ChannelsMask[3]; + ChannelsMaskRemaining[4] = ChannelsMask[4]; + ChannelsMaskRemaining[5] = ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + uint32_t freq = rxParamSetupReq->Frequency; + + // Verify radio frequency + if( ( Radio.CheckRfFrequency( freq ) == false ) || + ( freq < US915_FIRST_RX1_CHANNEL ) || + ( freq > US915_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Datarate and frequency KO + return 0; +} + +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + return -1; +} + +uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + return 0; +} + +int8_t RegionUS915AlternateDr( AlternateDrParams_t* alternateDr ) +{ + int8_t datarate = 0; + + // Re-enable 500 kHz default channels + ChannelsMask[4] = 0x00FF; + + if( ( alternateDr->NbTrials & 0x01 ) == 0x01 ) + { + datarate = DR_4; + } + else + { + datarate = DR_0; + } + return datarate; +} + +void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff ) +{ + uint8_t channel = calcBackOff->Channel; + uint16_t joinDutyCycle = 0; + + if( calcBackOff->Joined == false ) + { + // Get the join duty cycle + joinDutyCycle = RegionCommonGetJoinDc( calcBackOff->ElapsedTime ); + // Apply band time-off. + Bands[Channels[channel].Band].TimeOff = calcBackOff->TxTimeOnAir * joinDutyCycle - calcBackOff->TxTimeOnAir; + } + else + { + Bands[Channels[channel].Band].TimeOff = 0; + } +} + +bool RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 }; + TimerTime_t nextTxDelay = 0; + + // Count 125kHz channels + if( RegionCommonCountChannels( ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 4 ); + } + // Check other channels + if( nextChanParams->Datarate >= DR_4 ) + { + if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) + { + ChannelsMaskRemaining[4] = ChannelsMask[4]; + } + } + + if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Search how many channels are enabled + nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, + ChannelsMaskRemaining, Channels, + Bands, enabledChannels, &delayTx ); + } + else + { + delayTx++; + nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + } + + if( nbEnabledChannels > 0 ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + // Disable the channel in the mask + RegionCommonChanDisable( ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS - 8 ); + + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel + *time = 0; + return false; + } +} + +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave ) +{ + int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t phyTxPower = 0; + uint32_t frequency = Channels[continuousWave->Channel].Frequency; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout ); +} + +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsUS915[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} diff --git a/libraries/LoRa_Node/src/mac/region/RegionUS915.h b/libraries/LoRa_Node/src/mac/region/RegionUS915.h new file mode 100644 index 0000000..65bde02 --- /dev/null +++ b/libraries/LoRa_Node/src/mac/region/RegionUS915.h @@ -0,0 +1,448 @@ +/*! + * \file RegionUS915.h + * + * \brief Region definition for US915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONUS915 Region US915 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_US915_H__ +#define __REGION_US915_H__ + +/*! + * LoRaMac maximum number of channels + */ +#define US915_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_TX_MAX_DATARATE DR_4 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define US915_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define US915_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define US915_MAX_RX1_DR_OFFSET 3 + +/*! + * Default Rx1 receive datarate offset + */ +#define US915_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define US915_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define US915_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define US915_DEFAULT_TX_POWER TX_POWER_5 + +/*! + * Default Max ERP + */ +#define US915_DEFAULT_MAX_ERP 30.0f + +/*! + * ADR Ack limit + */ +#define US915_ADR_ACK_LIMIT 64 + +/*! + * ADR Ack delay + */ +#define US915_ADR_ACK_DELAY 32 + +/*! + * Enabled or disabled the duty cycle + */ +#define US915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define US915_MAX_RX_WINDOW 3000 + +/*! + * Receive delay 1 + */ +#define US915_RECEIVE_DELAY1 1000 + +/*! + * Receive delay 2 + */ +#define US915_RECEIVE_DELAY2 2000 + +/*! + * Join accept delay 1 + */ +#define US915_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay 2 + */ +#define US915_JOIN_ACCEPT_DELAY2 6000 + +/*! + * Maximum frame counter gap + */ +#define US915_MAX_FCNT_GAP 16384 + +/*! + * Ack timeout + */ +#define US915_ACKTIMEOUT 2000 + +/*! + * Random ack timeout limits + */ +#define US915_ACK_TIMEOUT_RND 1000 + +/*! + * Second reception window channel frequency definition. + */ +#define US915_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define US915_RX_WND_2_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define US915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + */ +#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define US915_FIRST_RX1_CHANNEL ( (uint32_t) 923.3e6 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define US915_LAST_RX1_CHANNEL ( (uint32_t) 927.5e6 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define US915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600e3 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesUS915[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsUS915[] = { 125e3, 125e3, 125e3, 125e3, 500e3, 0, 0, 0, 500e3, 500e3, 500e3, 500e3, 500e3, 500e3, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsUS915[5][4] = +{ + { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 + { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 + { DR_12, DR_11, DR_10, DR_9 }, // DR_2 + { DR_13, DR_12, DR_11, DR_10 }, // DR_3 + { DR_13, DR_13, DR_12, DR_11 }, // DR_4 +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateRepeaterUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionUS915InitDefaults( InitType_t type ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool RegionUS915AdrNext( AdrNextParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, uint32_t* adrAckCounter ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/* + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] alternateDr Pointer to the function parameters. + * + * \retval Datarate to apply. + */ +int8_t RegionUS915AlternateDr( AlternateDrParams_t* alternateDr ); + +/*! + * \brief Calculates the back-off time. + * + * \param [IN] calcBackOff Pointer to the function parameters. + */ +void RegionUS915CalcBackOff( CalcBackOffParams_t* calcBackOff ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +bool RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Sets the radio into continuous wave mode. + * + * \param [IN] continuousWave Pointer to the function parameters. + */ +void RegionUS915SetContinuousWave( ContinuousWaveParams_t* continuousWave ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! \} defgroup REGIONUS915 */ + +#endif // __REGION_US915_H__ diff --git a/libraries/LoRa_Node/src/radio/radio.h b/libraries/LoRa_Node/src/radio/radio.h new file mode 100644 index 0000000..c175c32 --- /dev/null +++ b/libraries/LoRa_Node/src/radio/radio.h @@ -0,0 +1,334 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic radio driver definition + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __RADIO_H__ +#define __RADIO_H__ + +/*! + * Radio driver supported modems + */ +typedef enum +{ + MODEM_FSK = 0, + MODEM_LORA, +}RadioModems_t; + +/*! + * Radio driver internal state machine states definition + */ +typedef enum +{ + RF_IDLE = 0, + RF_RX_RUNNING, + RF_TX_RUNNING, + RF_CAD, +}RadioState_t; + +/*! + * \brief Radio driver callback functions + */ +typedef struct +{ + /*! + * \brief Tx Done callback prototype. + */ + void ( *TxDone )( void ); + /*! + * \brief Tx Timeout callback prototype. + */ + void ( *TxTimeout )( void ); + /*! + * \brief Rx Done callback prototype. + * + * \param [IN] payload Received buffer pointer + * \param [IN] size Received buffer size + * \param [IN] rssi RSSI value computed while receiving the frame [dBm] + * \param [IN] snr Raw SNR value given by the radio hardware + * FSK : N/A ( set to 0 ) + * LoRa: SNR value in dB + */ + void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); + /*! + * \brief Rx Timeout callback prototype. + */ + void ( *RxTimeout )( void ); + /*! + * \brief Rx Error callback prototype. + */ + void ( *RxError )( void ); + /*! + * \brief FHSS Change Channel callback prototype. + * + * \param [IN] currentChannel Index number of the current channel + */ + void ( *FhssChangeChannel )( uint8_t currentChannel ); + + /*! + * \brief CAD Done callback prototype. + * + * \param [IN] channelDetected Channel Activity detected during the CAD + */ + void ( *CadDone ) ( bool channelActivityDetected ); +}RadioEvents_t; + +/*! + * \brief Radio driver definition + */ +struct Radio_s +{ + /*! + * \brief Initializes the radio + * + * \param [IN] events Structure containing the driver callback functions + */ + void ( *Init )( RadioEvents_t *events ); + /*! + * Return current radio status + * + * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ + RadioState_t ( *GetStatus )( void ); + /*! + * \brief Configures the radio with the given modem + * + * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ + void ( *SetModem )( RadioModems_t modem ); + /*! + * \brief Sets the channel frequency + * + * \param [IN] freq Channel RF frequency + */ + void ( *SetChannel )( uint32_t freq ); + /*! + * \brief Sets the channels configuration + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] freq Channel RF frequency + * \param [IN] rssiThresh RSSI threshold + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ + bool ( *IsChannelFree )( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ); + /*! + * \brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either Radio.SetRxConfig or + * Radio.SetTxConfig functions must be called. + * + * \retval randomValue 32 bits random value + */ + uint32_t ( *Random )( void ); + /*! + * \brief Sets the reception parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout in number of bytes + * LoRa: timeout in symbols + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] HopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ + void ( *SetRxConfig )( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool FreqHopOn, uint8_t HopPeriod, + bool iqInverted, bool rxContinuous ); + /*! + * \brief Sets the transmission parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] power Sets the output power [dBm] + * \param [IN] fdev Sets the frequency deviation (FSK only) + * FSK : [Hz] + * LoRa: 0 + * \param [IN] bandwidth Sets the bandwidth (LoRa only) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] HopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] timeout Transmission timeout [ms] + */ + void ( *SetTxConfig )( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool FreqHopOn, + uint8_t HopPeriod, bool iqInverted, uint32_t timeout ); + /*! + * \brief Checks if the given RF frequency is supported by the hardware + * + * \param [IN] frequency RF frequency to be checked + * \retval isSupported [true: supported, false: unsupported] + */ + bool ( *CheckRfFrequency )( uint32_t frequency ); + /*! + * \brief Computes the packet time on air in ms for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] pktLen Packet payload length + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ + uint32_t ( *TimeOnAir )( RadioModems_t modem, uint8_t pktLen ); + /*! + * \brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * \param [IN]: buffer Buffer pointer + * \param [IN]: size Buffer size + */ + void ( *Send )( uint8_t *buffer, uint8_t size ); + /*! + * \brief Sets the radio in sleep mode + */ + void ( *Sleep )( void ); + /*! + * \brief Sets the radio in standby mode + */ + void ( *Standby )( void ); + /*! + * \brief Sets the radio in reception mode for the given time + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ + void ( *Rx )( uint32_t timeout ); + /*! + * \brief Start a Channel Activity Detection + */ + void ( *StartCad )( void ); + /*! + * \brief Sets the radio in continuous wave transmission mode + * + * \param [IN]: freq Channel RF frequency + * \param [IN]: power Sets the output power [dBm] + * \param [IN]: time Transmission mode timeout [s] + */ + void ( *SetTxContinuousWave )( uint32_t freq, int8_t power, uint16_t time ); + /*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ + int16_t ( *Rssi )( RadioModems_t modem ); + /*! + * \brief Writes the radio register at the specified address + * + * \param [IN]: addr Register address + * \param [IN]: data New register value + */ + void ( *Write )( uint8_t addr, uint8_t data ); + /*! + * \brief Reads the radio register at the specified address + * + * \param [IN]: addr Register address + * \retval data Register value + */ + uint8_t ( *Read )( uint8_t addr ); + /*! + * \brief Writes multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [IN] buffer Buffer containing the new register's values + * \param [IN] size Number of registers to be written + */ + void ( *WriteBuffer )( uint8_t addr, uint8_t *buffer, uint8_t size ); + /*! + * \brief Reads multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [OUT] buffer Buffer where to copy the registers data + * \param [IN] size Number of registers to be read + */ + void ( *ReadBuffer )( uint8_t addr, uint8_t *buffer, uint8_t size ); + /*! + * \brief Sets the maximum payload length. + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] max Maximum payload length in bytes + */ + void ( *SetMaxPayloadLength )( RadioModems_t modem, uint8_t max ); + /*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ + void ( *SetPublicNetwork )( bool enable ); +}; + +/*! + * \brief Radio driver + * + * \remark This variable is defined and initialized in the specific radio + * board implementation + */ +extern const struct Radio_s Radio; + +#endif // __RADIO_H__ diff --git a/libraries/LoRa_Node/src/radio/sx1276/sx1276.c b/libraries/LoRa_Node/src/radio/sx1276/sx1276.c new file mode 100644 index 0000000..935788e --- /dev/null +++ b/libraries/LoRa_Node/src/radio/sx1276/sx1276.c @@ -0,0 +1,1827 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic SX1276 driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis, Gregory Cristian and Wael Guibene +*/ +#include +#include +//----------modified +// #include "board.h" +// #include "radio.h" +// #include "sx1276-board.h" +#include "boards/arduino/board.h" +// #ifdef __cplusplus +// extern "C"{ +// #endif +#include "radio/radio.h" +#include "boards/arduino/sx1276-board.h" +#include "system/timer.h" +#include "system/delay.h" +// #ifdef __cplusplus +// } +// #endif +//--------------------------- +#include "sx1276.h" + +/* + * Local types definition + */ + +/*! + * Radio registers definition + */ +typedef struct +{ + RadioModems_t Modem; + uint8_t Addr; + uint8_t Value; +}RadioRegisters_t; + +/*! + * FSK bandwidth definition + */ +typedef struct +{ + uint32_t bandwidth; + uint8_t RegValue; +}FskBandwidth_t; + + +/* + * Private functions prototypes + */ + +/*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ +static void RxChainCalibration( void ); + +/*! + * \brief Resets the SX1276 + */ +void SX1276Reset( void ); + +/*! + * \brief Sets the SX1276 in transmission mode for the given time + * \param [IN] timeout Transmission timeout [ms] [0: continuous, others timeout] + */ +void SX1276SetTx( uint32_t timeout ); + +/*! + * \brief Writes the buffer contents to the SX1276 FIFO + * + * \param [IN] buffer Buffer containing data to be put on the FIFO. + * \param [IN] size Number of bytes to be written to the FIFO + */ +void SX1276WriteFifo( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Reads the contents of the SX1276 FIFO + * + * \param [OUT] buffer Buffer where to copy the FIFO read data. + * \param [IN] size Number of bytes to be read from the FIFO + */ +void SX1276ReadFifo( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the SX1276 operating mode + * + * \param [IN] opMode New operating mode + */ +void SX1276SetOpMode( uint8_t opMode ); + +/* + * SX1276 DIO IRQ callback functions prototype + */ + +/*! + * \brief DIO 0 IRQ callback + */ +void SX1276OnDio0Irq( void ); + +/*! + * \brief DIO 1 IRQ callback + */ +void SX1276OnDio1Irq( void ); + +/*! + * \brief DIO 2 IRQ callback + */ +void SX1276OnDio2Irq( void ); + +/*! + * \brief DIO 3 IRQ callback + */ +void SX1276OnDio3Irq( void ); + +/*! + * \brief DIO 4 IRQ callback + */ +void SX1276OnDio4Irq( void ); + +/*! + * \brief DIO 5 IRQ callback + */ +void SX1276OnDio5Irq( void ); + +/*! + * \brief Tx & Rx timeout timer callback + */ +void SX1276OnTimeoutIrq( void ); + +/* + * Private global constants + */ + +/*! + * Radio hardware registers initialization + * + * \remark RADIO_INIT_REGISTERS_VALUE is defined in sx1276-board.h file + */ +const RadioRegisters_t RadioRegsInit[] = RADIO_INIT_REGISTERS_VALUE; + +/*! + * Constant values need to compute the RSSI value + */ +#define RSSI_OFFSET_LF -164 +#define RSSI_OFFSET_HF -157 + +/*! + * Precomputed FSK bandwidth registers values + */ +const FskBandwidth_t FskBandwidths[] = +{ + { 2600 , 0x17 }, + { 3100 , 0x0F }, + { 3900 , 0x07 }, + { 5200 , 0x16 }, + { 6300 , 0x0E }, + { 7800 , 0x06 }, + { 10400 , 0x15 }, + { 12500 , 0x0D }, + { 15600 , 0x05 }, + { 20800 , 0x14 }, + { 25000 , 0x0C }, + { 31300 , 0x04 }, + { 41700 , 0x13 }, + { 50000 , 0x0B }, + { 62500 , 0x03 }, + { 83333 , 0x12 }, + { 100000, 0x0A }, + { 125000, 0x02 }, + { 166700, 0x11 }, + { 200000, 0x09 }, + { 250000, 0x01 }, + { 300000, 0x00 }, // Invalid Bandwidth +}; + +/* + * Private global variables + */ + +/*! + * Radio callbacks variable + */ +static RadioEvents_t *RadioEvents; + +/*! + * Reception buffer + */ +static uint8_t RxTxBuffer[RX_BUFFER_SIZE]; + +/* + * Public global variables + */ + +/*! + * Radio hardware and global parameters + */ +SX1276_t SX1276; + +/*! + * Hardware DIO IRQ callback initialization + */ +DioIrqHandler *DioIrq[] = { SX1276OnDio0Irq, SX1276OnDio1Irq, + SX1276OnDio2Irq, SX1276OnDio3Irq, + SX1276OnDio4Irq, NULL }; + +/*! + * Tx and Rx timers + */ +TimerEvent_t TxTimeoutTimer; +TimerEvent_t RxTimeoutTimer; +TimerEvent_t RxTimeoutSyncWord; + +/* + * Radio driver functions implementation + */ + +void SX1276Init( RadioEvents_t *events ) +{ + uint8_t i; + + RadioEvents = events; + + // Initialize driver timeout timers + TimerInit( &TxTimeoutTimer, SX1276OnTimeoutIrq ); + TimerInit( &RxTimeoutTimer, SX1276OnTimeoutIrq ); + TimerInit( &RxTimeoutSyncWord, SX1276OnTimeoutIrq ); + + SX1276Reset( ); + + RxChainCalibration( ); + + SX1276SetOpMode( RF_OPMODE_SLEEP ); + + SX1276IoIrqInit( DioIrq ); + + for( i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ ) + { + SX1276SetModem( RadioRegsInit[i].Modem ); + SX1276Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value ); + } + + SX1276SetModem( MODEM_FSK ); + + SX1276.Settings.State = RF_IDLE; +} + +RadioState_t SX1276GetStatus( void ) +{ + return SX1276.Settings.State; +} + +void SX1276SetChannel( uint32_t freq ) +{ + SX1276.Settings.Channel = freq; + freq = ( uint32_t )( ( double )freq / ( double )FREQ_STEP ); + SX1276Write( REG_FRFMSB, ( uint8_t )( ( freq >> 16 ) & 0xFF ) ); + SX1276Write( REG_FRFMID, ( uint8_t )( ( freq >> 8 ) & 0xFF ) ); + SX1276Write( REG_FRFLSB, ( uint8_t )( freq & 0xFF ) ); +} + +bool SX1276IsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ) +{ + int16_t rssi = 0; + + SX1276SetModem( modem ); + + SX1276SetChannel( freq ); + + SX1276SetOpMode( RF_OPMODE_RECEIVER ); + + DelayMs( 1 ); + + rssi = SX1276ReadRssi( modem ); + + SX1276SetSleep( ); + + if( rssi > rssiThresh ) + { + return false; + } + return true; +} + +uint32_t SX1276Random( void ) +{ + uint8_t i; + uint32_t rnd = 0; + + /* + * Radio setup for random number generation + */ + // Set LoRa modem ON + SX1276SetModem( MODEM_LORA ); + + // Disable LoRa modem interrupts + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // Set radio in continuous reception + SX1276SetOpMode( RF_OPMODE_RECEIVER ); + + for( i = 0; i < 32; i++ ) + { + DelayMs( 1 ); + // Unfiltered RSSI value reading. Only takes the LSB value + rnd |= ( ( uint32_t )SX1276Read( REG_LR_RSSIWIDEBAND ) & 0x01 ) << i; + } + + SX1276SetSleep( ); + + return rnd; +} + +/*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ +static void RxChainCalibration( void ) +{ + uint8_t regPaConfigInitVal; + uint32_t initialFreq; + + // Save context + regPaConfigInitVal = SX1276Read( REG_PACONFIG ); + initialFreq = ( double )( ( ( uint32_t )SX1276Read( REG_FRFMSB ) << 16 ) | + ( ( uint32_t )SX1276Read( REG_FRFMID ) << 8 ) | + ( ( uint32_t )SX1276Read( REG_FRFLSB ) ) ) * ( double )FREQ_STEP; + + // Cut the PA just in case, RFO output, power = -1 dBm + SX1276Write( REG_PACONFIG, 0x00 ); + + // Launch Rx chain calibration for LF band + SX1276Write( REG_IMAGECAL, ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Sets a Frequency in HF band + SX1276SetChannel( 868000000 ); + + // Launch Rx chain calibration for HF band + SX1276Write( REG_IMAGECAL, ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Restore context + SX1276Write( REG_PACONFIG, regPaConfigInitVal ); + SX1276SetChannel( initialFreq ); +} + +/*! + * Returns the known FSK bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +static uint8_t GetFskBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( FskBandwidth_t ) ) - 1; i++ ) + { + if( ( bandwidth >= FskBandwidths[i].bandwidth ) && ( bandwidth < FskBandwidths[i + 1].bandwidth ) ) + { + return FskBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while( 1 ); +} + +void SX1276SetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ) +{ + SX1276SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + { + SX1276.Settings.Fsk.Bandwidth = bandwidth; + SX1276.Settings.Fsk.Datarate = datarate; + SX1276.Settings.Fsk.BandwidthAfc = bandwidthAfc; + SX1276.Settings.Fsk.FixLen = fixLen; + SX1276.Settings.Fsk.PayloadLen = payloadLen; + SX1276.Settings.Fsk.CrcOn = crcOn; + SX1276.Settings.Fsk.IqInverted = iqInverted; + SX1276.Settings.Fsk.RxContinuous = rxContinuous; + SX1276.Settings.Fsk.PreambleLen = preambleLen; + SX1276.Settings.Fsk.RxSingleTimeout = symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1e3; + + datarate = ( uint16_t )( ( double )XTAL_FREQ / ( double )datarate ); + SX1276Write( REG_BITRATEMSB, ( uint8_t )( datarate >> 8 ) ); + SX1276Write( REG_BITRATELSB, ( uint8_t )( datarate & 0xFF ) ); + + SX1276Write( REG_RXBW, GetFskBandwidthRegValue( bandwidth ) ); + SX1276Write( REG_AFCBW, GetFskBandwidthRegValue( bandwidthAfc ) ); + + SX1276Write( REG_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + SX1276Write( REG_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + SX1276Write( REG_PAYLOADLENGTH, payloadLen ); + } + else + { + SX1276Write( REG_PAYLOADLENGTH, 0xFF ); // Set payload length to the maximum + } + + SX1276Write( REG_PACKETCONFIG1, + ( SX1276Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + SX1276Write( REG_PACKETCONFIG2, ( SX1276Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + if( bandwidth > 2 ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + bandwidth += 7; + SX1276.Settings.LoRa.Bandwidth = bandwidth; + SX1276.Settings.LoRa.Datarate = datarate; + SX1276.Settings.LoRa.Coderate = coderate; + SX1276.Settings.LoRa.PreambleLen = preambleLen; + SX1276.Settings.LoRa.FixLen = fixLen; + SX1276.Settings.LoRa.PayloadLen = payloadLen; + SX1276.Settings.LoRa.CrcOn = crcOn; + SX1276.Settings.LoRa.FreqHopOn = freqHopOn; + SX1276.Settings.LoRa.HopPeriod = hopPeriod; + SX1276.Settings.LoRa.IqInverted = iqInverted; + SX1276.Settings.LoRa.RxContinuous = rxContinuous; + + if( datarate > 12 ) + { + datarate = 12; + } + else if( datarate < 6 ) + { + datarate = 6; + } + + if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ) + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x00; + } + + SX1276Write( REG_LR_MODEMCONFIG1, + ( SX1276Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + SX1276Write( REG_LR_MODEMCONFIG2, + ( SX1276Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK & + RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) | + ( ( symbTimeout >> 8 ) & ~RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) ); + + SX1276Write( REG_LR_MODEMCONFIG3, + ( SX1276Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( SX1276.Settings.LoRa.LowDatarateOptimize << 3 ) ); + + SX1276Write( REG_LR_SYMBTIMEOUTLSB, ( uint8_t )( symbTimeout & 0xFF ) ); + + SX1276Write( REG_LR_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + SX1276Write( REG_LR_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + SX1276Write( REG_LR_PAYLOADLENGTH, payloadLen ); + } + + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_PLLHOP, ( SX1276Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + SX1276Write( REG_LR_HOPPERIOD, SX1276.Settings.LoRa.HopPeriod ); + } + + if( ( bandwidth == 9 ) && ( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + SX1276Write( REG_LR_TEST36, 0x02 ); + SX1276Write( REG_LR_TEST3A, 0x64 ); + } + else if( bandwidth == 9 ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + SX1276Write( REG_LR_TEST36, 0x02 ); + SX1276Write( REG_LR_TEST3A, 0x7F ); + } + else + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + SX1276Write( REG_LR_TEST36, 0x03 ); + } + + if( datarate == 6 ) + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +void SX1276SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ) +{ + SX1276SetModem( modem ); + + SX1276SetRfTxPower( power ); + + switch( modem ) + { + case MODEM_FSK: + { + SX1276.Settings.Fsk.Power = power; + SX1276.Settings.Fsk.Fdev = fdev; + SX1276.Settings.Fsk.Bandwidth = bandwidth; + SX1276.Settings.Fsk.Datarate = datarate; + SX1276.Settings.Fsk.PreambleLen = preambleLen; + SX1276.Settings.Fsk.FixLen = fixLen; + SX1276.Settings.Fsk.CrcOn = crcOn; + SX1276.Settings.Fsk.IqInverted = iqInverted; + SX1276.Settings.Fsk.TxTimeout = timeout; + + fdev = ( uint16_t )( ( double )fdev / ( double )FREQ_STEP ); + SX1276Write( REG_FDEVMSB, ( uint8_t )( fdev >> 8 ) ); + SX1276Write( REG_FDEVLSB, ( uint8_t )( fdev & 0xFF ) ); + + datarate = ( uint16_t )( ( double )XTAL_FREQ / ( double )datarate ); + SX1276Write( REG_BITRATEMSB, ( uint8_t )( datarate >> 8 ) ); + SX1276Write( REG_BITRATELSB, ( uint8_t )( datarate & 0xFF ) ); + + SX1276Write( REG_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + SX1276Write( REG_PREAMBLELSB, preambleLen & 0xFF ); + + SX1276Write( REG_PACKETCONFIG1, + ( SX1276Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + SX1276Write( REG_PACKETCONFIG2, ( SX1276Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + SX1276.Settings.LoRa.Power = power; + if( bandwidth > 2 ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + bandwidth += 7; + SX1276.Settings.LoRa.Bandwidth = bandwidth; + SX1276.Settings.LoRa.Datarate = datarate; + SX1276.Settings.LoRa.Coderate = coderate; + SX1276.Settings.LoRa.PreambleLen = preambleLen; + SX1276.Settings.LoRa.FixLen = fixLen; + SX1276.Settings.LoRa.FreqHopOn = freqHopOn; + SX1276.Settings.LoRa.HopPeriod = hopPeriod; + SX1276.Settings.LoRa.CrcOn = crcOn; + SX1276.Settings.LoRa.IqInverted = iqInverted; + SX1276.Settings.LoRa.TxTimeout = timeout; + + if( datarate > 12 ) + { + datarate = 12; + } + else if( datarate < 6 ) + { + datarate = 6; + } + if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ) + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x00; + } + + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_PLLHOP, ( SX1276Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + SX1276Write( REG_LR_HOPPERIOD, SX1276.Settings.LoRa.HopPeriod ); + } + + SX1276Write( REG_LR_MODEMCONFIG1, + ( SX1276Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + SX1276Write( REG_LR_MODEMCONFIG2, + ( SX1276Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) ); + + SX1276Write( REG_LR_MODEMCONFIG3, + ( SX1276Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( SX1276.Settings.LoRa.LowDatarateOptimize << 3 ) ); + + SX1276Write( REG_LR_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + SX1276Write( REG_LR_PREAMBLELSB, preambleLen & 0xFF ); + + if( datarate == 6 ) + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +uint32_t SX1276GetTimeOnAir( RadioModems_t modem, uint8_t pktLen ) +{ + uint32_t airTime = 0; + + switch( modem ) + { + case MODEM_FSK: + { + airTime = round( ( 8 * ( SX1276.Settings.Fsk.PreambleLen + + ( ( SX1276Read( REG_SYNCCONFIG ) & ~RF_SYNCCONFIG_SYNCSIZE_MASK ) + 1 ) + + ( ( SX1276.Settings.Fsk.FixLen == 0x01 ) ? 0.0 : 1.0 ) + + ( ( ( SX1276Read( REG_PACKETCONFIG1 ) & ~RF_PACKETCONFIG1_ADDRSFILTERING_MASK ) != 0x00 ) ? 1.0 : 0 ) + + pktLen + + ( ( SX1276.Settings.Fsk.CrcOn == 0x01 ) ? 2.0 : 0 ) ) / + SX1276.Settings.Fsk.Datarate ) * 1e3 ); + } + break; + case MODEM_LORA: + { + double bw = 0.0; + // REMARK: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + switch( SX1276.Settings.LoRa.Bandwidth ) + { + //case 0: // 7.8 kHz + // bw = 78e2; + // break; + //case 1: // 10.4 kHz + // bw = 104e2; + // break; + //case 2: // 15.6 kHz + // bw = 156e2; + // break; + //case 3: // 20.8 kHz + // bw = 208e2; + // break; + //case 4: // 31.2 kHz + // bw = 312e2; + // break; + //case 5: // 41.4 kHz + // bw = 414e2; + // break; + //case 6: // 62.5 kHz + // bw = 625e2; + // break; + case 7: // 125 kHz + bw = 125e3; + break; + case 8: // 250 kHz + bw = 250e3; + break; + case 9: // 500 kHz + bw = 500e3; + break; + } + + // Symbol rate : time for one symbol (secs) + double rs = bw / ( 1 << SX1276.Settings.LoRa.Datarate ); + double ts = 1 / rs; + // time of preamble + double tPreamble = ( SX1276.Settings.LoRa.PreambleLen + 4.25 ) * ts; + // Symbol length of payload and time + double tmp = ceil( ( 8 * pktLen - 4 * SX1276.Settings.LoRa.Datarate + + 28 + 16 * SX1276.Settings.LoRa.CrcOn - + ( SX1276.Settings.LoRa.FixLen ? 20 : 0 ) ) / + ( double )( 4 * ( SX1276.Settings.LoRa.Datarate - + ( ( SX1276.Settings.LoRa.LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) * + ( SX1276.Settings.LoRa.Coderate + 4 ); + double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 ); + double tPayload = nPayload * ts; + // Time on air + double tOnAir = tPreamble + tPayload; + // return ms secs + airTime = floor( tOnAir * 1e3 + 0.999 ); + } + break; + } + return airTime; +} + +void SX1276Send( uint8_t *buffer, uint8_t size ) +{ + uint32_t txTimeout = 0; + + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = size; + + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276WriteFifo( ( uint8_t* )&size, 1 ); + } + else + { + SX1276Write( REG_PAYLOADLENGTH, size ); + } + + if( ( size > 0 ) && ( size <= 64 ) ) + { + SX1276.Settings.FskPacketHandler.ChunkSize = size; + } + else + { + memcpy1( RxTxBuffer, buffer, size ); + SX1276.Settings.FskPacketHandler.ChunkSize = 32; + } + + // Write payload buffer + SX1276WriteFifo( buffer, SX1276.Settings.FskPacketHandler.ChunkSize ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.ChunkSize; + txTimeout = SX1276.Settings.Fsk.TxTimeout; + } + break; + case MODEM_LORA: + { + if( SX1276.Settings.LoRa.IqInverted == true ) + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_ON ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + SX1276.Settings.LoRaPacketHandler.Size = size; + + // Initializes the payload size + SX1276Write( REG_LR_PAYLOADLENGTH, size ); + + // Full buffer used for Tx + SX1276Write( REG_LR_FIFOTXBASEADDR, 0 ); + SX1276Write( REG_LR_FIFOADDRPTR, 0 ); + + // FIFO operations can not take place in Sleep mode + if( ( SX1276Read( REG_OPMODE ) & ~RF_OPMODE_MASK ) == RF_OPMODE_SLEEP ) + { + SX1276SetStby( ); + DelayMs( 1 ); + } + // Write payload buffer + SX1276WriteFifo( buffer, size ); + txTimeout = SX1276.Settings.LoRa.TxTimeout; + } + break; + } + + SX1276SetTx( txTimeout ); +} + +void SX1276SetSleep( void ) +{ + TimerStop( &RxTimeoutTimer ); + TimerStop( &TxTimeoutTimer ); + + SX1276SetOpMode( RF_OPMODE_SLEEP ); + SX1276.Settings.State = RF_IDLE; +} + +void SX1276SetStby( void ) +{ + TimerStop( &RxTimeoutTimer ); + TimerStop( &TxTimeoutTimer ); + + SX1276SetOpMode( RF_OPMODE_STANDBY ); + SX1276.Settings.State = RF_IDLE; +} + +void SX1276SetRx( uint32_t timeout ) +{ + bool rxContinuous = false; + + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + rxContinuous = SX1276.Settings.Fsk.RxContinuous; + + // DIO0=PayloadReady + // DIO1=FifoLevel + // DIO2=SyncAddr + // DIO3=FifoEmpty + // DIO4=Preamble + // DIO5=ModeReady + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO0_00 | + RF_DIOMAPPING1_DIO1_00 | + RF_DIOMAPPING1_DIO2_11 ); + + SX1276Write( REG_DIOMAPPING2, ( SX1276Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) | + RF_DIOMAPPING2_DIO4_11 | + RF_DIOMAPPING2_MAP_PREAMBLEDETECT ); + + SX1276.Settings.FskPacketHandler.FifoThresh = SX1276Read( REG_FIFOTHRESH ) & 0x3F; + + SX1276Write( REG_RXCONFIG, RF_RXCONFIG_AFCAUTO_ON | RF_RXCONFIG_AGCAUTO_ON | RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT ); + + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + } + break; + case MODEM_LORA: + { + if( SX1276.Settings.LoRa.IqInverted == true ) + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_ON | RFLR_INVERTIQ_TX_OFF ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal + if( SX1276.Settings.LoRa.Bandwidth < 9 ) + { + SX1276Write( REG_LR_DETECTOPTIMIZE, SX1276Read( REG_LR_DETECTOPTIMIZE ) & 0x7F ); + SX1276Write( REG_LR_TEST30, 0x00 ); + switch( SX1276.Settings.LoRa.Bandwidth ) + { + case 0: // 7.8 kHz + SX1276Write( REG_LR_TEST2F, 0x48 ); + SX1276SetChannel(SX1276.Settings.Channel + 7.81e3 ); + break; + case 1: // 10.4 kHz + SX1276Write( REG_LR_TEST2F, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 10.42e3 ); + break; + case 2: // 15.6 kHz + SX1276Write( REG_LR_TEST2F, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 15.62e3 ); + break; + case 3: // 20.8 kHz + SX1276Write( REG_LR_TEST2F, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 20.83e3 ); + break; + case 4: // 31.2 kHz + SX1276Write( REG_LR_TEST2F, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 31.25e3 ); + break; + case 5: // 41.4 kHz + SX1276Write( REG_LR_TEST2F, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 41.67e3 ); + break; + case 6: // 62.5 kHz + SX1276Write( REG_LR_TEST2F, 0x40 ); + break; + case 7: // 125 kHz + SX1276Write( REG_LR_TEST2F, 0x40 ); + break; + case 8: // 250 kHz + SX1276Write( REG_LR_TEST2F, 0x40 ); + break; + } + } + else + { + SX1276Write( REG_LR_DETECTOPTIMIZE, SX1276Read( REG_LR_DETECTOPTIMIZE ) | 0x80 ); + } + + rxContinuous = SX1276.Settings.LoRa.RxContinuous; + + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone, DIO2=FhssChangeChannel + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + SX1276Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_00 ); + } + SX1276Write( REG_LR_FIFORXBASEADDR, 0 ); + SX1276Write( REG_LR_FIFOADDRPTR, 0 ); + } + break; + } + + memset( RxTxBuffer, 0, ( size_t )RX_BUFFER_SIZE ); + + SX1276.Settings.State = RF_RX_RUNNING; + if( timeout != 0 ) + { + TimerSetValue( &RxTimeoutTimer, timeout ); + TimerStart( &RxTimeoutTimer ); + } + + if( SX1276.Settings.Modem == MODEM_FSK ) + { + SX1276SetOpMode( RF_OPMODE_RECEIVER ); + + if( rxContinuous == false ) + { + TimerSetValue( &RxTimeoutSyncWord, SX1276.Settings.Fsk.RxSingleTimeout ); + TimerStart( &RxTimeoutSyncWord ); + } + } + else + { + if( rxContinuous == true ) + { + SX1276SetOpMode( RFLR_OPMODE_RECEIVER ); + } + else + { + SX1276SetOpMode( RFLR_OPMODE_RECEIVER_SINGLE ); + } + } +} + +void SX1276SetTx( uint32_t timeout ) +{ + TimerSetValue( &TxTimeoutTimer, timeout ); + + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + // DIO0=PacketSent + // DIO1=FifoEmpty + // DIO2=FifoFull + // DIO3=FifoEmpty + // DIO4=LowBat + // DIO5=ModeReady + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO1_01 ); + + SX1276Write( REG_DIOMAPPING2, ( SX1276Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) ); + SX1276.Settings.FskPacketHandler.FifoThresh = SX1276Read( REG_FIFOTHRESH ) & 0x3F; + } + break; + case MODEM_LORA: + { + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone, DIO2=FhssChangeChannel + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_01 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_01 ); + } + } + break; + } + + SX1276.Settings.State = RF_TX_RUNNING; + TimerStart( &TxTimeoutTimer ); + SX1276SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +void SX1276StartCad( void ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + + } + break; + case MODEM_LORA: + { + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + //RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL // | + //RFLR_IRQFLAGS_CADDETECTED + ); + + // DIO3=CADDone + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO3_MASK ) | RFLR_DIOMAPPING1_DIO3_00 ); + + SX1276.Settings.State = RF_CAD; + SX1276SetOpMode( RFLR_OPMODE_CAD ); + } + break; + default: + break; + } +} + +void SX1276SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) +{ + uint32_t timeout = ( uint32_t )( time * 1e3 ); + + SX1276SetChannel( freq ); + + SX1276SetTxConfig( MODEM_FSK, power, 0, 0, 4800, 0, 5, false, false, 0, 0, 0, timeout ); + + SX1276Write( REG_PACKETCONFIG2, ( SX1276Read( REG_PACKETCONFIG2 ) & RF_PACKETCONFIG2_DATAMODE_MASK ) ); + // Disable radio interrupts + SX1276Write( REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_11 | RF_DIOMAPPING1_DIO1_11 ); + SX1276Write( REG_DIOMAPPING2, RF_DIOMAPPING2_DIO4_10 | RF_DIOMAPPING2_DIO5_10 ); + + TimerSetValue( &TxTimeoutTimer, timeout ); + + SX1276.Settings.State = RF_TX_RUNNING; + TimerStart( &TxTimeoutTimer ); + SX1276SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +int16_t SX1276ReadRssi( RadioModems_t modem ) +{ + int16_t rssi = 0; + + switch( modem ) + { + case MODEM_FSK: + rssi = -( SX1276Read( REG_RSSIVALUE ) >> 1 ); + break; + case MODEM_LORA: + if( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) + { + rssi = RSSI_OFFSET_HF + SX1276Read( REG_LR_RSSIVALUE ); + } + else + { + rssi = RSSI_OFFSET_LF + SX1276Read( REG_LR_RSSIVALUE ); + } + break; + default: + rssi = -1; + break; + } + return rssi; +} + +void SX1276Reset( void ) +{ + // Set RESET pin to 0 + GpioInit( &SX1276.Reset, RADIO_RESET, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + + // Wait 1 ms + DelayMs( 1 ); + + // Configure RESET as input + GpioInit( &SX1276.Reset, RADIO_RESET, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + // Wait 6 ms + DelayMs( 6 ); +} + +void SX1276SetOpMode( uint8_t opMode ) +{ +#if defined( USE_RADIO_DEBUG ) + switch( opMode ) + { + case RF_OPMODE_TRANSMITTER: + GpioWrite( &DbgPin1, 1 ); + break; + case RF_OPMODE_RECEIVER: + case RFLR_OPMODE_RECEIVER_SINGLE: + GpioWrite( &DbgPin2, 1 ); + break; + default: + GpioWrite( &DbgPin1, 0 ); + GpioWrite( &DbgPin2, 0 ); + break; + } +#endif + if( opMode == RF_OPMODE_SLEEP ) + { + SX1276SetAntSwLowPower( true ); + } + else + { + SX1276SetAntSwLowPower( false ); + SX1276SetAntSw( opMode ); + } + SX1276Write( REG_OPMODE, ( SX1276Read( REG_OPMODE ) & RF_OPMODE_MASK ) | opMode ); +} + +void SX1276SetModem( RadioModems_t modem ) +{ + //assert_param( ( SX1276.Spi.Spi.Instance != NULL ) ); + + if( ( SX1276Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_ON ) != 0 ) + { + SX1276.Settings.Modem = MODEM_LORA; + } + else + { + SX1276.Settings.Modem = MODEM_FSK; + } + + if( SX1276.Settings.Modem == modem ) + { + return; + } + + SX1276.Settings.Modem = modem; + switch( SX1276.Settings.Modem ) + { + default: + case MODEM_FSK: + SX1276SetSleep( ); + SX1276Write( REG_OPMODE, ( SX1276Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_OFF ); + + SX1276Write( REG_DIOMAPPING1, 0x00 ); + SX1276Write( REG_DIOMAPPING2, 0x30 ); // DIO5=ModeReady + break; + case MODEM_LORA: + SX1276SetSleep( ); + SX1276Write( REG_OPMODE, ( SX1276Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_ON ); + + SX1276Write( REG_DIOMAPPING1, 0x00 ); + SX1276Write( REG_DIOMAPPING2, 0x00 ); + break; + } +} + +void SX1276Write( uint8_t addr, uint8_t data ) +{ + SX1276WriteBuffer( addr, &data, 1 ); +} + +uint8_t SX1276Read( uint8_t addr ) +{ + uint8_t data; + SX1276ReadBuffer( addr, &data, 1 ); + return data; +} + +void SX1276WriteBuffer( uint8_t addr, uint8_t *buffer, uint8_t size ) +{ + uint8_t i; + + //NSS = 0; + GpioWrite( &SX1276.Spi.Nss, 0 ); + + SpiInOut( &SX1276.Spi, addr | 0x80 ); + for( i = 0; i < size; i++ ) + { + SpiInOut( &SX1276.Spi, buffer[i] ); + } + + //NSS = 1; + GpioWrite( &SX1276.Spi.Nss, 1 ); +} + +void SX1276ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size ) +{ + uint8_t i; + + //NSS = 0; + GpioWrite( &SX1276.Spi.Nss, 0 ); + + SpiInOut( &SX1276.Spi, addr & 0x7F ); + + for( i = 0; i < size; i++ ) + { + buffer[i] = SpiInOut( &SX1276.Spi, 0 ); + } + + //NSS = 1; + GpioWrite( &SX1276.Spi.Nss, 1 ); +} + +void SX1276WriteFifo( uint8_t *buffer, uint8_t size ) +{ + SX1276WriteBuffer( 0, buffer, size ); +} + +void SX1276ReadFifo( uint8_t *buffer, uint8_t size ) +{ + SX1276ReadBuffer( 0, buffer, size ); +} + +void SX1276SetMaxPayloadLength( RadioModems_t modem, uint8_t max ) +{ + SX1276SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276Write( REG_PAYLOADLENGTH, max ); + } + break; + case MODEM_LORA: + SX1276Write( REG_LR_PAYLOADMAXLENGTH, max ); + break; + } +} + +void SX1276SetPublicNetwork( bool enable ) +{ + SX1276SetModem( MODEM_LORA ); + if( enable == true ) + { + // Change LoRa modem SyncWord + SX1276Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); + } + else + { + // Change LoRa modem SyncWord + SX1276Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); + } +} + +void SX1276OnTimeoutIrq( void ) +{ + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + if( SX1276.Settings.Modem == MODEM_FSK ) + { + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + + // Clear Irqs + SX1276Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + SX1276Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + if( SX1276.Settings.Fsk.RxContinuous == true ) + { + // Continuous mode restart Rx chain + SX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + TimerStart( &RxTimeoutSyncWord ); + } + else + { + SX1276.Settings.State = RF_IDLE; + TimerStop( &RxTimeoutSyncWord ); + } + } + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } + break; + case RF_TX_RUNNING: + // Tx timeout shouldn't happen. + // But it has been observed that when it happens it is a result of a corrupted SPI transfer + // it depends on the platform design. + // + // The workaround is to put the radio in a known state. Thus, we re-initialize it. + // + // BEGIN WORKAROUND + SX1276Reset( ); + + RxChainCalibration( ); + + SX1276SetOpMode( RF_OPMODE_SLEEP ); + + for( uint8_t i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ ) + { + SX1276SetModem( RadioRegsInit[i].Modem ); + SX1276Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value ); + } + + SX1276SetModem( MODEM_FSK ); + + // END WORKAROUND + + SX1276.Settings.State = RF_IDLE; + if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) ) + { + RadioEvents->TxTimeout( ); + } + break; + default: + break; + } +} + +void SX1276OnDio0Irq( void ) +{ + volatile uint8_t irqFlags = 0; + + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + //TimerStop( &RxTimeoutTimer ); + // RxDone interrupt + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + if( SX1276.Settings.Fsk.CrcOn == true ) + { + irqFlags = SX1276Read( REG_IRQFLAGS2 ); + if( ( irqFlags & RF_IRQFLAGS2_CRCOK ) != RF_IRQFLAGS2_CRCOK ) + { + // Clear Irqs + SX1276Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + SX1276Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + TimerStop( &RxTimeoutTimer ); + + if( SX1276.Settings.Fsk.RxContinuous == false ) + { + TimerStop( &RxTimeoutSyncWord ); + SX1276.Settings.State = RF_IDLE; + } + else + { + // Continuous mode restart Rx chain + SX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + TimerStart( &RxTimeoutSyncWord ); + } + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxError != NULL ) ) + { + RadioEvents->RxError( ); + } + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + break; + } + } + + // Read received packet size + if( ( SX1276.Settings.FskPacketHandler.Size == 0 ) && ( SX1276.Settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276ReadFifo( ( uint8_t* )&SX1276.Settings.FskPacketHandler.Size, 1 ); + } + else + { + SX1276.Settings.FskPacketHandler.Size = SX1276Read( REG_PAYLOADLENGTH ); + } + SX1276ReadFifo( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes, SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + } + else + { + SX1276ReadFifo( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes, SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + } + + TimerStop( &RxTimeoutTimer ); + + if( SX1276.Settings.Fsk.RxContinuous == false ) + { + SX1276.Settings.State = RF_IDLE; + TimerStop( &RxTimeoutSyncWord ); + } + else + { + // Continuous mode restart Rx chain + SX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + TimerStart( &RxTimeoutSyncWord ); + } + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) ) + { + RadioEvents->RxDone( RxTxBuffer, SX1276.Settings.FskPacketHandler.Size, SX1276.Settings.FskPacketHandler.RssiValue, 0 ); + } + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + break; + case MODEM_LORA: + { + int8_t snr = 0; + + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE ); + + irqFlags = SX1276Read( REG_LR_IRQFLAGS ); + if( ( irqFlags & RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK ) == RFLR_IRQFLAGS_PAYLOADCRCERROR ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR ); + + if( SX1276.Settings.LoRa.RxContinuous == false ) + { + SX1276.Settings.State = RF_IDLE; + } + TimerStop( &RxTimeoutTimer ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxError != NULL ) ) + { + RadioEvents->RxError( ); + } + break; + } + + SX1276.Settings.LoRaPacketHandler.SnrValue = SX1276Read( REG_LR_PKTSNRVALUE ); + if( SX1276.Settings.LoRaPacketHandler.SnrValue & 0x80 ) // The SNR sign bit is 1 + { + // Invert and divide by 4 + snr = ( ( ~SX1276.Settings.LoRaPacketHandler.SnrValue + 1 ) & 0xFF ) >> 2; + snr = -snr; + } + else + { + // Divide by 4 + snr = ( SX1276.Settings.LoRaPacketHandler.SnrValue & 0xFF ) >> 2; + } + + int16_t rssi = SX1276Read( REG_LR_PKTRSSIVALUE ); + if( snr < 0 ) + { + if( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) + + snr; + } + else + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) + + snr; + } + } + else + { + if( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ); + } + else + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ); + } + } + + SX1276.Settings.LoRaPacketHandler.Size = SX1276Read( REG_LR_RXNBBYTES ); + SX1276ReadFifo( RxTxBuffer, SX1276.Settings.LoRaPacketHandler.Size ); + + if( SX1276.Settings.LoRa.RxContinuous == false ) + { + SX1276.Settings.State = RF_IDLE; + } + TimerStop( &RxTimeoutTimer ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) ) + { + RadioEvents->RxDone( RxTxBuffer, SX1276.Settings.LoRaPacketHandler.Size, SX1276.Settings.LoRaPacketHandler.RssiValue, SX1276.Settings.LoRaPacketHandler.SnrValue ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + TimerStop( &TxTimeoutTimer ); + // TxDone interrupt + switch( SX1276.Settings.Modem ) + { + case MODEM_LORA: + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE ); + // Intentional fall through + case MODEM_FSK: + default: + SX1276.Settings.State = RF_IDLE; + if( ( RadioEvents != NULL ) && ( RadioEvents->TxDone != NULL ) ) + { + RadioEvents->TxDone( ); + } + break; + } + break; + default: + break; + } +} + +void SX1276OnDio1Irq( void ) +{ + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + // FifoLevel interrupt + // Read received packet size + if( ( SX1276.Settings.FskPacketHandler.Size == 0 ) && ( SX1276.Settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276ReadFifo( ( uint8_t* )&SX1276.Settings.FskPacketHandler.Size, 1 ); + } + else + { + SX1276.Settings.FskPacketHandler.Size = SX1276Read( REG_PAYLOADLENGTH ); + } + } + + if( ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ) > SX1276.Settings.FskPacketHandler.FifoThresh ) + { + SX1276ReadFifo( ( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes ), SX1276.Settings.FskPacketHandler.FifoThresh ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.FifoThresh; + } + else + { + SX1276ReadFifo( ( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes ), SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + } + break; + case MODEM_LORA: + // Sync time out + TimerStop( &RxTimeoutTimer ); + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXTIMEOUT ); + + SX1276.Settings.State = RF_IDLE; + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + // FifoEmpty interrupt + if( ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ) > SX1276.Settings.FskPacketHandler.ChunkSize ) + { + SX1276WriteFifo( ( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes ), SX1276.Settings.FskPacketHandler.ChunkSize ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.ChunkSize; + } + else + { + // Write the last chunk of data + SX1276WriteFifo( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes, SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes; + } + break; + case MODEM_LORA: + break; + default: + break; + } + break; + default: + break; + } +} + +void SX1276OnDio2Irq( void ) +{ + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + // Checks if DIO4 is connected. If it is not PreambleDtected is set to true. + if( SX1276.DIO4.port == NULL ) + { + SX1276.Settings.FskPacketHandler.PreambleDetected = true; + } + + if( ( SX1276.Settings.FskPacketHandler.PreambleDetected == true ) && ( SX1276.Settings.FskPacketHandler.SyncWordDetected == false ) ) + { + TimerStop( &RxTimeoutSyncWord ); + + SX1276.Settings.FskPacketHandler.SyncWordDetected = true; + + SX1276.Settings.FskPacketHandler.RssiValue = -( SX1276Read( REG_RSSIVALUE ) >> 1 ); + + SX1276.Settings.FskPacketHandler.AfcValue = ( int32_t )( double )( ( ( uint16_t )SX1276Read( REG_AFCMSB ) << 8 ) | + ( uint16_t )SX1276Read( REG_AFCLSB ) ) * + ( double )FREQ_STEP; + SX1276.Settings.FskPacketHandler.RxGain = ( SX1276Read( REG_LNA ) >> 5 ) & 0x07; + } + break; + case MODEM_LORA: + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->FhssChangeChannel != NULL ) ) + { + RadioEvents->FhssChangeChannel( ( SX1276Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->FhssChangeChannel != NULL ) ) + { + RadioEvents->FhssChangeChannel( ( SX1276Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + default: + break; + } +} + +void SX1276OnDio3Irq( void ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( ( SX1276Read( REG_LR_IRQFLAGS ) & RFLR_IRQFLAGS_CADDETECTED ) == RFLR_IRQFLAGS_CADDETECTED ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDETECTED | RFLR_IRQFLAGS_CADDONE ); + if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) ) + { + RadioEvents->CadDone( true ); + } + } + else + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDONE ); + if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) ) + { + RadioEvents->CadDone( false ); + } + } + break; + default: + break; + } +} + +void SX1276OnDio4Irq( void ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + if( SX1276.Settings.FskPacketHandler.PreambleDetected == false ) + { + SX1276.Settings.FskPacketHandler.PreambleDetected = true; + } + } + break; + case MODEM_LORA: + break; + default: + break; + } +} + +void SX1276OnDio5Irq( void ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + break; + default: + break; + } +} diff --git a/libraries/LoRa_Node/src/radio/sx1276/sx1276.h b/libraries/LoRa_Node/src/radio/sx1276/sx1276.h new file mode 100644 index 0000000..8f01e8d --- /dev/null +++ b/libraries/LoRa_Node/src/radio/sx1276/sx1276.h @@ -0,0 +1,409 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic SX1276 driver implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SX1276_H__ +#define __SX1276_H__ + +#include "sx1276Regs-Fsk.h" +#include "sx1276Regs-LoRa.h" + +//----------modified +#include "system/spi.h" +#include "radio/radio.h" +//------------------------------ + +/*! + * Radio wake-up time from sleep + */ +#define RADIO_WAKEUP_TIME 1 // [ms] + +/*! + * Sync word for Private LoRa networks + */ +#define LORA_MAC_PRIVATE_SYNCWORD 0x12 + +/*! + * Sync word for Public LoRa networks + */ +#define LORA_MAC_PUBLIC_SYNCWORD 0x34 + +/*! + * Radio FSK modem parameters + */ +typedef struct +{ + int8_t Power; + uint32_t Fdev; + uint32_t Bandwidth; + uint32_t BandwidthAfc; + uint32_t Datarate; + uint16_t PreambleLen; + bool FixLen; + uint8_t PayloadLen; + bool CrcOn; + bool IqInverted; + bool RxContinuous; + uint32_t TxTimeout; + uint32_t RxSingleTimeout; +}RadioFskSettings_t; + +/*! + * Radio FSK packet handler state + */ +typedef struct +{ + uint8_t PreambleDetected; + uint8_t SyncWordDetected; + int8_t RssiValue; + int32_t AfcValue; + uint8_t RxGain; + uint16_t Size; + uint16_t NbBytes; + uint8_t FifoThresh; + uint8_t ChunkSize; +}RadioFskPacketHandler_t; + +/*! + * Radio LoRa modem parameters + */ +typedef struct +{ + int8_t Power; + uint32_t Bandwidth; + uint32_t Datarate; + bool LowDatarateOptimize; + uint8_t Coderate; + uint16_t PreambleLen; + bool FixLen; + uint8_t PayloadLen; + bool CrcOn; + bool FreqHopOn; + uint8_t HopPeriod; + bool IqInverted; + bool RxContinuous; + uint32_t TxTimeout; +}RadioLoRaSettings_t; + +/*! + * Radio LoRa packet handler state + */ +typedef struct +{ + int8_t SnrValue; + int16_t RssiValue; + uint8_t Size; +}RadioLoRaPacketHandler_t; + +/*! + * Radio Settings + */ +typedef struct +{ + RadioState_t State; + RadioModems_t Modem; + uint32_t Channel; + RadioFskSettings_t Fsk; + RadioFskPacketHandler_t FskPacketHandler; + RadioLoRaSettings_t LoRa; + RadioLoRaPacketHandler_t LoRaPacketHandler; +}RadioSettings_t; + +/*! + * Radio hardware and global parameters + */ +typedef struct SX1276_s +{ + Gpio_t Reset; + Gpio_t DIO0; + Gpio_t DIO1; + Gpio_t DIO2; + Gpio_t DIO3; + Gpio_t DIO4; + Gpio_t DIO5; + Spi_t Spi; + RadioSettings_t Settings; +}SX1276_t; + +/*! + * Hardware IO IRQ callback function definition + */ +typedef void ( DioIrqHandler )( void ); + +/*! + * SX1276 definitions + */ +#define XTAL_FREQ 32000000 +#define FREQ_STEP 61.03515625 + +#define RX_BUFFER_SIZE 256 + +/*! + * ============================================================================ + * Public functions prototypes + * ============================================================================ + */ + +/*! + * \brief Initializes the radio + * + * \param [IN] events Structure containing the driver callback functions + */ +void SX1276Init( RadioEvents_t *events ); + +/*! + * Return current radio status + * + * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ +RadioState_t SX1276GetStatus( void ); + +/*! + * \brief Configures the radio with the given modem + * + * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ +void SX1276SetModem( RadioModems_t modem ); + +/*! + * \brief Sets the channel configuration + * + * \param [IN] freq Channel RF frequency + */ +void SX1276SetChannel( uint32_t freq ); + +/*! + * \brief Sets the channels configuration + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] freq Channel RF frequency + * \param [IN] rssiThresh RSSI threshold + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ +bool SX1276IsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ); + +/*! + * \brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either SX1276SetRxConfig or + * SX1276SetTxConfig functions must be called. + * + * \retval randomValue 32 bits random value + */ +uint32_t SX1276Random( void ); + +/*! + * \brief Sets the reception parameters + * + * \remark When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout number of bytes + * LoRa: timeout in symbols + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] HopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ +void SX1276SetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool FreqHopOn, uint8_t HopPeriod, + bool iqInverted, bool rxContinuous ); + +/*! + * \brief Sets the transmission parameters + * + * \remark When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] power Sets the output power [dBm] + * \param [IN] fdev Sets the frequency deviation (FSK only) + * FSK : [Hz] + * LoRa: 0 + * \param [IN] bandwidth Sets the bandwidth (LoRa only) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * \param [IN] FreqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] HopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] timeout Transmission timeout [ms] + */ +void SX1276SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool FreqHopOn, + uint8_t HopPeriod, bool iqInverted, uint32_t timeout ); + +/*! + * \brief Computes the packet time on air in ms for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] pktLen Packet payload length + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ +uint32_t SX1276GetTimeOnAir( RadioModems_t modem, uint8_t pktLen ); + +/*! + * \brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * \param [IN]: buffer Buffer pointer + * \param [IN]: size Buffer size + */ +void SX1276Send( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the radio in sleep mode + */ +void SX1276SetSleep( void ); + +/*! + * \brief Sets the radio in standby mode + */ +void SX1276SetStby( void ); + +/*! + * \brief Sets the radio in reception mode for the given time + * \param [IN] timeout Reception timeout [ms] [0: continuous, others timeout] + */ +void SX1276SetRx( uint32_t timeout ); + +/*! + * \brief Start a Channel Activity Detection + */ +void SX1276StartCad( void ); + +/*! + * \brief Sets the radio in continuous wave transmission mode + * + * \param [IN]: freq Channel RF frequency + * \param [IN]: power Sets the output power [dBm] + * \param [IN]: time Transmission mode timeout [s] + */ +void SX1276SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ); + +/*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ +int16_t SX1276ReadRssi( RadioModems_t modem ); + +/*! + * \brief Writes the radio register at the specified address + * + * \param [IN]: addr Register address + * \param [IN]: data New register value + */ +void SX1276Write( uint8_t addr, uint8_t data ); + +/*! + * \brief Reads the radio register at the specified address + * + * \param [IN]: addr Register address + * \retval data Register value + */ +uint8_t SX1276Read( uint8_t addr ); + +/*! + * \brief Writes multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [IN] buffer Buffer containing the new register's values + * \param [IN] size Number of registers to be written + */ +void SX1276WriteBuffer( uint8_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Reads multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [OUT] buffer Buffer where to copy the registers data + * \param [IN] size Number of registers to be read + */ +void SX1276ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the maximum payload length. + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] max Maximum payload length in bytes + */ +void SX1276SetMaxPayloadLength( RadioModems_t modem, uint8_t max ); + +/*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ +void SX1276SetPublicNetwork( bool enable ); + +#endif // __SX1276_H__ diff --git a/libraries/LoRa_Node/src/radio/sx1276/sx1276Regs-Fsk.h b/libraries/LoRa_Node/src/radio/sx1276/sx1276Regs-Fsk.h new file mode 100644 index 0000000..64ff293 --- /dev/null +++ b/libraries/LoRa_Node/src/radio/sx1276/sx1276Regs-Fsk.h @@ -0,0 +1,1134 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1276 FSK modem registers and bits definitions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SX1276_REGS_FSK_H__ +#define __SX1276_REGS_FSK_H__ + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define REG_FIFO 0x00 +// Common settings +#define REG_OPMODE 0x01 +#define REG_BITRATEMSB 0x02 +#define REG_BITRATELSB 0x03 +#define REG_FDEVMSB 0x04 +#define REG_FDEVLSB 0x05 +#define REG_FRFMSB 0x06 +#define REG_FRFMID 0x07 +#define REG_FRFLSB 0x08 +// Tx settings +#define REG_PACONFIG 0x09 +#define REG_PARAMP 0x0A +#define REG_OCP 0x0B +// Rx settings +#define REG_LNA 0x0C +#define REG_RXCONFIG 0x0D +#define REG_RSSICONFIG 0x0E +#define REG_RSSICOLLISION 0x0F +#define REG_RSSITHRESH 0x10 +#define REG_RSSIVALUE 0x11 +#define REG_RXBW 0x12 +#define REG_AFCBW 0x13 +#define REG_OOKPEAK 0x14 +#define REG_OOKFIX 0x15 +#define REG_OOKAVG 0x16 +#define REG_RES17 0x17 +#define REG_RES18 0x18 +#define REG_RES19 0x19 +#define REG_AFCFEI 0x1A +#define REG_AFCMSB 0x1B +#define REG_AFCLSB 0x1C +#define REG_FEIMSB 0x1D +#define REG_FEILSB 0x1E +#define REG_PREAMBLEDETECT 0x1F +#define REG_RXTIMEOUT1 0x20 +#define REG_RXTIMEOUT2 0x21 +#define REG_RXTIMEOUT3 0x22 +#define REG_RXDELAY 0x23 +// Oscillator settings +#define REG_OSC 0x24 +// Packet handler settings +#define REG_PREAMBLEMSB 0x25 +#define REG_PREAMBLELSB 0x26 +#define REG_SYNCCONFIG 0x27 +#define REG_SYNCVALUE1 0x28 +#define REG_SYNCVALUE2 0x29 +#define REG_SYNCVALUE3 0x2A +#define REG_SYNCVALUE4 0x2B +#define REG_SYNCVALUE5 0x2C +#define REG_SYNCVALUE6 0x2D +#define REG_SYNCVALUE7 0x2E +#define REG_SYNCVALUE8 0x2F +#define REG_PACKETCONFIG1 0x30 +#define REG_PACKETCONFIG2 0x31 +#define REG_PAYLOADLENGTH 0x32 +#define REG_NODEADRS 0x33 +#define REG_BROADCASTADRS 0x34 +#define REG_FIFOTHRESH 0x35 +// SM settings +#define REG_SEQCONFIG1 0x36 +#define REG_SEQCONFIG2 0x37 +#define REG_TIMERRESOL 0x38 +#define REG_TIMER1COEF 0x39 +#define REG_TIMER2COEF 0x3A +// Service settings +#define REG_IMAGECAL 0x3B +#define REG_TEMP 0x3C +#define REG_LOWBAT 0x3D +// Status +#define REG_IRQFLAGS1 0x3E +#define REG_IRQFLAGS2 0x3F +// I/O settings +#define REG_DIOMAPPING1 0x40 +#define REG_DIOMAPPING2 0x41 +// Version +#define REG_VERSION 0x42 +// Additional settings +#define REG_PLLHOP 0x44 +#define REG_TCXO 0x4B +#define REG_PADAC 0x4D +#define REG_FORMERTEMP 0x5B +#define REG_BITRATEFRAC 0x5D +#define REG_AGCREF 0x61 +#define REG_AGCTHRESH1 0x62 +#define REG_AGCTHRESH2 0x63 +#define REG_AGCTHRESH3 0x64 +#define REG_PLL 0x70 + +/*! + * ============================================================================ + * SX1276 FSK bits control definition + * ============================================================================ + */ + +/*! + * RegFifo + */ + +/*! + * RegOpMode + */ +#define RF_OPMODE_LONGRANGEMODE_MASK 0x7F +#define RF_OPMODE_LONGRANGEMODE_OFF 0x00 +#define RF_OPMODE_LONGRANGEMODE_ON 0x80 + +#define RF_OPMODE_MODULATIONTYPE_MASK 0x9F +#define RF_OPMODE_MODULATIONTYPE_FSK 0x00 // Default +#define RF_OPMODE_MODULATIONTYPE_OOK 0x20 + +#define RF_OPMODE_MODULATIONSHAPING_MASK 0xE7 +#define RF_OPMODE_MODULATIONSHAPING_00 0x00 // Default +#define RF_OPMODE_MODULATIONSHAPING_01 0x08 +#define RF_OPMODE_MODULATIONSHAPING_10 0x10 +#define RF_OPMODE_MODULATIONSHAPING_11 0x18 + +#define RF_OPMODE_MASK 0xF8 +#define RF_OPMODE_SLEEP 0x00 +#define RF_OPMODE_STANDBY 0x01 // Default +#define RF_OPMODE_SYNTHESIZER_TX 0x02 +#define RF_OPMODE_TRANSMITTER 0x03 +#define RF_OPMODE_SYNTHESIZER_RX 0x04 +#define RF_OPMODE_RECEIVER 0x05 + +/*! + * RegBitRate (bits/sec) + */ +#define RF_BITRATEMSB_1200_BPS 0x68 +#define RF_BITRATELSB_1200_BPS 0x2B +#define RF_BITRATEMSB_2400_BPS 0x34 +#define RF_BITRATELSB_2400_BPS 0x15 +#define RF_BITRATEMSB_4800_BPS 0x1A // Default +#define RF_BITRATELSB_4800_BPS 0x0B // Default +#define RF_BITRATEMSB_9600_BPS 0x0D +#define RF_BITRATELSB_9600_BPS 0x05 +#define RF_BITRATEMSB_15000_BPS 0x08 +#define RF_BITRATELSB_15000_BPS 0x55 +#define RF_BITRATEMSB_19200_BPS 0x06 +#define RF_BITRATELSB_19200_BPS 0x83 +#define RF_BITRATEMSB_38400_BPS 0x03 +#define RF_BITRATELSB_38400_BPS 0x41 +#define RF_BITRATEMSB_76800_BPS 0x01 +#define RF_BITRATELSB_76800_BPS 0xA1 +#define RF_BITRATEMSB_153600_BPS 0x00 +#define RF_BITRATELSB_153600_BPS 0xD0 +#define RF_BITRATEMSB_57600_BPS 0x02 +#define RF_BITRATELSB_57600_BPS 0x2C +#define RF_BITRATEMSB_115200_BPS 0x01 +#define RF_BITRATELSB_115200_BPS 0x16 +#define RF_BITRATEMSB_12500_BPS 0x0A +#define RF_BITRATELSB_12500_BPS 0x00 +#define RF_BITRATEMSB_25000_BPS 0x05 +#define RF_BITRATELSB_25000_BPS 0x00 +#define RF_BITRATEMSB_50000_BPS 0x02 +#define RF_BITRATELSB_50000_BPS 0x80 +#define RF_BITRATEMSB_100000_BPS 0x01 +#define RF_BITRATELSB_100000_BPS 0x40 +#define RF_BITRATEMSB_150000_BPS 0x00 +#define RF_BITRATELSB_150000_BPS 0xD5 +#define RF_BITRATEMSB_200000_BPS 0x00 +#define RF_BITRATELSB_200000_BPS 0xA0 +#define RF_BITRATEMSB_250000_BPS 0x00 +#define RF_BITRATELSB_250000_BPS 0x80 +#define RF_BITRATEMSB_32768_BPS 0x03 +#define RF_BITRATELSB_32768_BPS 0xD1 + +/*! + * RegFdev (Hz) + */ +#define RF_FDEVMSB_2000_HZ 0x00 +#define RF_FDEVLSB_2000_HZ 0x21 +#define RF_FDEVMSB_5000_HZ 0x00 // Default +#define RF_FDEVLSB_5000_HZ 0x52 // Default +#define RF_FDEVMSB_10000_HZ 0x00 +#define RF_FDEVLSB_10000_HZ 0xA4 +#define RF_FDEVMSB_15000_HZ 0x00 +#define RF_FDEVLSB_15000_HZ 0xF6 +#define RF_FDEVMSB_20000_HZ 0x01 +#define RF_FDEVLSB_20000_HZ 0x48 +#define RF_FDEVMSB_25000_HZ 0x01 +#define RF_FDEVLSB_25000_HZ 0x9A +#define RF_FDEVMSB_30000_HZ 0x01 +#define RF_FDEVLSB_30000_HZ 0xEC +#define RF_FDEVMSB_35000_HZ 0x02 +#define RF_FDEVLSB_35000_HZ 0x3D +#define RF_FDEVMSB_40000_HZ 0x02 +#define RF_FDEVLSB_40000_HZ 0x8F +#define RF_FDEVMSB_45000_HZ 0x02 +#define RF_FDEVLSB_45000_HZ 0xE1 +#define RF_FDEVMSB_50000_HZ 0x03 +#define RF_FDEVLSB_50000_HZ 0x33 +#define RF_FDEVMSB_55000_HZ 0x03 +#define RF_FDEVLSB_55000_HZ 0x85 +#define RF_FDEVMSB_60000_HZ 0x03 +#define RF_FDEVLSB_60000_HZ 0xD7 +#define RF_FDEVMSB_65000_HZ 0x04 +#define RF_FDEVLSB_65000_HZ 0x29 +#define RF_FDEVMSB_70000_HZ 0x04 +#define RF_FDEVLSB_70000_HZ 0x7B +#define RF_FDEVMSB_75000_HZ 0x04 +#define RF_FDEVLSB_75000_HZ 0xCD +#define RF_FDEVMSB_80000_HZ 0x05 +#define RF_FDEVLSB_80000_HZ 0x1F +#define RF_FDEVMSB_85000_HZ 0x05 +#define RF_FDEVLSB_85000_HZ 0x71 +#define RF_FDEVMSB_90000_HZ 0x05 +#define RF_FDEVLSB_90000_HZ 0xC3 +#define RF_FDEVMSB_95000_HZ 0x06 +#define RF_FDEVLSB_95000_HZ 0x14 +#define RF_FDEVMSB_100000_HZ 0x06 +#define RF_FDEVLSB_100000_HZ 0x66 +#define RF_FDEVMSB_110000_HZ 0x07 +#define RF_FDEVLSB_110000_HZ 0x0A +#define RF_FDEVMSB_120000_HZ 0x07 +#define RF_FDEVLSB_120000_HZ 0xAE +#define RF_FDEVMSB_130000_HZ 0x08 +#define RF_FDEVLSB_130000_HZ 0x52 +#define RF_FDEVMSB_140000_HZ 0x08 +#define RF_FDEVLSB_140000_HZ 0xF6 +#define RF_FDEVMSB_150000_HZ 0x09 +#define RF_FDEVLSB_150000_HZ 0x9A +#define RF_FDEVMSB_160000_HZ 0x0A +#define RF_FDEVLSB_160000_HZ 0x3D +#define RF_FDEVMSB_170000_HZ 0x0A +#define RF_FDEVLSB_170000_HZ 0xE1 +#define RF_FDEVMSB_180000_HZ 0x0B +#define RF_FDEVLSB_180000_HZ 0x85 +#define RF_FDEVMSB_190000_HZ 0x0C +#define RF_FDEVLSB_190000_HZ 0x29 +#define RF_FDEVMSB_200000_HZ 0x0C +#define RF_FDEVLSB_200000_HZ 0xCD + +/*! + * RegFrf (MHz) + */ +#define RF_FRFMSB_863_MHZ 0xD7 +#define RF_FRFMID_863_MHZ 0xC0 +#define RF_FRFLSB_863_MHZ 0x00 +#define RF_FRFMSB_864_MHZ 0xD8 +#define RF_FRFMID_864_MHZ 0x00 +#define RF_FRFLSB_864_MHZ 0x00 +#define RF_FRFMSB_865_MHZ 0xD8 +#define RF_FRFMID_865_MHZ 0x40 +#define RF_FRFLSB_865_MHZ 0x00 +#define RF_FRFMSB_866_MHZ 0xD8 +#define RF_FRFMID_866_MHZ 0x80 +#define RF_FRFLSB_866_MHZ 0x00 +#define RF_FRFMSB_867_MHZ 0xD8 +#define RF_FRFMID_867_MHZ 0xC0 +#define RF_FRFLSB_867_MHZ 0x00 +#define RF_FRFMSB_868_MHZ 0xD9 +#define RF_FRFMID_868_MHZ 0x00 +#define RF_FRFLSB_868_MHZ 0x00 +#define RF_FRFMSB_869_MHZ 0xD9 +#define RF_FRFMID_869_MHZ 0x40 +#define RF_FRFLSB_869_MHZ 0x00 +#define RF_FRFMSB_870_MHZ 0xD9 +#define RF_FRFMID_870_MHZ 0x80 +#define RF_FRFLSB_870_MHZ 0x00 + +#define RF_FRFMSB_902_MHZ 0xE1 +#define RF_FRFMID_902_MHZ 0x80 +#define RF_FRFLSB_902_MHZ 0x00 +#define RF_FRFMSB_903_MHZ 0xE1 +#define RF_FRFMID_903_MHZ 0xC0 +#define RF_FRFLSB_903_MHZ 0x00 +#define RF_FRFMSB_904_MHZ 0xE2 +#define RF_FRFMID_904_MHZ 0x00 +#define RF_FRFLSB_904_MHZ 0x00 +#define RF_FRFMSB_905_MHZ 0xE2 +#define RF_FRFMID_905_MHZ 0x40 +#define RF_FRFLSB_905_MHZ 0x00 +#define RF_FRFMSB_906_MHZ 0xE2 +#define RF_FRFMID_906_MHZ 0x80 +#define RF_FRFLSB_906_MHZ 0x00 +#define RF_FRFMSB_907_MHZ 0xE2 +#define RF_FRFMID_907_MHZ 0xC0 +#define RF_FRFLSB_907_MHZ 0x00 +#define RF_FRFMSB_908_MHZ 0xE3 +#define RF_FRFMID_908_MHZ 0x00 +#define RF_FRFLSB_908_MHZ 0x00 +#define RF_FRFMSB_909_MHZ 0xE3 +#define RF_FRFMID_909_MHZ 0x40 +#define RF_FRFLSB_909_MHZ 0x00 +#define RF_FRFMSB_910_MHZ 0xE3 +#define RF_FRFMID_910_MHZ 0x80 +#define RF_FRFLSB_910_MHZ 0x00 +#define RF_FRFMSB_911_MHZ 0xE3 +#define RF_FRFMID_911_MHZ 0xC0 +#define RF_FRFLSB_911_MHZ 0x00 +#define RF_FRFMSB_912_MHZ 0xE4 +#define RF_FRFMID_912_MHZ 0x00 +#define RF_FRFLSB_912_MHZ 0x00 +#define RF_FRFMSB_913_MHZ 0xE4 +#define RF_FRFMID_913_MHZ 0x40 +#define RF_FRFLSB_913_MHZ 0x00 +#define RF_FRFMSB_914_MHZ 0xE4 +#define RF_FRFMID_914_MHZ 0x80 +#define RF_FRFLSB_914_MHZ 0x00 +#define RF_FRFMSB_915_MHZ 0xE4 // Default +#define RF_FRFMID_915_MHZ 0xC0 // Default +#define RF_FRFLSB_915_MHZ 0x00 // Default +#define RF_FRFMSB_916_MHZ 0xE5 +#define RF_FRFMID_916_MHZ 0x00 +#define RF_FRFLSB_916_MHZ 0x00 +#define RF_FRFMSB_917_MHZ 0xE5 +#define RF_FRFMID_917_MHZ 0x40 +#define RF_FRFLSB_917_MHZ 0x00 +#define RF_FRFMSB_918_MHZ 0xE5 +#define RF_FRFMID_918_MHZ 0x80 +#define RF_FRFLSB_918_MHZ 0x00 +#define RF_FRFMSB_919_MHZ 0xE5 +#define RF_FRFMID_919_MHZ 0xC0 +#define RF_FRFLSB_919_MHZ 0x00 +#define RF_FRFMSB_920_MHZ 0xE6 +#define RF_FRFMID_920_MHZ 0x00 +#define RF_FRFLSB_920_MHZ 0x00 +#define RF_FRFMSB_921_MHZ 0xE6 +#define RF_FRFMID_921_MHZ 0x40 +#define RF_FRFLSB_921_MHZ 0x00 +#define RF_FRFMSB_922_MHZ 0xE6 +#define RF_FRFMID_922_MHZ 0x80 +#define RF_FRFLSB_922_MHZ 0x00 +#define RF_FRFMSB_923_MHZ 0xE6 +#define RF_FRFMID_923_MHZ 0xC0 +#define RF_FRFLSB_923_MHZ 0x00 +#define RF_FRFMSB_924_MHZ 0xE7 +#define RF_FRFMID_924_MHZ 0x00 +#define RF_FRFLSB_924_MHZ 0x00 +#define RF_FRFMSB_925_MHZ 0xE7 +#define RF_FRFMID_925_MHZ 0x40 +#define RF_FRFLSB_925_MHZ 0x00 +#define RF_FRFMSB_926_MHZ 0xE7 +#define RF_FRFMID_926_MHZ 0x80 +#define RF_FRFLSB_926_MHZ 0x00 +#define RF_FRFMSB_927_MHZ 0xE7 +#define RF_FRFMID_927_MHZ 0xC0 +#define RF_FRFLSB_927_MHZ 0x00 +#define RF_FRFMSB_928_MHZ 0xE8 +#define RF_FRFMID_928_MHZ 0x00 +#define RF_FRFLSB_928_MHZ 0x00 + +/*! + * RegPaConfig + */ +#define RF_PACONFIG_PASELECT_MASK 0x7F +#define RF_PACONFIG_PASELECT_PABOOST 0x80 +#define RF_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RF_PACONFIG_MAX_POWER_MASK 0x8F + +#define RF_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaRamp + */ +#define RF_PARAMP_MODULATIONSHAPING_MASK 0x9F +#define RF_PARAMP_MODULATIONSHAPING_00 0x00 // Default +#define RF_PARAMP_MODULATIONSHAPING_01 0x20 +#define RF_PARAMP_MODULATIONSHAPING_10 0x40 +#define RF_PARAMP_MODULATIONSHAPING_11 0x60 + +#define RF_PARAMP_LOWPNTXPLL_MASK 0xEF +#define RF_PARAMP_LOWPNTXPLL_OFF 0x10 +#define RF_PARAMP_LOWPNTXPLL_ON 0x00 // Default + +#define RF_PARAMP_MASK 0xF0 +#define RF_PARAMP_3400_US 0x00 +#define RF_PARAMP_2000_US 0x01 +#define RF_PARAMP_1000_US 0x02 +#define RF_PARAMP_0500_US 0x03 +#define RF_PARAMP_0250_US 0x04 +#define RF_PARAMP_0125_US 0x05 +#define RF_PARAMP_0100_US 0x06 +#define RF_PARAMP_0062_US 0x07 +#define RF_PARAMP_0050_US 0x08 +#define RF_PARAMP_0040_US 0x09 // Default +#define RF_PARAMP_0031_US 0x0A +#define RF_PARAMP_0025_US 0x0B +#define RF_PARAMP_0020_US 0x0C +#define RF_PARAMP_0015_US 0x0D +#define RF_PARAMP_0012_US 0x0E +#define RF_PARAMP_0010_US 0x0F + +/*! + * RegOcp + */ +#define RF_OCP_MASK 0xDF +#define RF_OCP_ON 0x20 // Default +#define RF_OCP_OFF 0x00 + +#define RF_OCP_TRIM_MASK 0xE0 +#define RF_OCP_TRIM_045_MA 0x00 +#define RF_OCP_TRIM_050_MA 0x01 +#define RF_OCP_TRIM_055_MA 0x02 +#define RF_OCP_TRIM_060_MA 0x03 +#define RF_OCP_TRIM_065_MA 0x04 +#define RF_OCP_TRIM_070_MA 0x05 +#define RF_OCP_TRIM_075_MA 0x06 +#define RF_OCP_TRIM_080_MA 0x07 +#define RF_OCP_TRIM_085_MA 0x08 +#define RF_OCP_TRIM_090_MA 0x09 +#define RF_OCP_TRIM_095_MA 0x0A +#define RF_OCP_TRIM_100_MA 0x0B // Default +#define RF_OCP_TRIM_105_MA 0x0C +#define RF_OCP_TRIM_110_MA 0x0D +#define RF_OCP_TRIM_115_MA 0x0E +#define RF_OCP_TRIM_120_MA 0x0F +#define RF_OCP_TRIM_130_MA 0x10 +#define RF_OCP_TRIM_140_MA 0x11 +#define RF_OCP_TRIM_150_MA 0x12 +#define RF_OCP_TRIM_160_MA 0x13 +#define RF_OCP_TRIM_170_MA 0x14 +#define RF_OCP_TRIM_180_MA 0x15 +#define RF_OCP_TRIM_190_MA 0x16 +#define RF_OCP_TRIM_200_MA 0x17 +#define RF_OCP_TRIM_210_MA 0x18 +#define RF_OCP_TRIM_220_MA 0x19 +#define RF_OCP_TRIM_230_MA 0x1A +#define RF_OCP_TRIM_240_MA 0x1B + +/*! + * RegLna + */ +#define RF_LNA_GAIN_MASK 0x1F +#define RF_LNA_GAIN_G1 0x20 // Default +#define RF_LNA_GAIN_G2 0x40 +#define RF_LNA_GAIN_G3 0x60 +#define RF_LNA_GAIN_G4 0x80 +#define RF_LNA_GAIN_G5 0xA0 +#define RF_LNA_GAIN_G6 0xC0 + +#define RF_LNA_BOOST_MASK 0xFC +#define RF_LNA_BOOST_OFF 0x00 // Default +#define RF_LNA_BOOST_ON 0x03 + +/*! + * RegRxConfig + */ +#define RF_RXCONFIG_RESTARTRXONCOLLISION_MASK 0x7F +#define RF_RXCONFIG_RESTARTRXONCOLLISION_ON 0x80 +#define RF_RXCONFIG_RESTARTRXONCOLLISION_OFF 0x00 // Default + +#define RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK 0x40 // Write only + +#define RF_RXCONFIG_RESTARTRXWITHPLLLOCK 0x20 // Write only + +#define RF_RXCONFIG_AFCAUTO_MASK 0xEF +#define RF_RXCONFIG_AFCAUTO_ON 0x10 +#define RF_RXCONFIG_AFCAUTO_OFF 0x00 // Default + +#define RF_RXCONFIG_AGCAUTO_MASK 0xF7 +#define RF_RXCONFIG_AGCAUTO_ON 0x08 // Default +#define RF_RXCONFIG_AGCAUTO_OFF 0x00 + +#define RF_RXCONFIG_RXTRIGER_MASK 0xF8 +#define RF_RXCONFIG_RXTRIGER_OFF 0x00 +#define RF_RXCONFIG_RXTRIGER_RSSI 0x01 +#define RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT 0x06 // Default +#define RF_RXCONFIG_RXTRIGER_RSSI_PREAMBLEDETECT 0x07 + +/*! + * RegRssiConfig + */ +#define RF_RSSICONFIG_OFFSET_MASK 0x07 +#define RF_RSSICONFIG_OFFSET_P_00_DB 0x00 // Default +#define RF_RSSICONFIG_OFFSET_P_01_DB 0x08 +#define RF_RSSICONFIG_OFFSET_P_02_DB 0x10 +#define RF_RSSICONFIG_OFFSET_P_03_DB 0x18 +#define RF_RSSICONFIG_OFFSET_P_04_DB 0x20 +#define RF_RSSICONFIG_OFFSET_P_05_DB 0x28 +#define RF_RSSICONFIG_OFFSET_P_06_DB 0x30 +#define RF_RSSICONFIG_OFFSET_P_07_DB 0x38 +#define RF_RSSICONFIG_OFFSET_P_08_DB 0x40 +#define RF_RSSICONFIG_OFFSET_P_09_DB 0x48 +#define RF_RSSICONFIG_OFFSET_P_10_DB 0x50 +#define RF_RSSICONFIG_OFFSET_P_11_DB 0x58 +#define RF_RSSICONFIG_OFFSET_P_12_DB 0x60 +#define RF_RSSICONFIG_OFFSET_P_13_DB 0x68 +#define RF_RSSICONFIG_OFFSET_P_14_DB 0x70 +#define RF_RSSICONFIG_OFFSET_P_15_DB 0x78 +#define RF_RSSICONFIG_OFFSET_M_16_DB 0x80 +#define RF_RSSICONFIG_OFFSET_M_15_DB 0x88 +#define RF_RSSICONFIG_OFFSET_M_14_DB 0x90 +#define RF_RSSICONFIG_OFFSET_M_13_DB 0x98 +#define RF_RSSICONFIG_OFFSET_M_12_DB 0xA0 +#define RF_RSSICONFIG_OFFSET_M_11_DB 0xA8 +#define RF_RSSICONFIG_OFFSET_M_10_DB 0xB0 +#define RF_RSSICONFIG_OFFSET_M_09_DB 0xB8 +#define RF_RSSICONFIG_OFFSET_M_08_DB 0xC0 +#define RF_RSSICONFIG_OFFSET_M_07_DB 0xC8 +#define RF_RSSICONFIG_OFFSET_M_06_DB 0xD0 +#define RF_RSSICONFIG_OFFSET_M_05_DB 0xD8 +#define RF_RSSICONFIG_OFFSET_M_04_DB 0xE0 +#define RF_RSSICONFIG_OFFSET_M_03_DB 0xE8 +#define RF_RSSICONFIG_OFFSET_M_02_DB 0xF0 +#define RF_RSSICONFIG_OFFSET_M_01_DB 0xF8 + +#define RF_RSSICONFIG_SMOOTHING_MASK 0xF8 +#define RF_RSSICONFIG_SMOOTHING_2 0x00 +#define RF_RSSICONFIG_SMOOTHING_4 0x01 +#define RF_RSSICONFIG_SMOOTHING_8 0x02 // Default +#define RF_RSSICONFIG_SMOOTHING_16 0x03 +#define RF_RSSICONFIG_SMOOTHING_32 0x04 +#define RF_RSSICONFIG_SMOOTHING_64 0x05 +#define RF_RSSICONFIG_SMOOTHING_128 0x06 +#define RF_RSSICONFIG_SMOOTHING_256 0x07 + +/*! + * RegRssiCollision + */ +#define RF_RSSICOLISION_THRESHOLD 0x0A // Default + +/*! + * RegRssiThresh + */ +#define RF_RSSITHRESH_THRESHOLD 0xFF // Default + +/*! + * RegRssiValue (Read Only) + */ + +/*! + * RegRxBw + */ +#define RF_RXBW_MANT_MASK 0xE7 +#define RF_RXBW_MANT_16 0x00 +#define RF_RXBW_MANT_20 0x08 +#define RF_RXBW_MANT_24 0x10 // Default + +#define RF_RXBW_EXP_MASK 0xF8 +#define RF_RXBW_EXP_0 0x00 +#define RF_RXBW_EXP_1 0x01 +#define RF_RXBW_EXP_2 0x02 +#define RF_RXBW_EXP_3 0x03 +#define RF_RXBW_EXP_4 0x04 +#define RF_RXBW_EXP_5 0x05 // Default +#define RF_RXBW_EXP_6 0x06 +#define RF_RXBW_EXP_7 0x07 + +/*! + * RegAfcBw + */ +#define RF_AFCBW_MANTAFC_MASK 0xE7 +#define RF_AFCBW_MANTAFC_16 0x00 +#define RF_AFCBW_MANTAFC_20 0x08 // Default +#define RF_AFCBW_MANTAFC_24 0x10 + +#define RF_AFCBW_EXPAFC_MASK 0xF8 +#define RF_AFCBW_EXPAFC_0 0x00 +#define RF_AFCBW_EXPAFC_1 0x01 +#define RF_AFCBW_EXPAFC_2 0x02 +#define RF_AFCBW_EXPAFC_3 0x03 // Default +#define RF_AFCBW_EXPAFC_4 0x04 +#define RF_AFCBW_EXPAFC_5 0x05 +#define RF_AFCBW_EXPAFC_6 0x06 +#define RF_AFCBW_EXPAFC_7 0x07 + +/*! + * RegOokPeak + */ +#define RF_OOKPEAK_BITSYNC_MASK 0xDF // Default +#define RF_OOKPEAK_BITSYNC_ON 0x20 // Default +#define RF_OOKPEAK_BITSYNC_OFF 0x00 + +#define RF_OOKPEAK_OOKTHRESHTYPE_MASK 0xE7 +#define RF_OOKPEAK_OOKTHRESHTYPE_FIXED 0x00 +#define RF_OOKPEAK_OOKTHRESHTYPE_PEAK 0x08 // Default +#define RF_OOKPEAK_OOKTHRESHTYPE_AVERAGE 0x10 + +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_MASK 0xF8 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_0_5_DB 0x00 // Default +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_1_0_DB 0x01 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_1_5_DB 0x02 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_2_0_DB 0x03 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_3_0_DB 0x04 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_4_0_DB 0x05 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_5_0_DB 0x06 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_6_0_DB 0x07 + +/*! + * RegOokFix + */ +#define RF_OOKFIX_OOKFIXEDTHRESHOLD 0x0C // Default + +/*! + * RegOokAvg + */ +#define RF_OOKAVG_OOKPEAKTHRESHDEC_MASK 0x1F +#define RF_OOKAVG_OOKPEAKTHRESHDEC_000 0x00 // Default +#define RF_OOKAVG_OOKPEAKTHRESHDEC_001 0x20 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_010 0x40 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_011 0x60 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_100 0x80 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_101 0xA0 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_110 0xC0 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_111 0xE0 + +#define RF_OOKAVG_AVERAGEOFFSET_MASK 0xF3 +#define RF_OOKAVG_AVERAGEOFFSET_0_DB 0x00 // Default +#define RF_OOKAVG_AVERAGEOFFSET_2_DB 0x04 +#define RF_OOKAVG_AVERAGEOFFSET_4_DB 0x08 +#define RF_OOKAVG_AVERAGEOFFSET_6_DB 0x0C + +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_MASK 0xFC +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_00 0x00 +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_01 0x01 +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_10 0x02 // Default +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_11 0x03 + +/*! + * RegAfcFei + */ +#define RF_AFCFEI_AGCSTART 0x10 + +#define RF_AFCFEI_AFCCLEAR 0x02 + +#define RF_AFCFEI_AFCAUTOCLEAR_MASK 0xFE +#define RF_AFCFEI_AFCAUTOCLEAR_ON 0x01 +#define RF_AFCFEI_AFCAUTOCLEAR_OFF 0x00 // Default + +/*! + * RegAfcMsb (Read Only) + */ + +/*! + * RegAfcLsb (Read Only) + */ + +/*! + * RegFeiMsb (Read Only) + */ + +/*! + * RegFeiLsb (Read Only) + */ + +/*! + * RegPreambleDetect + */ +#define RF_PREAMBLEDETECT_DETECTOR_MASK 0x7F +#define RF_PREAMBLEDETECT_DETECTOR_ON 0x80 // Default +#define RF_PREAMBLEDETECT_DETECTOR_OFF 0x00 + +#define RF_PREAMBLEDETECT_DETECTORSIZE_MASK 0x9F +#define RF_PREAMBLEDETECT_DETECTORSIZE_1 0x00 +#define RF_PREAMBLEDETECT_DETECTORSIZE_2 0x20 // Default +#define RF_PREAMBLEDETECT_DETECTORSIZE_3 0x40 +#define RF_PREAMBLEDETECT_DETECTORSIZE_4 0x60 + +#define RF_PREAMBLEDETECT_DETECTORTOL_MASK 0xE0 +#define RF_PREAMBLEDETECT_DETECTORTOL_0 0x00 +#define RF_PREAMBLEDETECT_DETECTORTOL_1 0x01 +#define RF_PREAMBLEDETECT_DETECTORTOL_2 0x02 +#define RF_PREAMBLEDETECT_DETECTORTOL_3 0x03 +#define RF_PREAMBLEDETECT_DETECTORTOL_4 0x04 +#define RF_PREAMBLEDETECT_DETECTORTOL_5 0x05 +#define RF_PREAMBLEDETECT_DETECTORTOL_6 0x06 +#define RF_PREAMBLEDETECT_DETECTORTOL_7 0x07 +#define RF_PREAMBLEDETECT_DETECTORTOL_8 0x08 +#define RF_PREAMBLEDETECT_DETECTORTOL_9 0x09 +#define RF_PREAMBLEDETECT_DETECTORTOL_10 0x0A // Default +#define RF_PREAMBLEDETECT_DETECTORTOL_11 0x0B +#define RF_PREAMBLEDETECT_DETECTORTOL_12 0x0C +#define RF_PREAMBLEDETECT_DETECTORTOL_13 0x0D +#define RF_PREAMBLEDETECT_DETECTORTOL_14 0x0E +#define RF_PREAMBLEDETECT_DETECTORTOL_15 0x0F +#define RF_PREAMBLEDETECT_DETECTORTOL_16 0x10 +#define RF_PREAMBLEDETECT_DETECTORTOL_17 0x11 +#define RF_PREAMBLEDETECT_DETECTORTOL_18 0x12 +#define RF_PREAMBLEDETECT_DETECTORTOL_19 0x13 +#define RF_PREAMBLEDETECT_DETECTORTOL_20 0x14 +#define RF_PREAMBLEDETECT_DETECTORTOL_21 0x15 +#define RF_PREAMBLEDETECT_DETECTORTOL_22 0x16 +#define RF_PREAMBLEDETECT_DETECTORTOL_23 0x17 +#define RF_PREAMBLEDETECT_DETECTORTOL_24 0x18 +#define RF_PREAMBLEDETECT_DETECTORTOL_25 0x19 +#define RF_PREAMBLEDETECT_DETECTORTOL_26 0x1A +#define RF_PREAMBLEDETECT_DETECTORTOL_27 0x1B +#define RF_PREAMBLEDETECT_DETECTORTOL_28 0x1C +#define RF_PREAMBLEDETECT_DETECTORTOL_29 0x1D +#define RF_PREAMBLEDETECT_DETECTORTOL_30 0x1E +#define RF_PREAMBLEDETECT_DETECTORTOL_31 0x1F + +/*! + * RegRxTimeout1 + */ +#define RF_RXTIMEOUT1_TIMEOUTRXRSSI 0x00 // Default + +/*! + * RegRxTimeout2 + */ +#define RF_RXTIMEOUT2_TIMEOUTRXPREAMBLE 0x00 // Default + +/*! + * RegRxTimeout3 + */ +#define RF_RXTIMEOUT3_TIMEOUTSIGNALSYNC 0x00 // Default + +/*! + * RegRxDelay + */ +#define RF_RXDELAY_INTERPACKETRXDELAY 0x00 // Default + +/*! + * RegOsc + */ +#define RF_OSC_RCCALSTART 0x08 + +#define RF_OSC_CLKOUT_MASK 0xF8 +#define RF_OSC_CLKOUT_32_MHZ 0x00 +#define RF_OSC_CLKOUT_16_MHZ 0x01 +#define RF_OSC_CLKOUT_8_MHZ 0x02 +#define RF_OSC_CLKOUT_4_MHZ 0x03 +#define RF_OSC_CLKOUT_2_MHZ 0x04 +#define RF_OSC_CLKOUT_1_MHZ 0x05 // Default +#define RF_OSC_CLKOUT_RC 0x06 +#define RF_OSC_CLKOUT_OFF 0x07 + +/*! + * RegPreambleMsb/RegPreambleLsb + */ +#define RF_PREAMBLEMSB_SIZE 0x00 // Default +#define RF_PREAMBLELSB_SIZE 0x03 // Default + +/*! + * RegSyncConfig + */ +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_MASK 0x3F +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_WAITPLL_ON 0x80 // Default +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_WAITPLL_OFF 0x40 +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_OFF 0x00 + + +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_MASK 0xDF +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_55 0x20 +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_AA 0x00 // Default + +#define RF_SYNCCONFIG_SYNC_MASK 0xEF +#define RF_SYNCCONFIG_SYNC_ON 0x10 // Default +#define RF_SYNCCONFIG_SYNC_OFF 0x00 + + +#define RF_SYNCCONFIG_SYNCSIZE_MASK 0xF8 +#define RF_SYNCCONFIG_SYNCSIZE_1 0x00 +#define RF_SYNCCONFIG_SYNCSIZE_2 0x01 +#define RF_SYNCCONFIG_SYNCSIZE_3 0x02 +#define RF_SYNCCONFIG_SYNCSIZE_4 0x03 // Default +#define RF_SYNCCONFIG_SYNCSIZE_5 0x04 +#define RF_SYNCCONFIG_SYNCSIZE_6 0x05 +#define RF_SYNCCONFIG_SYNCSIZE_7 0x06 +#define RF_SYNCCONFIG_SYNCSIZE_8 0x07 + +/*! + * RegSyncValue1-8 + */ +#define RF_SYNCVALUE1_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE2_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE3_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE4_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE5_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE6_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE7_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE8_SYNCVALUE 0x01 // Default + +/*! + * RegPacketConfig1 + */ +#define RF_PACKETCONFIG1_PACKETFORMAT_MASK 0x7F +#define RF_PACKETCONFIG1_PACKETFORMAT_FIXED 0x00 +#define RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE 0x80 // Default + +#define RF_PACKETCONFIG1_DCFREE_MASK 0x9F +#define RF_PACKETCONFIG1_DCFREE_OFF 0x00 // Default +#define RF_PACKETCONFIG1_DCFREE_MANCHESTER 0x20 +#define RF_PACKETCONFIG1_DCFREE_WHITENING 0x40 + +#define RF_PACKETCONFIG1_CRC_MASK 0xEF +#define RF_PACKETCONFIG1_CRC_ON 0x10 // Default +#define RF_PACKETCONFIG1_CRC_OFF 0x00 + +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_MASK 0xF7 +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_ON 0x00 // Default +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_OFF 0x08 + +#define RF_PACKETCONFIG1_ADDRSFILTERING_MASK 0xF9 +#define RF_PACKETCONFIG1_ADDRSFILTERING_OFF 0x00 // Default +#define RF_PACKETCONFIG1_ADDRSFILTERING_NODE 0x02 +#define RF_PACKETCONFIG1_ADDRSFILTERING_NODEBROADCAST 0x04 + +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_MASK 0xFE +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_CCITT 0x00 // Default +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_IBM 0x01 + +/*! + * RegPacketConfig2 + */ + +#define RF_PACKETCONFIG2_WMBUS_CRC_ENABLE_MASK 0x7F +#define RF_PACKETCONFIG2_WMBUS_CRC_ENABLE 0x80 +#define RF_PACKETCONFIG2_WMBUS_CRC_DISABLE 0x00 // Default + +#define RF_PACKETCONFIG2_DATAMODE_MASK 0xBF +#define RF_PACKETCONFIG2_DATAMODE_CONTINUOUS 0x00 +#define RF_PACKETCONFIG2_DATAMODE_PACKET 0x40 // Default + +#define RF_PACKETCONFIG2_IOHOME_MASK 0xDF +#define RF_PACKETCONFIG2_IOHOME_ON 0x20 +#define RF_PACKETCONFIG2_IOHOME_OFF 0x00 // Default + +#define RF_PACKETCONFIG2_BEACON_MASK 0xF7 +#define RF_PACKETCONFIG2_BEACON_ON 0x08 +#define RF_PACKETCONFIG2_BEACON_OFF 0x00 // Default + +#define RF_PACKETCONFIG2_PAYLOADLENGTH_MSB_MASK 0xF8 + +/*! + * RegPayloadLength + */ +#define RF_PAYLOADLENGTH_LENGTH 0x40 // Default + +/*! + * RegNodeAdrs + */ +#define RF_NODEADDRESS_ADDRESS 0x00 + +/*! + * RegBroadcastAdrs + */ +#define RF_BROADCASTADDRESS_ADDRESS 0x00 + +/*! + * RegFifoThresh + */ +#define RF_FIFOTHRESH_TXSTARTCONDITION_MASK 0x7F +#define RF_FIFOTHRESH_TXSTARTCONDITION_FIFOTHRESH 0x00 // Default +#define RF_FIFOTHRESH_TXSTARTCONDITION_FIFONOTEMPTY 0x80 + +#define RF_FIFOTHRESH_FIFOTHRESHOLD_MASK 0xC0 +#define RF_FIFOTHRESH_FIFOTHRESHOLD_THRESHOLD 0x0F // Default + +/*! + * RegSeqConfig1 + */ +#define RF_SEQCONFIG1_SEQUENCER_START 0x80 + +#define RF_SEQCONFIG1_SEQUENCER_STOP 0x40 + +#define RF_SEQCONFIG1_IDLEMODE_MASK 0xDF +#define RF_SEQCONFIG1_IDLEMODE_SLEEP 0x20 +#define RF_SEQCONFIG1_IDLEMODE_STANDBY 0x00 // Default + +#define RF_SEQCONFIG1_FROMSTART_MASK 0xE7 +#define RF_SEQCONFIG1_FROMSTART_TOLPS 0x00 // Default +#define RF_SEQCONFIG1_FROMSTART_TORX 0x08 +#define RF_SEQCONFIG1_FROMSTART_TOTX 0x10 +#define RF_SEQCONFIG1_FROMSTART_TOTX_ONFIFOLEVEL 0x18 + +#define RF_SEQCONFIG1_LPS_MASK 0xFB +#define RF_SEQCONFIG1_LPS_SEQUENCER_OFF 0x00 // Default +#define RF_SEQCONFIG1_LPS_IDLE 0x04 + +#define RF_SEQCONFIG1_FROMIDLE_MASK 0xFD +#define RF_SEQCONFIG1_FROMIDLE_TOTX 0x00 // Default +#define RF_SEQCONFIG1_FROMIDLE_TORX 0x02 + +#define RF_SEQCONFIG1_FROMTX_MASK 0xFE +#define RF_SEQCONFIG1_FROMTX_TOLPS 0x00 // Default +#define RF_SEQCONFIG1_FROMTX_TORX 0x01 + +/*! + * RegSeqConfig2 + */ +#define RF_SEQCONFIG2_FROMRX_MASK 0x1F +#define RF_SEQCONFIG2_FROMRX_TOUNUSED_000 0x00 // Default +#define RF_SEQCONFIG2_FROMRX_TORXPKT_ONPLDRDY 0x20 +#define RF_SEQCONFIG2_FROMRX_TOLPS_ONPLDRDY 0x40 +#define RF_SEQCONFIG2_FROMRX_TORXPKT_ONCRCOK 0x60 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONRSSI 0x80 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONSYNC 0xA0 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONPREAMBLE 0xC0 +#define RF_SEQCONFIG2_FROMRX_TOUNUSED_111 0xE0 + +#define RF_SEQCONFIG2_FROMRXTIMEOUT_MASK 0xE7 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TORXRESTART 0x00 // Default +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOTX 0x08 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOLPS 0x10 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOSEQUENCEROFF 0x18 + +#define RF_SEQCONFIG2_FROMRXPKT_MASK 0xF8 +#define RF_SEQCONFIG2_FROMRXPKT_TOSEQUENCEROFF 0x00 // Default +#define RF_SEQCONFIG2_FROMRXPKT_TOTX_ONFIFOEMPTY 0x01 +#define RF_SEQCONFIG2_FROMRXPKT_TOLPS 0x02 +#define RF_SEQCONFIG2_FROMRXPKT_TOSYNTHESIZERRX 0x03 +#define RF_SEQCONFIG2_FROMRXPKT_TORX 0x04 + +/*! + * RegTimerResol + */ +#define RF_TIMERRESOL_TIMER1RESOL_MASK 0xF3 +#define RF_TIMERRESOL_TIMER1RESOL_OFF 0x00 // Default +#define RF_TIMERRESOL_TIMER1RESOL_000064_US 0x04 +#define RF_TIMERRESOL_TIMER1RESOL_004100_US 0x08 +#define RF_TIMERRESOL_TIMER1RESOL_262000_US 0x0C + +#define RF_TIMERRESOL_TIMER2RESOL_MASK 0xFC +#define RF_TIMERRESOL_TIMER2RESOL_OFF 0x00 // Default +#define RF_TIMERRESOL_TIMER2RESOL_000064_US 0x01 +#define RF_TIMERRESOL_TIMER2RESOL_004100_US 0x02 +#define RF_TIMERRESOL_TIMER2RESOL_262000_US 0x03 + +/*! + * RegTimer1Coef + */ +#define RF_TIMER1COEF_TIMER1COEFFICIENT 0xF5 // Default + +/*! + * RegTimer2Coef + */ +#define RF_TIMER2COEF_TIMER2COEFFICIENT 0x20 // Default + +/*! + * RegImageCal + */ +#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RF_IMAGECAL_IMAGECAL_MASK 0xBF +#define RF_IMAGECAL_IMAGECAL_START 0x40 + +#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 +#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default + +#define RF_IMAGECAL_TEMPCHANGE_HIGHER 0x08 +#define RF_IMAGECAL_TEMPCHANGE_LOWER 0x00 + +#define RF_IMAGECAL_TEMPTHRESHOLD_MASK 0xF9 +#define RF_IMAGECAL_TEMPTHRESHOLD_05 0x00 +#define RF_IMAGECAL_TEMPTHRESHOLD_10 0x02 // Default +#define RF_IMAGECAL_TEMPTHRESHOLD_15 0x04 +#define RF_IMAGECAL_TEMPTHRESHOLD_20 0x06 + +#define RF_IMAGECAL_TEMPMONITOR_MASK 0xFE +#define RF_IMAGECAL_TEMPMONITOR_ON 0x00 // Default +#define RF_IMAGECAL_TEMPMONITOR_OFF 0x01 + +/*! + * RegTemp (Read Only) + */ + +/*! + * RegLowBat + */ +#define RF_LOWBAT_MASK 0xF7 +#define RF_LOWBAT_ON 0x08 +#define RF_LOWBAT_OFF 0x00 // Default + +#define RF_LOWBAT_TRIM_MASK 0xF8 +#define RF_LOWBAT_TRIM_1695 0x00 +#define RF_LOWBAT_TRIM_1764 0x01 +#define RF_LOWBAT_TRIM_1835 0x02 // Default +#define RF_LOWBAT_TRIM_1905 0x03 +#define RF_LOWBAT_TRIM_1976 0x04 +#define RF_LOWBAT_TRIM_2045 0x05 +#define RF_LOWBAT_TRIM_2116 0x06 +#define RF_LOWBAT_TRIM_2185 0x07 + +/*! + * RegIrqFlags1 + */ +#define RF_IRQFLAGS1_MODEREADY 0x80 + +#define RF_IRQFLAGS1_RXREADY 0x40 + +#define RF_IRQFLAGS1_TXREADY 0x20 + +#define RF_IRQFLAGS1_PLLLOCK 0x10 + +#define RF_IRQFLAGS1_RSSI 0x08 + +#define RF_IRQFLAGS1_TIMEOUT 0x04 + +#define RF_IRQFLAGS1_PREAMBLEDETECT 0x02 + +#define RF_IRQFLAGS1_SYNCADDRESSMATCH 0x01 + +/*! + * RegIrqFlags2 + */ +#define RF_IRQFLAGS2_FIFOFULL 0x80 + +#define RF_IRQFLAGS2_FIFOEMPTY 0x40 + +#define RF_IRQFLAGS2_FIFOLEVEL 0x20 + +#define RF_IRQFLAGS2_FIFOOVERRUN 0x10 + +#define RF_IRQFLAGS2_PACKETSENT 0x08 + +#define RF_IRQFLAGS2_PAYLOADREADY 0x04 + +#define RF_IRQFLAGS2_CRCOK 0x02 + +#define RF_IRQFLAGS2_LOWBAT 0x01 + +/*! + * RegDioMapping1 + */ +#define RF_DIOMAPPING1_DIO0_MASK 0x3F +#define RF_DIOMAPPING1_DIO0_00 0x00 // Default +#define RF_DIOMAPPING1_DIO0_01 0x40 +#define RF_DIOMAPPING1_DIO0_10 0x80 +#define RF_DIOMAPPING1_DIO0_11 0xC0 + +#define RF_DIOMAPPING1_DIO1_MASK 0xCF +#define RF_DIOMAPPING1_DIO1_00 0x00 // Default +#define RF_DIOMAPPING1_DIO1_01 0x10 +#define RF_DIOMAPPING1_DIO1_10 0x20 +#define RF_DIOMAPPING1_DIO1_11 0x30 + +#define RF_DIOMAPPING1_DIO2_MASK 0xF3 +#define RF_DIOMAPPING1_DIO2_00 0x00 // Default +#define RF_DIOMAPPING1_DIO2_01 0x04 +#define RF_DIOMAPPING1_DIO2_10 0x08 +#define RF_DIOMAPPING1_DIO2_11 0x0C + +#define RF_DIOMAPPING1_DIO3_MASK 0xFC +#define RF_DIOMAPPING1_DIO3_00 0x00 // Default +#define RF_DIOMAPPING1_DIO3_01 0x01 +#define RF_DIOMAPPING1_DIO3_10 0x02 +#define RF_DIOMAPPING1_DIO3_11 0x03 + +/*! + * RegDioMapping2 + */ +#define RF_DIOMAPPING2_DIO4_MASK 0x3F +#define RF_DIOMAPPING2_DIO4_00 0x00 // Default +#define RF_DIOMAPPING2_DIO4_01 0x40 +#define RF_DIOMAPPING2_DIO4_10 0x80 +#define RF_DIOMAPPING2_DIO4_11 0xC0 + +#define RF_DIOMAPPING2_DIO5_MASK 0xCF +#define RF_DIOMAPPING2_DIO5_00 0x00 // Default +#define RF_DIOMAPPING2_DIO5_01 0x10 +#define RF_DIOMAPPING2_DIO5_10 0x20 +#define RF_DIOMAPPING2_DIO5_11 0x30 + +#define RF_DIOMAPPING2_MAP_MASK 0xFE +#define RF_DIOMAPPING2_MAP_PREAMBLEDETECT 0x01 +#define RF_DIOMAPPING2_MAP_RSSI 0x00 // Default + +/*! + * RegVersion (Read Only) + */ + +/*! + * RegPllHop + */ +#define RF_PLLHOP_FASTHOP_MASK 0x7F +#define RF_PLLHOP_FASTHOP_ON 0x80 +#define RF_PLLHOP_FASTHOP_OFF 0x00 // Default + +/*! + * RegTcxo + */ +#define RF_TCXO_TCXOINPUT_MASK 0xEF +#define RF_TCXO_TCXOINPUT_ON 0x10 +#define RF_TCXO_TCXOINPUT_OFF 0x00 // Default + +/*! + * RegPaDac + */ +#define RF_PADAC_20DBM_MASK 0xF8 +#define RF_PADAC_20DBM_ON 0x07 +#define RF_PADAC_20DBM_OFF 0x04 // Default + +/*! + * RegFormerTemp + */ + +/*! + * RegBitrateFrac + */ +#define RF_BITRATEFRAC_MASK 0xF0 + +/*! + * RegAgcRef + */ + +/*! + * RegAgcThresh1 + */ + +/*! + * RegAgcThresh2 + */ + +/*! + * RegAgcThresh3 + */ + +/*! + * RegPll + */ +#define RF_PLL_BANDWIDTH_MASK 0x3F +#define RF_PLL_BANDWIDTH_75 0x00 +#define RF_PLL_BANDWIDTH_150 0x40 +#define RF_PLL_BANDWIDTH_225 0x80 +#define RF_PLL_BANDWIDTH_300 0xC0 // Default + +#endif // __SX1276_REGS_FSK_H__ diff --git a/libraries/LoRa_Node/src/radio/sx1276/sx1276Regs-LoRa.h b/libraries/LoRa_Node/src/radio/sx1276/sx1276Regs-LoRa.h new file mode 100644 index 0000000..e2fc751 --- /dev/null +++ b/libraries/LoRa_Node/src/radio/sx1276/sx1276Regs-LoRa.h @@ -0,0 +1,565 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1276 LoRa modem registers and bits definitions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SX1276_REGS_LORA_H__ +#define __SX1276_REGS_LORA_H__ + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define REG_LR_FIFO 0x00 +// Common settings +#define REG_LR_OPMODE 0x01 +#define REG_LR_FRFMSB 0x06 +#define REG_LR_FRFMID 0x07 +#define REG_LR_FRFLSB 0x08 +// Tx settings +#define REG_LR_PACONFIG 0x09 +#define REG_LR_PARAMP 0x0A +#define REG_LR_OCP 0x0B +// Rx settings +#define REG_LR_LNA 0x0C +// LoRa registers +#define REG_LR_FIFOADDRPTR 0x0D +#define REG_LR_FIFOTXBASEADDR 0x0E +#define REG_LR_FIFORXBASEADDR 0x0F +#define REG_LR_FIFORXCURRENTADDR 0x10 +#define REG_LR_IRQFLAGSMASK 0x11 +#define REG_LR_IRQFLAGS 0x12 +#define REG_LR_RXNBBYTES 0x13 +#define REG_LR_RXHEADERCNTVALUEMSB 0x14 +#define REG_LR_RXHEADERCNTVALUELSB 0x15 +#define REG_LR_RXPACKETCNTVALUEMSB 0x16 +#define REG_LR_RXPACKETCNTVALUELSB 0x17 +#define REG_LR_MODEMSTAT 0x18 +#define REG_LR_PKTSNRVALUE 0x19 +#define REG_LR_PKTRSSIVALUE 0x1A +#define REG_LR_RSSIVALUE 0x1B +#define REG_LR_HOPCHANNEL 0x1C +#define REG_LR_MODEMCONFIG1 0x1D +#define REG_LR_MODEMCONFIG2 0x1E +#define REG_LR_SYMBTIMEOUTLSB 0x1F +#define REG_LR_PREAMBLEMSB 0x20 +#define REG_LR_PREAMBLELSB 0x21 +#define REG_LR_PAYLOADLENGTH 0x22 +#define REG_LR_PAYLOADMAXLENGTH 0x23 +#define REG_LR_HOPPERIOD 0x24 +#define REG_LR_FIFORXBYTEADDR 0x25 +#define REG_LR_MODEMCONFIG3 0x26 +#define REG_LR_FEIMSB 0x28 +#define REG_LR_FEIMID 0x29 +#define REG_LR_FEILSB 0x2A +#define REG_LR_RSSIWIDEBAND 0x2C +#define REG_LR_TEST2F 0x2F +#define REG_LR_TEST30 0x30 +#define REG_LR_DETECTOPTIMIZE 0x31 +#define REG_LR_INVERTIQ 0x33 +#define REG_LR_TEST36 0x36 +#define REG_LR_DETECTIONTHRESHOLD 0x37 +#define REG_LR_SYNCWORD 0x39 +#define REG_LR_TEST3A 0x3A +#define REG_LR_INVERTIQ2 0x3B + +// end of documented register in datasheet +// I/O settings +#define REG_LR_DIOMAPPING1 0x40 +#define REG_LR_DIOMAPPING2 0x41 +// Version +#define REG_LR_VERSION 0x42 +// Additional settings +#define REG_LR_PLLHOP 0x44 +#define REG_LR_TCXO 0x4B +#define REG_LR_PADAC 0x4D +#define REG_LR_FORMERTEMP 0x5B +#define REG_LR_BITRATEFRAC 0x5D +#define REG_LR_AGCREF 0x61 +#define REG_LR_AGCTHRESH1 0x62 +#define REG_LR_AGCTHRESH2 0x63 +#define REG_LR_AGCTHRESH3 0x64 +#define REG_LR_PLL 0x70 + +/*! + * ============================================================================ + * SX1276 LoRa bits control definition + * ============================================================================ + */ + +/*! + * RegFifo + */ + +/*! + * RegOpMode + */ +#define RFLR_OPMODE_LONGRANGEMODE_MASK 0x7F +#define RFLR_OPMODE_LONGRANGEMODE_OFF 0x00 // Default +#define RFLR_OPMODE_LONGRANGEMODE_ON 0x80 + +#define RFLR_OPMODE_ACCESSSHAREDREG_MASK 0xBF +#define RFLR_OPMODE_ACCESSSHAREDREG_ENABLE 0x40 +#define RFLR_OPMODE_ACCESSSHAREDREG_DISABLE 0x00 // Default + +#define RFLR_OPMODE_FREQMODE_ACCESS_MASK 0xF7 +#define RFLR_OPMODE_FREQMODE_ACCESS_LF 0x08 // Default +#define RFLR_OPMODE_FREQMODE_ACCESS_HF 0x00 + +#define RFLR_OPMODE_MASK 0xF8 +#define RFLR_OPMODE_SLEEP 0x00 +#define RFLR_OPMODE_STANDBY 0x01 // Default +#define RFLR_OPMODE_SYNTHESIZER_TX 0x02 +#define RFLR_OPMODE_TRANSMITTER 0x03 +#define RFLR_OPMODE_SYNTHESIZER_RX 0x04 +#define RFLR_OPMODE_RECEIVER 0x05 +// LoRa specific modes +#define RFLR_OPMODE_RECEIVER_SINGLE 0x06 +#define RFLR_OPMODE_CAD 0x07 + +/*! + * RegFrf (MHz) + */ +#define RFLR_FRFMSB_434_MHZ 0x6C // Default +#define RFLR_FRFMID_434_MHZ 0x80 // Default +#define RFLR_FRFLSB_434_MHZ 0x00 // Default + +/*! + * RegPaConfig + */ +#define RFLR_PACONFIG_PASELECT_MASK 0x7F +#define RFLR_PACONFIG_PASELECT_PABOOST 0x80 +#define RFLR_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RFLR_PACONFIG_MAX_POWER_MASK 0x8F + +#define RFLR_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaRamp + */ +#define RFLR_PARAMP_TXBANDFORCE_MASK 0xEF +#define RFLR_PARAMP_TXBANDFORCE_BAND_SEL 0x10 +#define RFLR_PARAMP_TXBANDFORCE_AUTO 0x00 // Default + +#define RFLR_PARAMP_MASK 0xF0 +#define RFLR_PARAMP_3400_US 0x00 +#define RFLR_PARAMP_2000_US 0x01 +#define RFLR_PARAMP_1000_US 0x02 +#define RFLR_PARAMP_0500_US 0x03 +#define RFLR_PARAMP_0250_US 0x04 +#define RFLR_PARAMP_0125_US 0x05 +#define RFLR_PARAMP_0100_US 0x06 +#define RFLR_PARAMP_0062_US 0x07 +#define RFLR_PARAMP_0050_US 0x08 +#define RFLR_PARAMP_0040_US 0x09 // Default +#define RFLR_PARAMP_0031_US 0x0A +#define RFLR_PARAMP_0025_US 0x0B +#define RFLR_PARAMP_0020_US 0x0C +#define RFLR_PARAMP_0015_US 0x0D +#define RFLR_PARAMP_0012_US 0x0E +#define RFLR_PARAMP_0010_US 0x0F + +/*! + * RegOcp + */ +#define RFLR_OCP_MASK 0xDF +#define RFLR_OCP_ON 0x20 // Default +#define RFLR_OCP_OFF 0x00 + +#define RFLR_OCP_TRIM_MASK 0xE0 +#define RFLR_OCP_TRIM_045_MA 0x00 +#define RFLR_OCP_TRIM_050_MA 0x01 +#define RFLR_OCP_TRIM_055_MA 0x02 +#define RFLR_OCP_TRIM_060_MA 0x03 +#define RFLR_OCP_TRIM_065_MA 0x04 +#define RFLR_OCP_TRIM_070_MA 0x05 +#define RFLR_OCP_TRIM_075_MA 0x06 +#define RFLR_OCP_TRIM_080_MA 0x07 +#define RFLR_OCP_TRIM_085_MA 0x08 +#define RFLR_OCP_TRIM_090_MA 0x09 +#define RFLR_OCP_TRIM_095_MA 0x0A +#define RFLR_OCP_TRIM_100_MA 0x0B // Default +#define RFLR_OCP_TRIM_105_MA 0x0C +#define RFLR_OCP_TRIM_110_MA 0x0D +#define RFLR_OCP_TRIM_115_MA 0x0E +#define RFLR_OCP_TRIM_120_MA 0x0F +#define RFLR_OCP_TRIM_130_MA 0x10 +#define RFLR_OCP_TRIM_140_MA 0x11 +#define RFLR_OCP_TRIM_150_MA 0x12 +#define RFLR_OCP_TRIM_160_MA 0x13 +#define RFLR_OCP_TRIM_170_MA 0x14 +#define RFLR_OCP_TRIM_180_MA 0x15 +#define RFLR_OCP_TRIM_190_MA 0x16 +#define RFLR_OCP_TRIM_200_MA 0x17 +#define RFLR_OCP_TRIM_210_MA 0x18 +#define RFLR_OCP_TRIM_220_MA 0x19 +#define RFLR_OCP_TRIM_230_MA 0x1A +#define RFLR_OCP_TRIM_240_MA 0x1B + +/*! + * RegLna + */ +#define RFLR_LNA_GAIN_MASK 0x1F +#define RFLR_LNA_GAIN_G1 0x20 // Default +#define RFLR_LNA_GAIN_G2 0x40 +#define RFLR_LNA_GAIN_G3 0x60 +#define RFLR_LNA_GAIN_G4 0x80 +#define RFLR_LNA_GAIN_G5 0xA0 +#define RFLR_LNA_GAIN_G6 0xC0 + +#define RFLR_LNA_BOOST_LF_MASK 0xE7 +#define RFLR_LNA_BOOST_LF_DEFAULT 0x00 // Default + +#define RFLR_LNA_BOOST_HF_MASK 0xFC +#define RFLR_LNA_BOOST_HF_OFF 0x00 // Default +#define RFLR_LNA_BOOST_HF_ON 0x03 + +/*! + * RegFifoAddrPtr + */ +#define RFLR_FIFOADDRPTR 0x00 // Default + +/*! + * RegFifoTxBaseAddr + */ +#define RFLR_FIFOTXBASEADDR 0x80 // Default + +/*! + * RegFifoTxBaseAddr + */ +#define RFLR_FIFORXBASEADDR 0x00 // Default + +/*! + * RegFifoRxCurrentAddr (Read Only) + */ + +/*! + * RegIrqFlagsMask + */ +#define RFLR_IRQFLAGS_RXTIMEOUT_MASK 0x80 +#define RFLR_IRQFLAGS_RXDONE_MASK 0x40 +#define RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK 0x20 +#define RFLR_IRQFLAGS_VALIDHEADER_MASK 0x10 +#define RFLR_IRQFLAGS_TXDONE_MASK 0x08 +#define RFLR_IRQFLAGS_CADDONE_MASK 0x04 +#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL_MASK 0x02 +#define RFLR_IRQFLAGS_CADDETECTED_MASK 0x01 + +/*! + * RegIrqFlags + */ +#define RFLR_IRQFLAGS_RXTIMEOUT 0x80 +#define RFLR_IRQFLAGS_RXDONE 0x40 +#define RFLR_IRQFLAGS_PAYLOADCRCERROR 0x20 +#define RFLR_IRQFLAGS_VALIDHEADER 0x10 +#define RFLR_IRQFLAGS_TXDONE 0x08 +#define RFLR_IRQFLAGS_CADDONE 0x04 +#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL 0x02 +#define RFLR_IRQFLAGS_CADDETECTED 0x01 + +/*! + * RegFifoRxNbBytes (Read Only) + */ + +/*! + * RegRxHeaderCntValueMsb (Read Only) + */ + +/*! + * RegRxHeaderCntValueLsb (Read Only) + */ + +/*! + * RegRxPacketCntValueMsb (Read Only) + */ + +/*! + * RegRxPacketCntValueLsb (Read Only) + */ + +/*! + * RegModemStat (Read Only) + */ +#define RFLR_MODEMSTAT_RX_CR_MASK 0x1F +#define RFLR_MODEMSTAT_MODEM_STATUS_MASK 0xE0 + +/*! + * RegPktSnrValue (Read Only) + */ + +/*! + * RegPktRssiValue (Read Only) + */ + +/*! + * RegRssiValue (Read Only) + */ + +/*! + * RegHopChannel (Read Only) + */ +#define RFLR_HOPCHANNEL_PLL_LOCK_TIMEOUT_MASK 0x7F +#define RFLR_HOPCHANNEL_PLL_LOCK_FAIL 0x80 +#define RFLR_HOPCHANNEL_PLL_LOCK_SUCCEED 0x00 // Default + +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_MASK 0xBF +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_ON 0x40 +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_OFF 0x00 // Default + +#define RFLR_HOPCHANNEL_CHANNEL_MASK 0x3F + +/*! + * RegModemConfig1 + */ +#define RFLR_MODEMCONFIG1_BW_MASK 0x0F +#define RFLR_MODEMCONFIG1_BW_7_81_KHZ 0x00 +#define RFLR_MODEMCONFIG1_BW_10_41_KHZ 0x10 +#define RFLR_MODEMCONFIG1_BW_15_62_KHZ 0x20 +#define RFLR_MODEMCONFIG1_BW_20_83_KHZ 0x30 +#define RFLR_MODEMCONFIG1_BW_31_25_KHZ 0x40 +#define RFLR_MODEMCONFIG1_BW_41_66_KHZ 0x50 +#define RFLR_MODEMCONFIG1_BW_62_50_KHZ 0x60 +#define RFLR_MODEMCONFIG1_BW_125_KHZ 0x70 // Default +#define RFLR_MODEMCONFIG1_BW_250_KHZ 0x80 +#define RFLR_MODEMCONFIG1_BW_500_KHZ 0x90 + +#define RFLR_MODEMCONFIG1_CODINGRATE_MASK 0xF1 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_5 0x02 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_6 0x04 // Default +#define RFLR_MODEMCONFIG1_CODINGRATE_4_7 0x06 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_8 0x08 + +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK 0xFE +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_ON 0x01 +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_OFF 0x00 // Default + +/*! + * RegModemConfig2 + */ +#define RFLR_MODEMCONFIG2_SF_MASK 0x0F +#define RFLR_MODEMCONFIG2_SF_6 0x60 +#define RFLR_MODEMCONFIG2_SF_7 0x70 // Default +#define RFLR_MODEMCONFIG2_SF_8 0x80 +#define RFLR_MODEMCONFIG2_SF_9 0x90 +#define RFLR_MODEMCONFIG2_SF_10 0xA0 +#define RFLR_MODEMCONFIG2_SF_11 0xB0 +#define RFLR_MODEMCONFIG2_SF_12 0xC0 + +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_MASK 0xF7 +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_ON 0x08 +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_OFF 0x00 + +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK 0xFB +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_ON 0x04 +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_OFF 0x00 // Default + +#define RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK 0xFC +#define RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB 0x00 // Default + +/*! + * RegSymbTimeoutLsb + */ +#define RFLR_SYMBTIMEOUTLSB_SYMBTIMEOUT 0x64 // Default + +/*! + * RegPreambleLengthMsb + */ +#define RFLR_PREAMBLELENGTHMSB 0x00 // Default + +/*! + * RegPreambleLengthLsb + */ +#define RFLR_PREAMBLELENGTHLSB 0x08 // Default + +/*! + * RegPayloadLength + */ +#define RFLR_PAYLOADLENGTH 0x0E // Default + +/*! + * RegPayloadMaxLength + */ +#define RFLR_PAYLOADMAXLENGTH 0xFF // Default + +/*! + * RegHopPeriod + */ +#define RFLR_HOPPERIOD_FREQFOPPINGPERIOD 0x00 // Default + +/*! + * RegFifoRxByteAddr (Read Only) + */ + +/*! + * RegModemConfig3 + */ +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK 0xF7 +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_ON 0x08 +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_OFF 0x00 // Default + +#define RFLR_MODEMCONFIG3_AGCAUTO_MASK 0xFB +#define RFLR_MODEMCONFIG3_AGCAUTO_ON 0x04 // Default +#define RFLR_MODEMCONFIG3_AGCAUTO_OFF 0x00 + +/*! + * RegFeiMsb (Read Only) + */ + +/*! + * RegFeiMid (Read Only) + */ + +/*! + * RegFeiLsb (Read Only) + */ + +/*! + * RegRssiWideband (Read Only) + */ + +/*! + * RegDetectOptimize + */ +#define RFLR_DETECTIONOPTIMIZE_MASK 0xF8 +#define RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 0x03 // Default +#define RFLR_DETECTIONOPTIMIZE_SF6 0x05 + +/*! + * RegInvertIQ + */ +#define RFLR_INVERTIQ_RX_MASK 0xBF +#define RFLR_INVERTIQ_RX_OFF 0x00 +#define RFLR_INVERTIQ_RX_ON 0x40 +#define RFLR_INVERTIQ_TX_MASK 0xFE +#define RFLR_INVERTIQ_TX_OFF 0x01 +#define RFLR_INVERTIQ_TX_ON 0x00 + +/*! + * RegDetectionThreshold + */ +#define RFLR_DETECTIONTHRESH_SF7_TO_SF12 0x0A // Default +#define RFLR_DETECTIONTHRESH_SF6 0x0C + +/*! + * RegInvertIQ2 + */ +#define RFLR_INVERTIQ2_ON 0x19 +#define RFLR_INVERTIQ2_OFF 0x1D + +/*! + * RegDioMapping1 + */ +#define RFLR_DIOMAPPING1_DIO0_MASK 0x3F +#define RFLR_DIOMAPPING1_DIO0_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO0_01 0x40 +#define RFLR_DIOMAPPING1_DIO0_10 0x80 +#define RFLR_DIOMAPPING1_DIO0_11 0xC0 + +#define RFLR_DIOMAPPING1_DIO1_MASK 0xCF +#define RFLR_DIOMAPPING1_DIO1_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO1_01 0x10 +#define RFLR_DIOMAPPING1_DIO1_10 0x20 +#define RFLR_DIOMAPPING1_DIO1_11 0x30 + +#define RFLR_DIOMAPPING1_DIO2_MASK 0xF3 +#define RFLR_DIOMAPPING1_DIO2_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO2_01 0x04 +#define RFLR_DIOMAPPING1_DIO2_10 0x08 +#define RFLR_DIOMAPPING1_DIO2_11 0x0C + +#define RFLR_DIOMAPPING1_DIO3_MASK 0xFC +#define RFLR_DIOMAPPING1_DIO3_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO3_01 0x01 +#define RFLR_DIOMAPPING1_DIO3_10 0x02 +#define RFLR_DIOMAPPING1_DIO3_11 0x03 + +/*! + * RegDioMapping2 + */ +#define RFLR_DIOMAPPING2_DIO4_MASK 0x3F +#define RFLR_DIOMAPPING2_DIO4_00 0x00 // Default +#define RFLR_DIOMAPPING2_DIO4_01 0x40 +#define RFLR_DIOMAPPING2_DIO4_10 0x80 +#define RFLR_DIOMAPPING2_DIO4_11 0xC0 + +#define RFLR_DIOMAPPING2_DIO5_MASK 0xCF +#define RFLR_DIOMAPPING2_DIO5_00 0x00 // Default +#define RFLR_DIOMAPPING2_DIO5_01 0x10 +#define RFLR_DIOMAPPING2_DIO5_10 0x20 +#define RFLR_DIOMAPPING2_DIO5_11 0x30 + +#define RFLR_DIOMAPPING2_MAP_MASK 0xFE +#define RFLR_DIOMAPPING2_MAP_PREAMBLEDETECT 0x01 +#define RFLR_DIOMAPPING2_MAP_RSSI 0x00 // Default + +/*! + * RegVersion (Read Only) + */ + +/*! + * RegPllHop + */ +#define RFLR_PLLHOP_FASTHOP_MASK 0x7F +#define RFLR_PLLHOP_FASTHOP_ON 0x80 +#define RFLR_PLLHOP_FASTHOP_OFF 0x00 // Default + +/*! + * RegTcxo + */ +#define RFLR_TCXO_TCXOINPUT_MASK 0xEF +#define RFLR_TCXO_TCXOINPUT_ON 0x10 +#define RFLR_TCXO_TCXOINPUT_OFF 0x00 // Default + +/*! + * RegPaDac + */ +#define RFLR_PADAC_20DBM_MASK 0xF8 +#define RFLR_PADAC_20DBM_ON 0x07 +#define RFLR_PADAC_20DBM_OFF 0x04 // Default + +/*! + * RegFormerTemp + */ + +/*! + * RegBitrateFrac + */ +#define RF_BITRATEFRAC_MASK 0xF0 + +/*! + * RegAgcRef + */ + +/*! + * RegAgcThresh1 + */ + +/*! + * RegAgcThresh2 + */ + +/*! + * RegAgcThresh3 + */ + +/*! + * RegPll + */ +#define RF_PLL_BANDWIDTH_MASK 0x3F +#define RF_PLL_BANDWIDTH_75 0x00 +#define RF_PLL_BANDWIDTH_150 0x40 +#define RF_PLL_BANDWIDTH_225 0x80 +#define RF_PLL_BANDWIDTH_300 0xC0 // Default + +#endif // __SX1276_REGS_LORA_H__ diff --git a/libraries/LoRa_Node/src/system/adc.c b/libraries/LoRa_Node/src/system/adc.c new file mode 100644 index 0000000..05796c3 --- /dev/null +++ b/libraries/LoRa_Node/src/system/adc.c @@ -0,0 +1,52 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements a generic ADC driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//-------------------modified +// #include "board.h" +#include "../boards/arduino/board.h" +#include "../boards/arduino/adc-board.h" +//------------------------- + +/*! + * Flag to indicates if the ADC is initialized + */ +static bool AdcInitialized = false; + +void AdcInit( Adc_t *obj, PinNames adcInput ) +{ + if( AdcInitialized == false ) + { + AdcInitialized = true; + + AdcMcuInit( obj, adcInput ); + AdcMcuConfig( ); + } +} + +void AdcDeInit( Adc_t *obj ) +{ + AdcInitialized = false; +} + +uint16_t AdcReadChannel( Adc_t *obj, uint32_t channel ) +{ + if( AdcInitialized == true ) + { + return AdcMcuReadChannel( obj, channel ); + } + else + { + return 0; + } +} diff --git a/libraries/LoRa_Node/src/system/adc.h b/libraries/LoRa_Node/src/system/adc.h new file mode 100644 index 0000000..752da3a --- /dev/null +++ b/libraries/LoRa_Node/src/system/adc.h @@ -0,0 +1,50 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements a generic ADC driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __ADC_H__ +#define __ADC_H__ + +/*! + * ADC object type definition + */ +typedef struct +{ + Gpio_t AdcInput; +}Adc_t; + +/*! + * \brief Initializes the ADC input + * + * \param [IN] obj ADC object + * \param [IN] scl ADC input pin name to be used + */ +void AdcInit( Adc_t *obj, PinNames adcInput ); + +/*! + * \brief DeInitializes the ADC + * + * \param [IN] obj ADC object + */ +void AdcDeInit( Adc_t *obj ); + +/*! + * \brief Read the analogue voltage value + * + * \param [IN] obj ADC object + * \param [IN] channel ADC channel + * \retval value Analogue pin value + */ +uint16_t AdcReadChannel( Adc_t *obj, uint32_t channel ); + +#endif // __ADC_H__ diff --git a/libraries/LoRa_Node/src/system/crypto/aes.c b/libraries/LoRa_Node/src/system/crypto/aes.c new file mode 100644 index 0000000..7f46ea3 --- /dev/null +++ b/libraries/LoRa_Node/src/system/crypto/aes.c @@ -0,0 +1,936 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* define if you have a fast memcpy function on your system */ +#if 0 +# define HAVE_MEMCPY +# include +# if defined( _MSC_VER ) +# include +# pragma intrinsic( memcpy ) +# endif +#endif + + +#include +#include + +/* define if you have fast 32-bit types on your system */ +#if ( __CORTEX_M != 0 ) // if Cortex is different from M0/M0+ +# define HAVE_UINT_32T +#endif + +/* define if you don't want any tables */ +#if 1 +# define USE_TABLES +#endif + +/* On Intel Core 2 duo VERSION_1 is faster */ + +/* alternative versions (test for performance on your system) */ +#if 1 +# define VERSION_1 +#endif + +#include "aes.h" + +//#if defined( HAVE_UINT_32T ) +// typedef unsigned long uint32_t; +//#endif + +/* functions for finite field multiplication in the AES Galois field */ + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \ + ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#if defined( USE_TABLES ) + +#define sb_data(w) { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +static const uint8_t sbox[256] = sb_data(f1); + +#if defined( AES_DEC_PREKEYED ) +static const uint8_t isbox[256] = isb_data(f1); +#endif + +static const uint8_t gfm2_sbox[256] = sb_data(f2); +static const uint8_t gfm3_sbox[256] = sb_data(f3); + +#if defined( AES_DEC_PREKEYED ) +static const uint8_t gfmul_9[256] = mm_data(f9); +static const uint8_t gfmul_b[256] = mm_data(fb); +static const uint8_t gfmul_d[256] = mm_data(fd); +static const uint8_t gfmul_e[256] = mm_data(fe); +#endif + +#define s_box(x) sbox[(x)] +#if defined( AES_DEC_PREKEYED ) +#define is_box(x) isbox[(x)] +#endif +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#if defined( AES_DEC_PREKEYED ) +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] +#endif +#else + +/* this is the high bit of x right shifted by 1 */ +/* position. Since the starting polynomial has */ +/* 9 bits (0x11b), this right shift keeps the */ +/* values of all top bits within a byte */ + +static uint8_t hibit(const uint8_t x) +{ uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) +{ uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) + return x; + + for( ; ; ) + { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= (v1 * n2); /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) +{ +#if defined( HAVE_UINT_32T ) + uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) + ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4); +#endif +} + +uint8_t inv_affine(const uint8_t x) +{ +#if defined( HAVE_UINT_32T ) + uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) + ^ (x >> 7) ^ (x >> 5) ^ (x >> 2); +#endif +} + +#define s_box(x) fwd_affine(gf_inv(x)) +#define is_box(x) gf_inv(inv_affine(x)) +#define gfm2_sb(x) f2(s_box(x)) +#define gfm3_sb(x) f3(s_box(x)) +#define gfm_9(x) f9(x) +#define gfm_b(x) fb(x) +#define gfm_d(x) fd(x) +#define gfm_e(x) fe(x) + +#endif + +#if defined( HAVE_MEMCPY ) +# define block_copy_nn(d, s, l) memcpy(d, s, l) +# define block_copy(d, s) memcpy(d, s, N_BLOCK) +#else +# define block_copy_nn(d, s, l) copy_block_nn(d, s, l) +# define block_copy(d, s) copy_block(d, s) +#endif + +static void copy_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0]; + ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1]; + ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2]; + ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3]; +#else + ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0]; + ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1]; + ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2]; + ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3]; + ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4]; + ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5]; + ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6]; + ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7]; + ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8]; + ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9]; + ((uint8_t*)d)[10] = ((uint8_t*)s)[10]; + ((uint8_t*)d)[11] = ((uint8_t*)s)[11]; + ((uint8_t*)d)[12] = ((uint8_t*)s)[12]; + ((uint8_t*)d)[13] = ((uint8_t*)s)[13]; + ((uint8_t*)d)[14] = ((uint8_t*)s)[14]; + ((uint8_t*)d)[15] = ((uint8_t*)s)[15]; +#endif +} + +static void copy_block_nn( uint8_t * d, const uint8_t *s, uint8_t nn ) +{ + while( nn-- ) + //*((uint8_t*)d)++ = *((uint8_t*)s)++; + *d++ = *s++; +} + +static void xor_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] ^= ((uint32_t*)s)[ 0]; + ((uint32_t*)d)[ 1] ^= ((uint32_t*)s)[ 1]; + ((uint32_t*)d)[ 2] ^= ((uint32_t*)s)[ 2]; + ((uint32_t*)d)[ 3] ^= ((uint32_t*)s)[ 3]; +#else + ((uint8_t*)d)[ 0] ^= ((uint8_t*)s)[ 0]; + ((uint8_t*)d)[ 1] ^= ((uint8_t*)s)[ 1]; + ((uint8_t*)d)[ 2] ^= ((uint8_t*)s)[ 2]; + ((uint8_t*)d)[ 3] ^= ((uint8_t*)s)[ 3]; + ((uint8_t*)d)[ 4] ^= ((uint8_t*)s)[ 4]; + ((uint8_t*)d)[ 5] ^= ((uint8_t*)s)[ 5]; + ((uint8_t*)d)[ 6] ^= ((uint8_t*)s)[ 6]; + ((uint8_t*)d)[ 7] ^= ((uint8_t*)s)[ 7]; + ((uint8_t*)d)[ 8] ^= ((uint8_t*)s)[ 8]; + ((uint8_t*)d)[ 9] ^= ((uint8_t*)s)[ 9]; + ((uint8_t*)d)[10] ^= ((uint8_t*)s)[10]; + ((uint8_t*)d)[11] ^= ((uint8_t*)s)[11]; + ((uint8_t*)d)[12] ^= ((uint8_t*)s)[12]; + ((uint8_t*)d)[13] ^= ((uint8_t*)s)[13]; + ((uint8_t*)d)[14] ^= ((uint8_t*)s)[14]; + ((uint8_t*)d)[15] ^= ((uint8_t*)s)[15]; +#endif +} + +static void copy_and_key( void *d, const void *s, const void *k ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0] ^ ((uint32_t*)k)[ 0]; + ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1] ^ ((uint32_t*)k)[ 1]; + ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2] ^ ((uint32_t*)k)[ 2]; + ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3] ^ ((uint32_t*)k)[ 3]; +#elif 1 + ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0] ^ ((uint8_t*)k)[ 0]; + ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1] ^ ((uint8_t*)k)[ 1]; + ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2] ^ ((uint8_t*)k)[ 2]; + ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3] ^ ((uint8_t*)k)[ 3]; + ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4] ^ ((uint8_t*)k)[ 4]; + ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5] ^ ((uint8_t*)k)[ 5]; + ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6] ^ ((uint8_t*)k)[ 6]; + ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7] ^ ((uint8_t*)k)[ 7]; + ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8] ^ ((uint8_t*)k)[ 8]; + ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9] ^ ((uint8_t*)k)[ 9]; + ((uint8_t*)d)[10] = ((uint8_t*)s)[10] ^ ((uint8_t*)k)[10]; + ((uint8_t*)d)[11] = ((uint8_t*)s)[11] ^ ((uint8_t*)k)[11]; + ((uint8_t*)d)[12] = ((uint8_t*)s)[12] ^ ((uint8_t*)k)[12]; + ((uint8_t*)d)[13] = ((uint8_t*)s)[13] ^ ((uint8_t*)k)[13]; + ((uint8_t*)d)[14] = ((uint8_t*)s)[14] ^ ((uint8_t*)k)[14]; + ((uint8_t*)d)[15] = ((uint8_t*)s)[15] ^ ((uint8_t*)k)[15]; +#else + block_copy(d, s); + xor_block(d, k); +#endif +} + +static void add_round_key( uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK] ) +{ + xor_block(d, k); +} + +static void shift_sub_rows( uint8_t st[N_BLOCK] ) +{ uint8_t tt; + + st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]); + st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]); + + tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]); + st[ 9] = s_box(st[13]); st[13] = s_box( tt ); + + tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt ); + tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt ); + + tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]); + st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt ); +} + +#if defined( AES_DEC_PREKEYED ) + +static void inv_shift_sub_rows( uint8_t st[N_BLOCK] ) +{ uint8_t tt; + + st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]); + st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]); + + tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]); + st[ 5] = is_box(st[1]); st[ 1] = is_box( tt ); + + tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt ); + tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt ); + + tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]); + st[11] = is_box(st[15]); st[15] = is_box( tt ); +} + +#endif + +#if defined( VERSION_1 ) + static void mix_sub_columns( uint8_t dt[N_BLOCK] ) + { uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else + static void mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] ) + { +#endif + dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); + } + +#if defined( AES_DEC_PREKEYED ) + +#if defined( VERSION_1 ) + static void inv_mix_sub_columns( uint8_t dt[N_BLOCK] ) + { uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else + static void inv_mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] ) + { +#endif + dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3])); + dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3])); + dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3])); + dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3])); + + dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7])); + dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7])); + dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7])); + dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7])); + + dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); + } + +#endif + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +/* Set the cipher key for the pre-keyed version */ + +return_type aes_set_key( const uint8_t key[], length_type keylen, aes_context ctx[1] ) +{ + uint8_t cc, rc, hi; + + switch( keylen ) + { + case 16: + case 24: + case 32: + break; + default: + ctx->rnd = 0; + return ( uint8_t )-1; + } + block_copy_nn(ctx->ksch, key, keylen); + hi = (keylen + 28) << 2; + ctx->rnd = (hi >> 4) - 1; + for( cc = keylen, rc = 1; cc < hi; cc += 4 ) + { uint8_t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if( cc % keylen == 0 ) + { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = f2(rc); + } + else if( keylen > 24 && cc % keylen == 16 ) + { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = cc - keylen; + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } + return 0; +} + +#endif + +#if defined( AES_ENC_PREKEYED ) + +/* Encrypt a single block of 16 bytes */ + +return_type aes_encrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch ); + + for( r = 1 ; r < ctx->rnd ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + add_round_key( s1, ctx->ksch + r * N_BLOCK); + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK); + } +#endif + shift_sub_rows( s1 ); + copy_and_key( out, s1, ctx->ksch + r * N_BLOCK ); + } + else + return ( uint8_t )-1; + return 0; +} + +/* CBC encrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_encrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] ) +{ + + while(n_block--) + { + xor_block(iv, in); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + //memcpy(out, iv, N_BLOCK); + block_copy(out, iv); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_DEC_PREKEYED ) + +/* Decrypt a single block of 16 bytes */ + +return_type aes_decrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK ); + inv_shift_sub_rows( s1 ); + + for( r = ctx->rnd ; --r ; ) +#if defined( VERSION_1 ) + { + add_round_key( s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, ctx->ksch ); + } + else + return -1; + return 0; +} + +/* CBC decrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_decrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] ) +{ + while(n_block--) + { uint8_t tmp[N_BLOCK]; + + //memcpy(tmp, in, N_BLOCK); + block_copy(tmp, in); + if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + xor_block(out, iv); + //memcpy(iv, tmp, N_BLOCK); + block_copy(iv, tmp); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_ENC_128_OTFK ) + +/* The 'on the fly' encryption key update for for 128 bit keys */ + +static void update_encrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_encrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] ) +{ uint8_t s1[N_BLOCK], r, rc = 1; + + if(o_key != key) + block_copy( o_key, key ); + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 10 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + update_encrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_128_OTFK ) + +/* The 'on the fly' decryption key update for for 128 bit keys */ + +static void update_decrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + for( cc = 12; cc > 0; cc -= 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + *rc = d2(*rc); + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_decrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 0x6c; + if(o_key != key) + block_copy( o_key, key ); + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 10 ; --r ; ) +#if defined( VERSION_1 ) + { + update_decrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + update_decrypt_key_128( o_key, &rc ); + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + update_decrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_ENC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_encrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for( cc = 20; cc < 32; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */ + +void aes_encrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 1; + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 14 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns(s1); + if( r & 1 ) + add_round_key( s1, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key ); + } + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + if( r & 1 ) + copy_and_key( s1, s2, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_256( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_decrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + for(cc = 28; cc > 16; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for(cc = 12; cc > 0; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + *rc = d2(*rc); + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' + 256 bit keying +*/ +void aes_decrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 0x80; + + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 14 ; --r ; ) +#if defined( VERSION_1 ) + { + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key + 16 ); + } + else + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + copy_and_key( s2, s1, o_key + 16 ); + } + else + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, o_key ); +} + +#endif diff --git a/libraries/LoRa_Node/src/system/crypto/aes.h b/libraries/LoRa_Node/src/system/crypto/aes.h new file mode 100644 index 0000000..7203db5 --- /dev/null +++ b/libraries/LoRa_Node/src/system/crypto/aes.h @@ -0,0 +1,160 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + +#ifndef AES_H +#define AES_H + +#if 1 +# define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */ +#endif +#if 0 +# define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */ +#endif +#if 0 +# define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +# define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +# define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */ +#endif +#if 0 +# define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */ +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +typedef uint8_t return_type; + +/* Warning: The key length for 256 bit keys overflows a byte + (see comment below) +*/ + +typedef uint8_t length_type; + +typedef struct +{ uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint8_t rnd; +} aes_context; + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +return_type aes_set_key( const uint8_t key[], + length_type keylen, + aes_context ctx[1] ); +#endif + +#if defined( AES_ENC_PREKEYED ) + +return_type aes_encrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_encrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +#if defined( AES_DEC_PREKEYED ) + +return_type aes_decrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_decrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +/* The following calls are for 'on the fly' keying. In this case the + encryption and decryption keys are different. + + The encryption subroutines take a key in an array of bytes in + key[L] where L is 16, 24 or 32 bytes for key lengths of 128, + 192, and 256 bits respectively. They then encrypts the input + data, in[] with this key and put the reult in the output array + out[]. In addition, the second key array, o_key[L], is used + to output the key that is needed by the decryption subroutine + to reverse the encryption operation. The two key arrays can + be the same array but in this case the original key will be + overwritten. + + In the same way, the decryption subroutines output keys that + can be used to reverse their effect when used for encryption. + + Only 128 and 256 bit keys are supported in these 'on the fly' + modes. +*/ + +#if defined( AES_ENC_128_OTFK ) +void aes_encrypt_128( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK] ); +#endif + +#if defined( AES_DEC_128_OTFK ) +void aes_decrypt_128( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK] ); +#endif + +#if defined( AES_ENC_256_OTFK ) +void aes_encrypt_256( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK] ); +#endif + +#if defined( AES_DEC_256_OTFK ) +void aes_decrypt_256( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK] ); +#endif + +#endif diff --git a/libraries/LoRa_Node/src/system/crypto/cmac.c b/libraries/LoRa_Node/src/system/crypto/cmac.c new file mode 100644 index 0000000..94bb85a --- /dev/null +++ b/libraries/LoRa_Node/src/system/crypto/cmac.c @@ -0,0 +1,153 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with 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: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this 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 +CONTRIBUTORS 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 WITH THE SOFTWARE + +*****************************************************************************/ +//#include +//#include +#include +#include "aes.h" +#include "cmac.h" +#include "boards/mcu/arduino/utilities.h" + +#define LSHIFT(v, r) do { \ + int32_t i; \ + for (i = 0; i < 15; i++) \ + (r)[i] = (v)[i] << 1 | (v)[i + 1] >> 7; \ + (r)[15] = (v)[15] << 1; \ + } while (0) + +#define XOR(v, r) do { \ + int32_t i; \ + for (i = 0; i < 16; i++) \ + { \ + (r)[i] = (r)[i] ^ (v)[i]; \ + } \ + } while (0) \ + + +void AES_CMAC_Init(AES_CMAC_CTX *ctx) +{ + memset1(ctx->X, 0, sizeof ctx->X); + ctx->M_n = 0; + memset1(ctx->rijndael.ksch, '\0', 240); +} + +void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]) +{ + //rijndael_set_key_enc_only(&ctx->rijndael, key, 128); + aes_set_key( key, AES_CMAC_KEY_LENGTH, &ctx->rijndael); +} + +void AES_CMAC_Update(AES_CMAC_CTX *ctx, const uint8_t *data, uint32_t len) +{ + uint32_t mlen; + uint8_t in[16]; + + if (ctx->M_n > 0) { + mlen = MIN(16 - ctx->M_n, len); + memcpy1(ctx->M_last + ctx->M_n, data, mlen); + ctx->M_n += mlen; + if (ctx->M_n < 16 || len == mlen) + return; + XOR(ctx->M_last, ctx->X); + //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X); + aes_encrypt( ctx->X, ctx->X, &ctx->rijndael); + data += mlen; + len -= mlen; + } + while (len > 16) { /* not last block */ + + XOR(data, ctx->X); + //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X); + + memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten + aes_encrypt( in, in, &ctx->rijndael); + memcpy1(&ctx->X[0], in, 16); + + data += 16; + len -= 16; + } + /* potential last block, save it */ + memcpy1(ctx->M_last, data, len); + ctx->M_n = len; +} + +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx) +{ + uint8_t K[16]; + uint8_t in[16]; + /* generate subkey K1 */ + memset1(K, '\0', 16); + + //rijndael_encrypt(&ctx->rijndael, K, K); + + aes_encrypt( K, K, &ctx->rijndael); + + if (K[0] & 0x80) { + LSHIFT(K, K); + K[15] ^= 0x87; + } else + LSHIFT(K, K); + + + if (ctx->M_n == 16) { + /* last block was a complete block */ + XOR(K, ctx->M_last); + + } else { + /* generate subkey K2 */ + if (K[0] & 0x80) { + LSHIFT(K, K); + K[15] ^= 0x87; + } else + LSHIFT(K, K); + + /* padding(M_last) */ + ctx->M_last[ctx->M_n] = 0x80; + while (++ctx->M_n < 16) + ctx->M_last[ctx->M_n] = 0; + + XOR(K, ctx->M_last); + + + } + XOR(ctx->M_last, ctx->X); + + //rijndael_encrypt(&ctx->rijndael, ctx->X, digest); + + memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten + aes_encrypt(in, digest, &ctx->rijndael); + memset1(K, 0, sizeof K); + +} + diff --git a/libraries/LoRa_Node/src/system/crypto/cmac.h b/libraries/LoRa_Node/src/system/crypto/cmac.h new file mode 100644 index 0000000..af52f5a --- /dev/null +++ b/libraries/LoRa_Node/src/system/crypto/cmac.h @@ -0,0 +1,63 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with 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: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this 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 +CONTRIBUTORS 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 WITH THE SOFTWARE + +*****************************************************************************/ + +#ifndef _CMAC_H_ +#define _CMAC_H_ + +#include "aes.h" + +#define AES_CMAC_KEY_LENGTH 16 +#define AES_CMAC_DIGEST_LENGTH 16 + +typedef struct _AES_CMAC_CTX { + aes_context rijndael; + uint8_t X[16]; + uint8_t M_last[16]; + uint32_t M_n; + } AES_CMAC_CTX; + +//#include + +//__BEGIN_DECLS +void AES_CMAC_Init(AES_CMAC_CTX * ctx); +void AES_CMAC_SetKey(AES_CMAC_CTX * ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]); +void AES_CMAC_Update(AES_CMAC_CTX * ctx, const uint8_t * data, uint32_t len); + // __attribute__((__bounded__(__string__,2,3))); +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX * ctx); + // __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH))); +//__END_DECLS + +#endif /* _CMAC_H_ */ + diff --git a/libraries/LoRa_Node/src/system/delay.c b/libraries/LoRa_Node/src/system/delay.c new file mode 100644 index 0000000..874d405 --- /dev/null +++ b/libraries/LoRa_Node/src/system/delay.c @@ -0,0 +1,28 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Delay functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//-------------modified +// #include "board.h" +#include "boards/arduino/board.h" +//--------------------- + +void Delay( float s ) +{ + DelayMs( s * 1000.0f ); +} + +void DelayMs( uint32_t ms ) +{ + HAL_Delay( ms ); +} diff --git a/libraries/LoRa_Node/src/system/delay.h b/libraries/LoRa_Node/src/system/delay.h new file mode 100644 index 0000000..6c4c8a9 --- /dev/null +++ b/libraries/LoRa_Node/src/system/delay.h @@ -0,0 +1,29 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Delay functions implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __DELAY_H__ +#define __DELAY_H__ + +/*! + * Blocking delay of "s" seconds + */ +void Delay( float s ); + +/*! + * Blocking delay of "ms" milliseconds + */ +void DelayMs( uint32_t ms ); + +#endif // __DELAY_H__ + diff --git a/libraries/LoRa_Node/src/system/eeprom.c b/libraries/LoRa_Node/src/system/eeprom.c new file mode 100644 index 0000000..3f67ce9 --- /dev/null +++ b/libraries/LoRa_Node/src/system/eeprom.c @@ -0,0 +1,45 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Timer objects and scheduling management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//-------------------modified +// #include "board.h" +#include "boards/arduino/board.h" + +// #include "i2c-board.h" +#include "boards/arduino/i2c-board.h" + +// #include "eeprom-board.h" +#include "boards/arduino/eeprom-board.h" + +//---------------------------- + +uint8_t EepromWriteBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ) +{ + return EepromMcuWriteBuffer( addr, buffer, size ); +} + +uint8_t EepromReadBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ) +{ + return EepromMcuReadBuffer( addr, buffer, size ); +} + +void EepromSetDeviceAddr( uint8_t addr ) +{ + EepromMcuSetDeviceAddr( addr ); +} + +uint8_t EepromGetDeviceAddr( void ) +{ + return EepromMcuGetDeviceAddr( ); +} diff --git a/libraries/LoRa_Node/src/system/eeprom.h b/libraries/LoRa_Node/src/system/eeprom.h new file mode 100644 index 0000000..a9a6c82 --- /dev/null +++ b/libraries/LoRa_Node/src/system/eeprom.h @@ -0,0 +1,56 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Timer objects and scheduling management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __EEPROM_H__ +#define __EEPROM_H__ + +/*! + * Writes the given buffer to the EEPROM at the specified address. + * + * \param[IN] addr EEPROM address to write to + * \param[IN] buffer Pointer to the buffer to be written. + * \param[IN] size Size of the buffer to be written. + * \retval status [SUCCESS, FAIL] + */ +uint8_t EepromWriteBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * Reads the EEPROM at the specified address to the given buffer. + * + * \param[IN] addr EEPROM address to read from + * \param[OUT] buffer Pointer to the buffer to be written with read data. + * \param[IN] size Size of the buffer to be read. + * \retval status [SUCCESS, FAIL] + */ +uint8_t EepromReadBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * Sets the device address. + * + * \remark Useful for I2C external EEPROMS + * + * \param[IN] addr External EEPROM address + */ +void EepromSetDeviceAddr( uint8_t addr ); + +/*! + * Gets the current device address. + * + * \remark Useful for I2C external EEPROMS + * + * \retval addr External EEPROM address + */ +uint8_t EepromGetDeviceAddr( void ); + +#endif // __EEPROM_H__ diff --git a/libraries/LoRa_Node/src/system/fifo.c b/libraries/LoRa_Node/src/system/fifo.c new file mode 100644 index 0000000..b0c6934 --- /dev/null +++ b/libraries/LoRa_Node/src/system/fifo.c @@ -0,0 +1,58 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements a FIFO buffer + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include "fifo.h" + +static uint16_t FifoNext( Fifo_t *fifo, uint16_t index ) +{ + return ( index + 1 ) % fifo->Size; +} + +void FifoInit( Fifo_t *fifo, uint8_t *buffer, uint16_t size ) +{ + fifo->Begin = 0; + fifo->End = 0; + fifo->Data = buffer; + fifo->Size = size; +} + +void FifoPush( Fifo_t *fifo, uint8_t data ) +{ + fifo->End = FifoNext( fifo, fifo->End ); + fifo->Data[fifo->End] = data; +} + +uint8_t FifoPop( Fifo_t *fifo ) +{ + uint8_t data = fifo->Data[FifoNext( fifo, fifo->Begin )]; + + fifo->Begin = FifoNext( fifo, fifo->Begin ); + return data; +} + +void FifoFlush( Fifo_t *fifo ) +{ + fifo->Begin = 0; + fifo->End = 0; +} + +bool IsFifoEmpty( Fifo_t *fifo ) +{ + return ( fifo->Begin == fifo->End ); +} + +bool IsFifoFull( Fifo_t *fifo ) +{ + return ( FifoNext( fifo, fifo->End ) == fifo->Begin ); +} diff --git a/libraries/LoRa_Node/src/system/fifo.h b/libraries/LoRa_Node/src/system/fifo.h new file mode 100644 index 0000000..cc76dbd --- /dev/null +++ b/libraries/LoRa_Node/src/system/fifo.h @@ -0,0 +1,80 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements a FIFO buffer + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __FIFO_H__ +#define __FIFO_H__ + +#include +#include + +/*! + * FIFO structure + */ +typedef struct Fifo_s +{ + uint16_t Begin; + uint16_t End; + uint8_t *Data; + uint16_t Size; +}Fifo_t; + +/*! + * Initializes the FIFO structure + * + * \param [IN] fifo Pointer to the FIFO object + * \param [IN] buffer Buffer to be used as FIFO + * \param [IN] size Size of the buffer + */ +void FifoInit( Fifo_t *fifo, uint8_t *buffer, uint16_t size ); + +/*! + * Pushes data to the FIFO + * + * \param [IN] fifo Pointer to the FIFO object + * \param [IN] data Data to be pushed into the FIFO + */ +void FifoPush( Fifo_t *fifo, uint8_t data ); + +/*! + * Pops data from the FIFO + * + * \param [IN] fifo Pointer to the FIFO object + * \retval data Data popped from the FIFO + */ +uint8_t FifoPop( Fifo_t *fifo ); + +/*! + * Flushes the FIFO + * + * \param [IN] fifo Pointer to the FIFO object + */ +void FifoFlush( Fifo_t *fifo ); + +/*! + * Checks if the FIFO is empty + * + * \param [IN] fifo Pointer to the FIFO object + * \retval isEmpty true: FIFO is empty, false FIFO is not empty + */ +bool IsFifoEmpty( Fifo_t *fifo ); + +/*! + * Checks if the FIFO is full + * + * \param [IN] fifo Pointer to the FIFO object + * \retval isFull true: FIFO is full, false FIFO is not full + */ +bool IsFifoFull( Fifo_t *fifo ); + +#endif // __FIFO_H__ diff --git a/libraries/LoRa_Node/src/system/gpio.c b/libraries/LoRa_Node/src/system/gpio.c new file mode 100644 index 0000000..c3b3fc5 --- /dev/null +++ b/libraries/LoRa_Node/src/system/gpio.c @@ -0,0 +1,121 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic GPIO driver implementation + +Comment: Relies on the specific board GPIO implementation as well as on + IO expander driver implementation if one is available on the target + board. + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//-------------modified +// #include "board.h" +#include "boards/arduino/board.h" + +// #include "gpio-board.h" +#include "boards/arduino/gpio-board.h" +//------------------------ + +#if defined( BOARD_IOE_EXT ) +#include "gpio-ioe.h" +#endif + +void GpioInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value ) +{ + if( ( uint32_t )( pin >> 4 ) <= 6 ) + { + GpioMcuInit( obj, pin, mode, config, type, value ); + } + else + { +#if defined( BOARD_IOE_EXT ) + // IOExt Pin + GpioIoeInit( obj, pin, mode, config, type, value ); +#endif + } +} + +void GpioSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler ) +{ + if( ( uint32_t )( obj->pin >> 4 ) <= 6 ) + { + GpioMcuSetInterrupt( obj, irqMode, irqPriority, irqHandler ); + } + else + { +#if defined( BOARD_IOE_EXT ) + // IOExt Pin + GpioIoeSetInterrupt( obj, irqMode, irqPriority, irqHandler ); +#endif + } +} + +void GpioRemoveInterrupt( Gpio_t *obj ) +{ + if( ( uint32_t )( obj->pin >> 4 ) <= 6 ) + { + //GpioMcuRemoveInterrupt( obj ); + } + else + { +#if defined( BOARD_IOE_EXT ) + // IOExt Pin + //GpioIoeRemoveInterrupt( obj ); +#endif + } +} + +void GpioWrite( Gpio_t *obj, uint32_t value ) +{ + if( ( uint32_t )( obj->pin >> 4 ) <= 6 ) + { + GpioMcuWrite( obj, value ); + } + else + { +#if defined( BOARD_IOE_EXT ) + // IOExt Pin + GpioIoeWrite( obj, value ); +#endif + } +} + +void GpioToggle( Gpio_t *obj ) +{ + if( ( uint32_t )( obj->pin >> 4 ) <= 6 ) + { + GpioMcuToggle( obj ); + } + else + { +#if defined( BOARD_IOE_EXT ) + // IOExt Pin + GpioIoeWrite( obj, GpioIoeRead( obj ) ^ 1 ); +#endif + } +} + +uint32_t GpioRead( Gpio_t *obj ) +{ + if( ( uint32_t )( obj->pin >> 4 ) <= 6 ) + { + return GpioMcuRead( obj ); + } + else + { +#if defined( BOARD_IOE_EXT ) + // IOExt Pin + return GpioIoeRead( obj ); +#else + return 0; +#endif + } +} diff --git a/libraries/LoRa_Node/src/system/gpio.h b/libraries/LoRa_Node/src/system/gpio.h new file mode 100644 index 0000000..2acfb88 --- /dev/null +++ b/libraries/LoRa_Node/src/system/gpio.h @@ -0,0 +1,172 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic GPIO driver implementation + +Comment: Relies on the specific board GPIO implementation as well as on + IO expander driver implementation if one is available on the target + board. + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __GPIO_H__ +#define __GPIO_H__ + +//------------------modified +// #include "pinName-board.h" +#include "boards/arduino/pinName-board.h" + +// #include "pinName-ioe.h" +//--------------------------- + +/*! + * Board GPIO pin names + */ +typedef enum +{ + MCU_PINS, + IOE_PINS, + + // Not connected + NC = (int)0xFFFFFFFF +}PinNames; + +/*! + * Operation Mode for the GPIO + */ +typedef enum +{ + PIN_INPUT = 0, + PIN_OUTPUT, + PIN_ALTERNATE_FCT, + PIN_ANALOGIC +}PinModes; + +/*! + * Add a pull-up, a pull-down or nothing on the GPIO line + */ +typedef enum +{ + PIN_NO_PULL = 0, + PIN_PULL_UP, + PIN_PULL_DOWN +}PinTypes; + +/*! + * Define the GPIO as Push-pull type or Open Drain + */ +typedef enum +{ + PIN_PUSH_PULL = 0, + PIN_OPEN_DRAIN +}PinConfigs; + +/*! + * Define the GPIO IRQ on a rising, falling or both edges + */ +typedef enum +{ + NO_IRQ = 0, + IRQ_RISING_EDGE, + IRQ_FALLING_EDGE, + IRQ_RISING_FALLING_EDGE +}IrqModes; + +/*! + * Define the IRQ priority on the GPIO + */ +typedef enum +{ + IRQ_VERY_LOW_PRIORITY = 0, + IRQ_LOW_PRIORITY, + IRQ_MEDIUM_PRIORITY, + IRQ_HIGH_PRIORITY, + IRQ_VERY_HIGH_PRIORITY +}IrqPriorities; + +/*! + * Structure for the GPIO + */ +typedef struct +{ + PinNames pin; + uint16_t pinIndex; + void *port; + uint16_t portIndex; + PinTypes pull; +}Gpio_t; + +/*! + * GPIO IRQ handler function prototype + */ +typedef void( GpioIrqHandler )( void ); + +/*! + * GPIO Expander IRQ handler function prototype + */ +typedef void( GpioIoeIrqHandler )( void ); + +/*! + * \brief Initializes the given GPIO object + * + * \param [IN] obj Pointer to the GPIO object + * \param [IN] pin Pin name ( please look in pinName-board.h file ) + * \param [IN] mode Pin mode [PIN_INPUT, PIN_OUTPUT, + * PIN_ALTERNATE_FCT, PIN_ANALOGIC] + * \param [IN] config Pin config [PIN_PUSH_PULL, PIN_OPEN_DRAIN] + * \param [IN] type Pin type [PIN_NO_PULL, PIN_PULL_UP, PIN_PULL_DOWN] + * \param [IN] value Default output value at initialization + */ +void GpioInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value ); + +/*! + * \brief GPIO IRQ Initialization + * + * \param [IN] obj Pointer to the GPIO object + * \param [IN] irqMode IRQ mode [NO_IRQ, IRQ_RISING_EDGE, + * IRQ_FALLING_EDGE, IRQ_RISING_FALLING_EDGE] + * \param [IN] irqPriority IRQ priority [IRQ_VERY_LOW_PRIORITY, IRQ_LOW_PRIORITY + * IRQ_MEDIUM_PRIORITY, IRQ_HIGH_PRIORITY + * IRQ_VERY_HIGH_PRIORITY] + * \param [IN] irqHandler Callback function pointer + */ +void GpioSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler ); + +/*! + * \brief Removes the interrupt from the object + * + * \param [IN] obj Pointer to the GPIO object + */ +void GpioRemoveInterrupt( Gpio_t *obj ); + +/*! + * \brief Writes the given value to the GPIO output + * + * \param [IN] obj Pointer to the GPIO object + * \param [IN] value New GPIO output value + */ +void GpioWrite( Gpio_t *obj, uint32_t value ); + +/*! + * \brief Toggle the value to the GPIO output + * + * \param [IN] obj Pointer to the GPIO object + */ +void GpioToggle( Gpio_t *obj ); + +/*! + * \brief Reads the current GPIO input value + * + * \param [IN] obj Pointer to the GPIO object + * \retval value Current GPIO input value + */ +uint32_t GpioRead( Gpio_t *obj ); + +#endif // __GPIO_H__ diff --git a/libraries/LoRa_Node/src/system/gps.c b/libraries/LoRa_Node/src/system/gps.c new file mode 100644 index 0000000..9cef32f --- /dev/null +++ b/libraries/LoRa_Node/src/system/gps.c @@ -0,0 +1,651 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic driver for any GPS receiver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#include +#include +#include +#include +#include +//------------modified +// #include "board.h" +#include "boards/arduino/board.h" +//-------------------- +#include "gps.h" + +#define TRIGGER_GPS_CNT 10 + +/* Various type of NMEA data we can receive with the Gps */ +const char NmeaDataTypeGPGGA[] = "GPGGA"; +const char NmeaDataTypeGPGSA[] = "GPGSA"; +const char NmeaDataTypeGPGSV[] = "GPGSV"; +const char NmeaDataTypeGPRMC[] = "GPRMC"; + +/* Value used for the conversion of the position from DMS to decimal */ +const int32_t MaxNorthPosition = 8388607; // 2^23 - 1 +const int32_t MaxSouthPosition = 8388608; // -2^23 +const int32_t MaxEastPosition = 8388607; // 2^23 - 1 +const int32_t MaxWestPosition = 8388608; // -2^23 + +tNmeaGpsData NmeaGpsData; + +static double HasFix = false; +static double Latitude = 0; +static double Longitude = 0; + +static int32_t LatitudeBinary = 0; +static int32_t LongitudeBinary = 0; + +static int16_t Altitude = 0xFFFF; + +static uint32_t PpsCnt = 0; + +bool PpsDetected = false; + +void GpsPpsHandler( bool *parseData ) +{ + PpsDetected = true; + PpsCnt++; + *parseData = false; + + if( PpsCnt >= TRIGGER_GPS_CNT ) + { + PpsCnt = 0; + BlockLowPowerDuringTask ( true ); + *parseData = true; + } +} + +void GpsInit( void ) +{ + PpsDetected = false; + GpsMcuInit( ); +} + +void GpsStart( void ) +{ + GpsMcuStart( ); +} + +void GpsStop( void ) +{ + GpsMcuStop( ); +} + +void GpsProcess( void ) +{ + GpsMcuProcess( ); +} + +bool GpsGetPpsDetectedState( void ) +{ + bool state = false; + + BoardDisableIrq( ); + state = PpsDetected; + PpsDetected = false; + BoardEnableIrq( ); + return state; +} + +bool GpsHasFix( void ) +{ + return HasFix; +} + +void GpsConvertPositionIntoBinary( void ) +{ + long double temp; + + if( Latitude >= 0 ) // North + { + temp = Latitude * MaxNorthPosition; + LatitudeBinary = temp / 90; + } + else // South + { + temp = Latitude * MaxSouthPosition; + LatitudeBinary = temp / 90; + } + + if( Longitude >= 0 ) // East + { + temp = Longitude * MaxEastPosition; + LongitudeBinary = temp / 180; + } + else // West + { + temp = Longitude * MaxWestPosition; + LongitudeBinary = temp / 180; + } +} + +void GpsConvertPositionFromStringToNumerical( void ) +{ + int i; + + double valueTmp1; + double valueTmp2; + double valueTmp3; + double valueTmp4; + + // Convert the latitude from ASCII to uint8_t values + for( i = 0 ; i < 10 ; i++ ) + { + NmeaGpsData.NmeaLatitude[i] = NmeaGpsData.NmeaLatitude[i] & 0xF; + } + // Convert latitude from degree/minute/second (DMS) format into decimal + valueTmp1 = ( double )NmeaGpsData.NmeaLatitude[0] * 10.0 + ( double )NmeaGpsData.NmeaLatitude[1]; + valueTmp2 = ( double )NmeaGpsData.NmeaLatitude[2] * 10.0 + ( double )NmeaGpsData.NmeaLatitude[3]; + valueTmp3 = ( double )NmeaGpsData.NmeaLatitude[5] * 1000.0 + ( double )NmeaGpsData.NmeaLatitude[6] * 100.0 + + ( double )NmeaGpsData.NmeaLatitude[7] * 10.0 + ( double )NmeaGpsData.NmeaLatitude[8]; + + Latitude = valueTmp1 + ( ( valueTmp2 + ( valueTmp3 * 0.0001 ) ) / 60.0 ); + + if( NmeaGpsData.NmeaLatitudePole[0] == 'S' ) + { + Latitude *= -1; + } + + // Convert the longitude from ASCII to uint8_t values + for( i = 0 ; i < 10 ; i++ ) + { + NmeaGpsData.NmeaLongitude[i] = NmeaGpsData.NmeaLongitude[i] & 0xF; + } + // Convert longitude from degree/minute/second (DMS) format into decimal + valueTmp1 = ( double )NmeaGpsData.NmeaLongitude[0] * 100.0 + ( double )NmeaGpsData.NmeaLongitude[1] * 10.0 + ( double )NmeaGpsData.NmeaLongitude[2]; + valueTmp2 = ( double )NmeaGpsData.NmeaLongitude[3] * 10.0 + ( double )NmeaGpsData.NmeaLongitude[4]; + valueTmp3 = ( double )NmeaGpsData.NmeaLongitude[6] * 1000.0 + ( double )NmeaGpsData.NmeaLongitude[7] * 100; + valueTmp4 = ( double )NmeaGpsData.NmeaLongitude[8] * 10.0 + ( double )NmeaGpsData.NmeaLongitude[9]; + + Longitude = valueTmp1 + ( valueTmp2 / 60.0 ) + ( ( ( valueTmp3 + valueTmp4 ) * 0.0001 ) / 60.0 ); + + if( NmeaGpsData.NmeaLongitudePole[0] == 'W' ) + { + Longitude *= -1; + } +} + + +uint8_t GpsGetLatestGpsPositionDouble( double *lati, double *longi ) +{ + uint8_t status = FAIL; + if( HasFix == true ) + { + status = SUCCESS; + } + else + { + GpsResetPosition( ); + } + *lati = Latitude; + *longi = Longitude; + return status; +} + +uint8_t GpsGetLatestGpsPositionBinary( int32_t *latiBin, int32_t *longiBin ) +{ + uint8_t status = FAIL; + + BoardDisableIrq( ); + if( HasFix == true ) + { + status = SUCCESS; + } + else + { + GpsResetPosition( ); + } + *latiBin = LatitudeBinary; + *longiBin = LongitudeBinary; + BoardEnableIrq( ); + return status; +} + +int16_t GpsGetLatestGpsAltitude( void ) +{ + BoardDisableIrq( ); + if( HasFix == true ) + { + Altitude = atoi( NmeaGpsData.NmeaAltitude ); + } + else + { + Altitude = 0xFFFF; + } + BoardEnableIrq( ); + + return Altitude; +} + +/*! + * Calculates the checksum for a NMEA sentence + * + * Skip the first '$' if necessary and calculate checksum until '*' character is + * reached (or buffSize exceeded). + * + * \retval chkPosIdx Position of the checksum in the sentence + */ +int32_t GpsNmeaChecksum( int8_t *nmeaStr, int32_t nmeaStrSize, int8_t * checksum ) +{ + int i = 0; + uint8_t checkNum = 0; + + // Check input parameters + if( ( nmeaStr == NULL ) || ( checksum == NULL ) || ( nmeaStrSize <= 1 ) ) + { + return -1; + } + + // Skip the first '$' if necessary + if( nmeaStr[i] == '$' ) + { + i += 1; + } + + // XOR until '*' or max length is reached + while( nmeaStr[i] != '*' ) + { + checkNum ^= nmeaStr[i]; + i += 1; + if( i >= nmeaStrSize ) + { + return -1; + } + } + + // Convert checksum value to 2 hexadecimal characters + checksum[0] = Nibble2HexChar( checkNum / 16 ); // upper nibble + checksum[1] = Nibble2HexChar( checkNum % 16 ); // lower nibble + + return i + 1; +} + +/*! + * Calculate the checksum of a NMEA frame and compare it to the checksum that is + * present at the end of it. + * Return true if it matches + */ +static bool GpsNmeaValidateChecksum( int8_t *serialBuff, int32_t buffSize ) +{ + int32_t checksumIndex; + int8_t checksum[2]; // 2 characters to calculate NMEA checksum + + checksumIndex = GpsNmeaChecksum( serialBuff, buffSize, checksum ); + + // could we calculate a verification checksum ? + if( checksumIndex < 0 ) + { + return false; + } + + // check if there are enough char in the serial buffer to read checksum + if( checksumIndex >= ( buffSize - 2 ) ) + { + return false; + } + + // check the checksum + if( ( serialBuff[checksumIndex] == checksum[0] ) && ( serialBuff[checksumIndex + 1] == checksum[1] ) ) + { + return true; + } + else + { + return false; + } +} + +uint8_t GpsParseGpsData( int8_t *rxBuffer, int32_t rxBufferSize ) +{ + uint8_t i = 1; + uint8_t j = 0; + uint8_t fieldSize = 0; + + if( rxBuffer[0] != '$' ) + { + GpsMcuInvertPpsTrigger( ); + return FAIL; + } + + if( GpsNmeaValidateChecksum( rxBuffer, rxBufferSize ) == false ) + { + return FAIL; + } + + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 6 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaDataType[j] = rxBuffer[i]; + } + // Parse the GPGGA data + if( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPGGA, 5 ) == 0 ) + { + // NmeaUtcTime + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 11 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaUtcTime[j] = rxBuffer[i]; + } + // NmeaLatitude + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 10 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLatitude[j] = rxBuffer[i]; + } + // NmeaLatitudePole + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLatitudePole[j] = rxBuffer[i]; + } + // NmeaLongitude + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 11 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLongitude[j] = rxBuffer[i]; + } + // NmeaLongitudePole + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLongitudePole[j] = rxBuffer[i]; + } + // NmeaFixQuality + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaFixQuality[j] = rxBuffer[i]; + } + // NmeaSatelliteTracked + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 3 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaSatelliteTracked[j] = rxBuffer[i]; + } + // NmeaHorizontalDilution + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 6 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaHorizontalDilution[j] = rxBuffer[i]; + } + // NmeaAltitude + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 8 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaAltitude[j] = rxBuffer[i]; + } + // NmeaAltitudeUnit + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaAltitudeUnit[j] = rxBuffer[i]; + } + // NmeaHeightGeoid + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 8 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaHeightGeoid[j] = rxBuffer[i]; + } + // NmeaHeightGeoidUnit + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaHeightGeoidUnit[j] = rxBuffer[i]; + } + + GpsFormatGpsData( ); + return SUCCESS; + } + else if ( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPRMC, 5 ) == 0 ) + { + // NmeaUtcTime + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 11 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaUtcTime[j] = rxBuffer[i]; + } + // NmeaDataStatus + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaDataStatus[j] = rxBuffer[i]; + } + // NmeaLatitude + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 10 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLatitude[j] = rxBuffer[i]; + } + // NmeaLatitudePole + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLatitudePole[j] = rxBuffer[i]; + } + // NmeaLongitude + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 11 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLongitude[j] = rxBuffer[i]; + } + // NmeaLongitudePole + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 2 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaLongitudePole[j] = rxBuffer[i]; + } + // NmeaSpeed + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 8 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaSpeed[j] = rxBuffer[i]; + } + // NmeaDetectionAngle + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 8 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaDetectionAngle[j] = rxBuffer[i]; + } + // NmeaDate + fieldSize = 0; + while( rxBuffer[i + fieldSize++] != ',' ) + { + if( fieldSize > 8 ) + { + return FAIL; + } + } + for( j = 0; j < fieldSize; j++, i++ ) + { + NmeaGpsData.NmeaDate[j] = rxBuffer[i]; + } + + GpsFormatGpsData( ); + return SUCCESS; + } + else + { + return FAIL; + } +} + +void GpsFormatGpsData( void ) +{ + if( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPGGA, 5 ) == 0 ) + { + HasFix = ( NmeaGpsData.NmeaFixQuality[0] > 0x30 ) ? true : false; + } + else if ( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPRMC, 5 ) == 0 ) + { + HasFix = ( NmeaGpsData.NmeaDataStatus[0] == 0x41 ) ? true : false; + } + GpsConvertPositionFromStringToNumerical( ); + GpsConvertPositionIntoBinary( ); +} + +void GpsResetPosition( void ) +{ + Altitude = 0xFFFF; + Latitude = 0; + Longitude = 0; + LatitudeBinary = 0; + LongitudeBinary = 0; +} diff --git a/libraries/LoRa_Node/src/system/gps.h b/libraries/LoRa_Node/src/system/gps.h new file mode 100644 index 0000000..da77084 --- /dev/null +++ b/libraries/LoRa_Node/src/system/gps.h @@ -0,0 +1,144 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Generic driver for the GPS receiver UP501 + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __GPS_H__ +#define __GPS_H__ + +/* Structure to handle the GPS parsed data in ASCII */ +typedef struct +{ + char NmeaDataType[6]; + char NmeaUtcTime[11]; + char NmeaDataStatus[2]; + char NmeaLatitude[10]; + char NmeaLatitudePole[2]; + char NmeaLongitude[11]; + char NmeaLongitudePole[2]; + char NmeaFixQuality[2]; + char NmeaSatelliteTracked[3]; + char NmeaHorizontalDilution[6]; + char NmeaAltitude[8]; + char NmeaAltitudeUnit[2]; + char NmeaHeightGeoid[8]; + char NmeaHeightGeoidUnit[2]; + char NmeaSpeed[8]; + char NmeaDetectionAngle[8]; + char NmeaDate[8]; +}tNmeaGpsData; + +extern tNmeaGpsData NmeaGpsData; + +/*! + * \brief Initializes the handling of the GPS receiver + */ +void GpsInit( void ); + +/*! + * \brief Switch ON the GPS + */ +void GpsStart( void ); + +/*! + * \brief Switch OFF the GPS + */ +void GpsStop( void ); + +/*! + * Updates the GPS status + */ +void GpsProcess( void ); + +/*! + * \brief PPS signal handling function + */ +void GpsPpsHandler( bool *parseData ); + +/*! + * \brief PPS signal handling function + * + * \retval ppsDetected State of PPS signal. + */ +bool GpsGetPpsDetectedState( void ); + +/*! + * \brief Indicates if GPS has fix + * + * \retval hasFix + */ +bool GpsHasFix( void ); + +/*! + * \brief Converts the latest Position (latitude and longitude) into a binary + * number + */ +void GpsConvertPositionIntoBinary( void ); + +/*! + * \brief Converts the latest Position (latitude and Longitude) from ASCII into + * DMS numerical format + */ +void GpsConvertPositionFromStringToNumerical( void ); + +/*! + * \brief Gets the latest Position (latitude and Longitude) as two double values + * if available + * + * \param [OUT] lati Latitude value + * \param [OUT] longi Longitude value + * + * \retval status [SUCCESS, FAIL] + */ +uint8_t GpsGetLatestGpsPositionDouble ( double *lati, double *longi ); + +/*! + * \brief Gets the latest Position (latitude and Longitude) as two binary values + * if available + * + * \param [OUT] latiBin Latitude value + * \param [OUT] longiBin Longitude value + * + * \retval status [SUCCESS, FAIL] + */ +uint8_t GpsGetLatestGpsPositionBinary ( int32_t *latiBin, int32_t *longiBin ); + +/*! + * \brief Parses the NMEA sentence. + * + * \remark Only parses GPGGA and GPRMC sentences + * + * \param [IN] rxBuffer Data buffer to be parsed + * \param [IN] rxBufferSize Size of data buffer + * + * \retval status [SUCCESS, FAIL] + */ +uint8_t GpsParseGpsData( int8_t *rxBuffer, int32_t rxBufferSize ); + +/*! + * \brief Returns the latest altitude from the parsed NMEA sentence + * + * \retval altitude + */ +int16_t GpsGetLatestGpsAltitude( void ); + +/*! + * \brief Format GPS data into numeric and binary formats + */ +void GpsFormatGpsData( void ); + +/*! + * \brief Resets the GPS position variables + */ +void GpsResetPosition( void ); + +#endif // __GPS_H__ diff --git a/libraries/LoRa_Node/src/system/i2c.c b/libraries/LoRa_Node/src/system/i2c.c new file mode 100644 index 0000000..7fb0182 --- /dev/null +++ b/libraries/LoRa_Node/src/system/i2c.c @@ -0,0 +1,126 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements the generic I2C driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//---------------------modified +// #include "board.h" +// #include "i2c-board.h" +#include "boards/arduino/board.h" +#include "boards/arduino/i2c-board.h" +//------------------------------ + +/*! + * Flag to indicates if the I2C is initialized + */ +static bool I2cInitialized = false; + +void I2cInit( I2c_t *obj, PinNames scl, PinNames sda ) +{ + if( I2cInitialized == false ) + { + I2cInitialized = true; + + I2cMcuInit( obj, scl, sda ); + I2cMcuFormat( obj, MODE_I2C, I2C_DUTY_CYCLE_2, true, I2C_ACK_ADD_7_BIT, 400000 ); + } +} + +void I2cDeInit( I2c_t *obj ) +{ + I2cInitialized = false; + I2cMcuDeInit( obj ); +} + +void I2cResetBus( I2c_t *obj ) +{ + I2cInitialized = false; + I2cInit( obj, /*I2C_SCL*/PIN_WIRE_SCL, /*I2C_SDA*/PIN_WIRE_SDA ); +} + +uint8_t I2cWrite( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t data ) +{ + if( I2cInitialized == true ) + { + if( I2cMcuWriteBuffer( obj, deviceAddr, addr, &data, 1 ) == FAIL ) + { + // if first attempt fails due to an IRQ, try a second time + if( I2cMcuWriteBuffer( obj, deviceAddr, addr, &data, 1 ) == FAIL ) + { + return FAIL; + } + else + { + return SUCCESS; + } + } + else + { + return SUCCESS; + } + } + else + { + return FAIL; + } +} + +uint8_t I2cWriteBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ) +{ + if( I2cInitialized == true ) + { + if( I2cMcuWriteBuffer( obj, deviceAddr, addr, buffer, size ) == FAIL ) + { + // if first attempt fails due to an IRQ, try a second time + if( I2cMcuWriteBuffer( obj, deviceAddr, addr, buffer, size ) == FAIL ) + { + return FAIL; + } + else + { + return SUCCESS; + } + } + else + { + return SUCCESS; + } + } + else + { + return FAIL; + } +} + +uint8_t I2cRead( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *data ) +{ + if( I2cInitialized == true ) + { + return( I2cMcuReadBuffer( obj, deviceAddr, addr, data, 1 ) ); + } + else + { + return FAIL; + } +} + +uint8_t I2cReadBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ) +{ + if( I2cInitialized == true ) + { + return( I2cMcuReadBuffer( obj, deviceAddr, addr, buffer, size ) ); + } + else + { + return FAIL; + } +} diff --git a/libraries/LoRa_Node/src/system/i2c.h b/libraries/LoRa_Node/src/system/i2c.h new file mode 100644 index 0000000..ed22403 --- /dev/null +++ b/libraries/LoRa_Node/src/system/i2c.h @@ -0,0 +1,93 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements the generic I2C driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __I2C_H__ +#define __I2C_H__ + +/*! + * I2C object type definition + */ +typedef struct +{ +// I2C_HandleTypeDef I2c; + Gpio_t Scl; + Gpio_t Sda; +}I2c_t; + +/*! + * \brief Initializes the I2C object and MCU peripheral + * + * \param [IN] obj I2C object + * \param [IN] scl I2C Scl pin name to be used + * \param [IN] sda I2C Sda pin name to be used + */ +void I2cInit( I2c_t *obj, PinNames scl, PinNames sda ); + +/*! + * \brief DeInitializes the I2C object and MCU peripheral + * + * \param [IN] obj I2C object + */ +void I2cDeInit( I2c_t *obj ); + +/*! + * \brief Reset the I2C object and MCU peripheral + * + * \param [IN] obj I2C object + */ +void I2cResetBus( I2c_t *obj ); + +/*! + * \brief Write data to the I2C device + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + * \param [IN] addr data address + * \param [IN] data data to write + */ +uint8_t I2cWrite( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t data ); + +/*! + * \brief Write several data to the I2C device + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + * \param [IN] addr data address + * \param [IN] buffer data buffer to write + * \param [IN] size number of bytes to write + */ +uint8_t I2cWriteBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ); + +/*! + * \brief Read data from the I2C device + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + * \param [IN] addr data address + * \param [OUT] data variable used to store the data read + */ +uint8_t I2cRead( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *data ); + +/*! + * \brief Read several data byte from the I2C device + * + * \param [IN] obj I2C object + * \param [IN] deviceAddr device address + * \param [IN] addr data address + * \param [OUT] buffer data buffer used to store the data read + * \param [IN] size number of data byte to read + */ +uint8_t I2cReadBuffer( I2c_t *obj, uint8_t deviceAddr, uint16_t addr, uint8_t *buffer, uint16_t size ); + +#endif // __I2C_H__ diff --git a/libraries/LoRa_Node/src/system/serial.h b/libraries/LoRa_Node/src/system/serial.h new file mode 100644 index 0000000..2a1f534 --- /dev/null +++ b/libraries/LoRa_Node/src/system/serial.h @@ -0,0 +1,18 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements the generic UART driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SERIAL_H__ +#define __SERIAL_H__ + +#endif // __SERIAL_H__ diff --git a/libraries/LoRa_Node/src/system/spi.h b/libraries/LoRa_Node/src/system/spi.h new file mode 100644 index 0000000..98c48bf --- /dev/null +++ b/libraries/LoRa_Node/src/system/spi.h @@ -0,0 +1,76 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements the generic SPI driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SPI_H__ +#define __SPI_H__ + +#include "boards/arduino/spi-board.h" + +/*! + * SPI object type definition + */ +typedef struct Spi_s Spi_t; + +/*! + * \brief Initializes the SPI object and MCU peripheral + * + * \remark When NSS pin is software controlled set the pin name to NC otherwise + * set the pin name to be used. + * + * \param [IN] obj SPI object + * \param [IN] mosi SPI MOSI pin name to be used + * \param [IN] miso SPI MISO pin name to be used + * \param [IN] sclk SPI SCLK pin name to be used + * \param [IN] nss SPI NSS pin name to be used + */ +void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss ); + +/*! + * \brief De-initializes the SPI object and MCU peripheral + * + * \param [IN] obj SPI object + */ +void SpiDeInit( Spi_t *obj ); + +/*! + * \brief Configures the SPI peripheral + * + * \remark Slave mode isn't currently handled + * + * \param [IN] obj SPI object + * \param [IN] bits Number of bits to be used. [8 or 16] + * \param [IN] cpol Clock polarity + * \param [IN] cpha Clock phase + * \param [IN] slave When set the peripheral acts in slave mode + */ +void SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave ); + +/*! + * \brief Sets the SPI speed + * + * \param [IN] obj SPI object + * \param [IN] hz SPI clock frequency in hz + */ +void SpiFrequency( Spi_t *obj, uint32_t hz ); + +/*! + * \brief Sends outData and receives inData + * + * \param [IN] obj SPI object + * \param [IN] outData Byte to be sent + * \retval inData Received byte. + */ +uint16_t SpiInOut( Spi_t *obj, uint16_t outData ); + +#endif // __SPI_H__ diff --git a/libraries/LoRa_Node/src/system/timer.c b/libraries/LoRa_Node/src/system/timer.c new file mode 100644 index 0000000..5e27ade --- /dev/null +++ b/libraries/LoRa_Node/src/system/timer.c @@ -0,0 +1,409 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Timer objects and scheduling management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//---------------modified +// #include "board.h" +// #include "rtc-board.h" +#include "boards/arduino/board.h" + +#include "timer.h" +//----------------------- + + +/*! + * This flag is used to make sure we have looped through the main several time to avoid race issues + */ +volatile uint8_t HasLoopedThroughMain = 0; + +/*! + * Timers list head pointer + */ +static TimerEvent_t *TimerListHead = NULL; + +/*! + * \brief Adds or replace the head timer of the list. + * + * \remark The list is automatically sorted. The list head always contains the + * next timer to expire. + * + * \param [IN] obj Timer object to be become the new head + * \param [IN] remainingTime Remaining time of the previous head to be replaced + */ +static void TimerInsertNewHeadTimer( TimerEvent_t *obj, uint32_t remainingTime ); + +/*! + * \brief Adds a timer to the list. + * + * \remark The list is automatically sorted. The list head always contains the + * next timer to expire. + * + * \param [IN] obj Timer object to be added to the list + * \param [IN] remainingTime Remaining time of the running head after which the object may be added + */ +static void TimerInsertTimer( TimerEvent_t *obj, uint32_t remainingTime ); + +/*! + * \brief Sets a timeout with the duration "timestamp" + * + * \param [IN] timestamp Delay duration + */ +static void TimerSetTimeout( TimerEvent_t *obj ); + +/*! + * \brief Check if the Object to be added is not already in the list + * + * \param [IN] timestamp Delay duration + * \retval true (the object is already in the list) or false + */ +static bool TimerExists( TimerEvent_t *obj ); + +/*! + * \brief Read the timer value of the currently running timer + * + * \retval value current timer value + */ +TimerTime_t TimerGetValue( void ); + +void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) ) +{ + obj->Timestamp = 0; + obj->ReloadValue = 0; + obj->IsRunning = false; + obj->Callback = callback; + obj->Next = NULL; +} + +void TimerStart( TimerEvent_t *obj ) +{ + uint32_t elapsedTime = 0; + uint32_t remainingTime = 0; + + BoardDisableIrq( ); + + if( ( obj == NULL ) || ( TimerExists( obj ) == true ) ) + { + BoardEnableIrq( ); + return; + } + + obj->Timestamp = obj->ReloadValue; + obj->IsRunning = false; + + if( TimerListHead == NULL ) + { + TimerInsertNewHeadTimer( obj, obj->Timestamp ); + } + else + { + if( TimerListHead->IsRunning == true ) + { + elapsedTime = TimerGetValue( ); + if( elapsedTime > TimerListHead->Timestamp ) + { + elapsedTime = TimerListHead->Timestamp; // security but should never occur + } + remainingTime = TimerListHead->Timestamp - elapsedTime; + } + else + { + remainingTime = TimerListHead->Timestamp; + } + + if( obj->Timestamp < remainingTime ) + { + TimerInsertNewHeadTimer( obj, remainingTime ); + } + else + { + TimerInsertTimer( obj, remainingTime ); + } + } + BoardEnableIrq( ); +} + +static void TimerInsertTimer( TimerEvent_t *obj, uint32_t remainingTime ) +{ + uint32_t aggregatedTimestamp = 0; // hold the sum of timestamps + uint32_t aggregatedTimestampNext = 0; // hold the sum of timestamps up to the next event + + TimerEvent_t* prev = TimerListHead; + TimerEvent_t* cur = TimerListHead->Next; + + if( cur == NULL ) + { // obj comes just after the head + obj->Timestamp -= remainingTime; + prev->Next = obj; + obj->Next = NULL; + } + else + { + aggregatedTimestamp = remainingTime; + aggregatedTimestampNext = remainingTime + cur->Timestamp; + + while( prev != NULL ) + { + if( aggregatedTimestampNext > obj->Timestamp ) + { + obj->Timestamp -= aggregatedTimestamp; + if( cur != NULL ) + { + cur->Timestamp -= obj->Timestamp; + } + prev->Next = obj; + obj->Next = cur; + break; + } + else + { + prev = cur; + cur = cur->Next; + if( cur == NULL ) + { // obj comes at the end of the list + aggregatedTimestamp = aggregatedTimestampNext; + obj->Timestamp -= aggregatedTimestamp; + prev->Next = obj; + obj->Next = NULL; + break; + } + else + { + aggregatedTimestamp = aggregatedTimestampNext; + aggregatedTimestampNext = aggregatedTimestampNext + cur->Timestamp; + } + } + } + } +} + +static void TimerInsertNewHeadTimer( TimerEvent_t *obj, uint32_t remainingTime ) +{ + TimerEvent_t* cur = TimerListHead; + + if( cur != NULL ) + { + cur->Timestamp = remainingTime - obj->Timestamp; + cur->IsRunning = false; + } + + obj->Next = cur; + obj->IsRunning = true; + TimerListHead = obj; + TimerSetTimeout( TimerListHead ); +} + +void TimerIrqHandler( void ) +{ + uint32_t elapsedTime = 0; + + // Early out when TimerListHead is null to prevent null pointer + if ( TimerListHead == NULL ) + { + return; + } + + elapsedTime = TimerGetValue( ); + + if( elapsedTime >= TimerListHead->Timestamp ) + { + TimerListHead->Timestamp = 0; + } + else + { + TimerListHead->Timestamp -= elapsedTime; + } + + TimerListHead->IsRunning = false; + + while( ( TimerListHead != NULL ) && ( TimerListHead->Timestamp == 0 ) ) + { + TimerEvent_t* elapsedTimer = TimerListHead; + TimerListHead = TimerListHead->Next; + + if( elapsedTimer->Callback != NULL ) + { + elapsedTimer->Callback( ); + } + } + + // start the next TimerListHead if it exists + if( TimerListHead != NULL ) + { + if( TimerListHead->IsRunning != true ) + { + TimerListHead->IsRunning = true; + TimerSetTimeout( TimerListHead ); + } + } +} + +void TimerStop( TimerEvent_t *obj ) +{ + BoardDisableIrq( ); + + uint32_t elapsedTime = 0; + uint32_t remainingTime = 0; + + TimerEvent_t* prev = TimerListHead; + TimerEvent_t* cur = TimerListHead; + + // List is empty or the Obj to stop does not exist + if( ( TimerListHead == NULL ) || ( obj == NULL ) ) + { + BoardEnableIrq( ); + return; + } + + if( TimerListHead == obj ) // Stop the Head + { + if( TimerListHead->IsRunning == true ) // The head is already running + { + elapsedTime = TimerGetValue( ); + if( elapsedTime > obj->Timestamp ) + { + elapsedTime = obj->Timestamp; + } + + remainingTime = obj->Timestamp - elapsedTime; + + if( TimerListHead->Next != NULL ) + { + TimerListHead->IsRunning = false; + TimerListHead = TimerListHead->Next; + TimerListHead->Timestamp += remainingTime; + TimerListHead->IsRunning = true; + TimerSetTimeout( TimerListHead ); + } + else + { + TimerListHead = NULL; + } + } + else // Stop the head before it is started + { + if( TimerListHead->Next != NULL ) + { + remainingTime = obj->Timestamp; + TimerListHead = TimerListHead->Next; + TimerListHead->Timestamp += remainingTime; + } + else + { + TimerListHead = NULL; + } + } + } + else // Stop an object within the list + { + remainingTime = obj->Timestamp; + + while( cur != NULL ) + { + if( cur == obj ) + { + if( cur->Next != NULL ) + { + cur = cur->Next; + prev->Next = cur; + cur->Timestamp += remainingTime; + } + else + { + cur = NULL; + prev->Next = cur; + } + break; + } + else + { + prev = cur; + cur = cur->Next; + } + } + } + BoardEnableIrq( ); +} + +static bool TimerExists( TimerEvent_t *obj ) +{ + TimerEvent_t* cur = TimerListHead; + + while( cur != NULL ) + { + if( cur == obj ) + { + return true; + } + cur = cur->Next; + } + return false; +} + +void TimerReset( TimerEvent_t *obj ) +{ + TimerStop( obj ); + TimerStart( obj ); +} + +void TimerSetValue( TimerEvent_t *obj, uint32_t value ) +{ + TimerStop( obj ); + obj->Timestamp = value; + obj->ReloadValue = value; +} + +TimerTime_t TimerGetValue( void ) +{ + return RtcGetElapsedAlarmTime( ); +} + +TimerTime_t TimerGetCurrentTime( void ) +{ + return RtcGetTimerValue( ); +} + +TimerTime_t TimerGetElapsedTime( TimerTime_t savedTime ) +{ + return RtcComputeElapsedTime( savedTime ); +} + +TimerTime_t TimerGetFutureTime( TimerTime_t eventInFuture ) +{ + return RtcComputeFutureEventTime( eventInFuture ); +} + +static void TimerSetTimeout( TimerEvent_t *obj ) +{ + HasLoopedThroughMain = 0; + obj->Timestamp = RtcGetAdjustedTimeoutValue( obj->Timestamp ); + RtcSetTimeout( obj->Timestamp ); +} + +void TimerLowPowerHandler( void ) +{ + if( ( TimerListHead != NULL ) && ( TimerListHead->IsRunning == true ) ) + { + if( HasLoopedThroughMain < 5 ) + { + HasLoopedThroughMain++; + } + else + { + HasLoopedThroughMain = 0; + //if( GetBoardPowerSource( ) == BATTERY_POWER ) + //{ + // RtcEnterLowPowerStopMode( ); + //} + } + } +} diff --git a/libraries/LoRa_Node/src/system/timer.h b/libraries/LoRa_Node/src/system/timer.h new file mode 100644 index 0000000..a1a966e --- /dev/null +++ b/libraries/LoRa_Node/src/system/timer.h @@ -0,0 +1,110 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Timer objects and scheduling management + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +/*! + * \brief Timer object description + */ +typedef struct TimerEvent_s +{ + uint32_t Timestamp; //! Current timer value + uint32_t ReloadValue; //! Timer delay value + bool IsRunning; //! Is the timer currently running + void ( *Callback )( void ); //! Timer IRQ callback function + struct TimerEvent_s *Next; //! Pointer to the next Timer object. +}TimerEvent_t; + +/*! + * \brief Timer time variable definition + */ +#ifndef TimerTime_t +typedef uint32_t TimerTime_t; +#endif + +/*! + * \brief Initializes the timer object + * + * \remark TimerSetValue function must be called before starting the timer. + * this function initializes timestamp and reload value at 0. + * + * \param [IN] obj Structure containing the timer object parameters + * \param [IN] callback Function callback called at the end of the timeout + */ +void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) ); + +/*! + * Timer IRQ event handler + */ +void TimerIrqHandler( void ); + +/*! + * \brief Starts and adds the timer object to the list of timer events + * + * \param [IN] obj Structure containing the timer object parameters + */ +void TimerStart( TimerEvent_t *obj ); + +/*! + * \brief Stops and removes the timer object from the list of timer events + * + * \param [IN] obj Structure containing the timer object parameters + */ +void TimerStop( TimerEvent_t *obj ); + +/*! + * \brief Resets the timer object + * + * \param [IN] obj Structure containing the timer object parameters + */ +void TimerReset( TimerEvent_t *obj ); + +/*! + * \brief Set timer new timeout value + * + * \param [IN] obj Structure containing the timer object parameters + * \param [IN] value New timer timeout value + */ +void TimerSetValue( TimerEvent_t *obj, uint32_t value ); + +/*! + * \brief Read the current time + * + * \retval time returns current time + */ +TimerTime_t TimerGetCurrentTime( void ); + +/*! + * \brief Return the Time elapsed since a fix moment in Time + * + * \param [IN] savedTime fix moment in Time + * \retval time returns elapsed time + */ +TimerTime_t TimerGetElapsedTime( TimerTime_t savedTime ); + +/*! + * \brief Return the Time elapsed since a fix moment in Time + * + * \param [IN] eventInFuture fix moment in the future + * \retval time returns difference between now and future event + */ +TimerTime_t TimerGetFutureTime( TimerTime_t eventInFuture ); + +/*! + * \brief Manages the entry into ARM cortex deep-sleep mode + */ +void TimerLowPowerHandler( void ); + +#endif // __TIMER_H__ diff --git a/libraries/LoRa_Node/src/system/uart.c b/libraries/LoRa_Node/src/system/uart.c new file mode 100644 index 0000000..093f8c6 --- /dev/null +++ b/libraries/LoRa_Node/src/system/uart.c @@ -0,0 +1,173 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements the generic UART driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +//-------------------modified +#include "boards/arduino/board.h" +// #include "board.h" +// #include "uart-board.h" +//--------------------------- +#if defined( USE_USB_CDC ) +#include "uart-usb-board.h" +#endif + +#include "uart.h" + +/*! + * Number of times the UartPutBuffer will try to send the buffer before + * returning ERROR + */ +#define TX_BUFFER_RETRY_COUNT 10 + +void UartInit( Uart_t *obj, uint8_t uartId, PinNames tx, PinNames rx ) +{ + if( obj->IsInitialized == false ) + { + obj->IsInitialized = true; + +// if( uartId == UART_USB_CDC ) +// { +//#if defined( USE_USB_CDC ) +// UartUsbInit( obj, uartId, NC, NC ); +//#endif +// } +// else +// { +// UartMcuInit( obj, uartId, tx, rx ); +// } + } +} + +void UartConfig( Uart_t *obj, UartMode_t mode, uint32_t baudrate, WordLength_t wordLength, StopBits_t stopBits, Parity_t parity, FlowCtrl_t flowCtrl ) +{ + if( obj->IsInitialized == false ) + { + // UartInit function must be called first. + assert_param( FAIL ); + } +// if( obj->UartId == UART_USB_CDC ) +// { +//#if defined( USE_USB_CDC ) +// UartUsbConfig( obj, mode, baudrate, wordLength, stopBits, parity, flowCtrl ); +//#endif +// } +// else +// { +// UartMcuConfig( obj, mode, baudrate, wordLength, stopBits, parity, flowCtrl ); +// } +} + +void UartDeInit( Uart_t *obj ) +{ + obj->IsInitialized = false; +// if( obj->UartId == UART_USB_CDC ) +// { +//#if defined( USE_USB_CDC ) +// UartUsbDeInit( obj ); +//#endif +// } +// else +// { +// UartMcuDeInit( obj ); +// } +} + +uint8_t UartPutChar( Uart_t *obj, uint8_t data ) +{ +// if( obj->UartId == UART_USB_CDC ) +// { +//#if defined( USE_USB_CDC ) +// return UartUsbPutChar( obj, data ); +//#else +// return 255; // Not supported +//#endif +// } +// else +// { +// return UartMcuPutChar( obj, data ); +// } +} + +uint8_t UartGetChar( Uart_t *obj, uint8_t *data ) +{ +// if( obj->UartId == UART_USB_CDC ) +// { +//#if defined( USE_USB_CDC ) +// return UartUsbGetChar( obj, data ); +//#else +// return 255; // Not supported +//#endif +// } +// else +// { +// return UartMcuGetChar( obj, data ); +// } +} + +uint8_t UartPutBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size ) +{ +// if( obj->UartId == UART_USB_CDC ) +// { +//#if defined( USE_USB_CDC ) +// return UartUsbPutBuffer( obj, buffer, size ); +//#else +// return 255; // Not supported +//#endif +// } +// else +// { +// uint8_t retryCount; +// uint16_t i; +// +// for( i = 0; i < size; i++ ) +// { +// retryCount = 0; +// while( UartPutChar( obj, buffer[i] ) != 0 ) +// { +// retryCount++; +// +// Exit if something goes terribly wrong +// if( retryCount > TX_BUFFER_RETRY_COUNT ) +// { +// return 1; // Error +// } +// } +// } +// return 0; // OK +// } +} + +uint8_t UartGetBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size, uint16_t *nbReadBytes ) +{ + uint16_t localSize = 0; + + while( localSize < size ) + { + if( UartGetChar( obj, buffer + localSize ) == 0 ) + { + localSize++; + } + else + { + break; + } + } + + *nbReadBytes = localSize; + + if( localSize == 0 ) + { + return 1; // Empty + } + return 0; // OK +} diff --git a/libraries/LoRa_Node/src/system/uart.h b/libraries/LoRa_Node/src/system/uart.h new file mode 100644 index 0000000..b8a51d5 --- /dev/null +++ b/libraries/LoRa_Node/src/system/uart.h @@ -0,0 +1,167 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: Implements the generic UART driver + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __UART_H__ +#define __UART_H__ + +#include "fifo.h" + +/*! + * UART notification identifier + */ +typedef enum +{ + UART_NOTIFY_TX, + UART_NOTIFY_RX +}UartNotifyId_t; + +/*! + * UART object type definition + */ +typedef struct +{ + uint8_t UartId; + bool IsInitialized; + Gpio_t Tx; + Gpio_t Rx; + Fifo_t FifoTx; + Fifo_t FifoRx; + /*! + * IRQ user notification callback prototype. + */ + void ( *IrqNotify )( UartNotifyId_t id ); +}Uart_t; + +/*! + * Operation Mode for the UART + */ +typedef enum +{ + TX_ONLY = 0, + RX_ONLY, + RX_TX +}UartMode_t; + +/*! + * UART word length + */ +typedef enum +{ + UART_8_BIT = 0, + UART_9_BIT +}WordLength_t; + +/*! + * UART stop bits + */ +typedef enum +{ + UART_1_STOP_BIT = 0, + UART_0_5_STOP_BIT, + UART_2_STOP_BIT, + UART_1_5_STOP_BIT +}StopBits_t; + +/*! + * UART parity + */ +typedef enum +{ + NO_PARITY = 0, + EVEN_PARITY, + ODD_PARITY +}Parity_t; + +/*! + * UART flow control + */ +typedef enum +{ + NO_FLOW_CTRL = 0, + RTS_FLOW_CTRL, + CTS_FLOW_CTRL, + RTS_CTS_FLOW_CTRL +}FlowCtrl_t; + +/*! + * \brief Initializes the UART object and MCU peripheral + * + * \param [IN] obj UART object + * \param [IN] tx UART Tx pin name to be used + * \param [IN] rx UART Rx pin name to be used + */ +void UartInit( Uart_t *obj, uint8_t uartId, PinNames tx, PinNames rx ); + +/*! + * \brief Configures the UART object and MCU peripheral + * + * \remark UartInit function must be called first. + * + * \param [IN] obj UART object + * \param [IN] mode Mode of operation for the UART + * \param [IN] baudrate UART baudrate + * \param [IN] wordLength packet length + * \param [IN] stopBits stop bits setup + * \param [IN] parity packet parity + * \param [IN] flowCtrl UART flow control + */ +void UartConfig( Uart_t *obj, UartMode_t mode, uint32_t baudrate, WordLength_t wordLength, StopBits_t stopBits, Parity_t parity, FlowCtrl_t flowCtrl ); + +/*! + * \brief DeInitializes the UART object and MCU pin + * + * \param [IN] obj UART object + */ +void UartDeInit( Uart_t *obj ); + +/*! + * \brief Sends a character to the UART + * + * \param [IN] obj UART object + * \param [IN] data Character to be sent + * \retval status [0: OK, 1: Busy] + */ +uint8_t UartPutChar( Uart_t *obj, uint8_t data ); + +/*! + * \brief Gets a character from the UART + * + * \param [IN] obj UART object + * \param [IN] data Received character + * \retval status [0: OK, 1: Busy] + */ +uint8_t UartGetChar( Uart_t *obj, uint8_t *data ); + +/*! + * \brief Sends a buffer to the UART + * + * \param [IN] obj UART object + * \param [IN] buffer Buffer to be sent + * \param [IN] size Buffer size + * \retval status [0: OK, 1: Busy] + */ +uint8_t UartPutBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size ); + +/*! + * \brief Gets a character from the UART + * + * \param [IN] obj UART object + * \param [IN] buffer Buffer to be sent + * \param [IN] size Buffer size + * \param [OUT] nbReadBytes Number of bytes really read + * \retval status [0: OK, 1: Busy] + */ +uint8_t UartGetBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size, uint16_t *nbReadBytes ); + +#endif // __UART_H__ diff --git a/libraries/arduino-LoRa-master/.github/FUNDING.yml b/libraries/arduino-LoRa-master/.github/FUNDING.yml new file mode 100644 index 0000000..6612607 --- /dev/null +++ b/libraries/arduino-LoRa-master/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [sandeepmistry] diff --git a/libraries/arduino-LoRa-master/.travis.yml b/libraries/arduino-LoRa-master/.travis.yml new file mode 100644 index 0000000..b574b4e --- /dev/null +++ b/libraries/arduino-LoRa-master/.travis.yml @@ -0,0 +1,39 @@ +language: generic +env: + global: + - IDE_VERSION=1.8.2 + matrix: + - BOARD="arduino:avr:uno" + - BOARD="arduino:avr:micro" + - BOARD="arduino:avr:mega:cpu=atmega2560" + - BOARD="arduino:samd:arduino_zero_edbg" + - BOARD="arduino:samd:mkr1000" + - BOARD="arduino:samd:mkrzero" + - BOARD="arduino:samd:mkrwan1300" + - BOARD="arduino:samd:mkrwan1310" +before_install: + - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz + - tar xf arduino-$IDE_VERSION-linux64.tar.xz + - mv arduino-$IDE_VERSION $HOME/arduino-ide + - export PATH=$PATH:$HOME/arduino-ide + - if [[ "$BOARD" =~ "arduino:samd:" ]]; then + arduino --install-boards arduino:samd &> /dev/null; + fi + - buildExampleSketch() { arduino --verbose-build --verify --board $BOARD $PWD/examples/$1/$1.ino; } +install: + - mkdir -p $HOME/Arduino/libraries + - ln -s $PWD $HOME/Arduino/libraries/LoRa +script: + - buildExampleSketch LoRaDumpRegisters + - buildExampleSketch LoRaDuplex + - if [[ "$BOARD" != "arduino:samd:mkrwan1300" ]]; then + buildExampleSketch LoRaDuplexCallback; + fi + - buildExampleSketch LoRaReceiver + - if [[ "$BOARD" != "arduino:samd:mkrwan1300" ]]; then + buildExampleSketch LoRaReceiverCallback; + fi + - buildExampleSketch LoRaSender + - buildExampleSketch LoRaSenderNonBlocking + - buildExampleSketch LoRaSetSpread + - buildExampleSketch LoRaSetSyncWord diff --git a/libraries/arduino-LoRa-master/API.md b/libraries/arduino-LoRa-master/API.md new file mode 100644 index 0000000..74133d7 --- /dev/null +++ b/libraries/arduino-LoRa-master/API.md @@ -0,0 +1,396 @@ +# LoRa API + +## Include Library + +```arduino +#include +``` + +## Setup + +### Begin + +Initialize the library with the specified frequency. + +```arduino +LoRa.begin(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `868E6`, `915E6`) + +Returns `1` on success, `0` on failure. + +### Set pins + +Override the default `NSS`, `NRESET`, and `DIO0` pins used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setPins(ss, reset, dio0); +``` + * `ss` - new slave select pin to use, defaults to `10` + * `reset` - new reset pin to use, defaults to `9` + * `dio0` - new DIO0 pin to use, defaults to `2`. **Must** be interrupt capable via [attachInterrupt(...)](https://www.arduino.cc/en/Reference/AttachInterrupt). + +This call is optional and only needs to be used if you need to change the default pins used. + +#### No MCU controlled reset pin + +To save further pins one could connect the reset pin of the MCU with reset pin of the radio thus resetting only during startup. + +* `reset` - set to `-1` to omit this pin + +#### Pin dio0 interrupt callbacks + +The dio0 pin can be used for transmission finish callback and/or receiving callback, check `onTxDone` and `onReceive`. + +### Set SPI interface + +Override the default SPI interface used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPI(spi); +``` + * `spi` - new SPI interface to use, defaults to `SPI` + +This call is optional and only needs to be used if you need to change the default SPI interface used, in the case your Arduino (or compatible) board has more than one SPI interface present. + +### Set SPI Frequency + +Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPIFrequency(frequency); +``` + * `frequency` - new SPI frequency to use, defaults to `8E6` + +This call is optional and only needs to be used if you need to change the default SPI frequency used. Some logic level converters cannot support high speeds such as 8 MHz, so a lower SPI frequency can be selected with `LoRa.setSPIFrequency(frequency)`. + +### End + +Stop the library + +```arduino +LoRa.end() +``` + +## Sending data + +### Begin packet + +Start the sequence of sending a packet. + +```arduino +LoRa.beginPacket(); + +LoRa.beginPacket(implicitHeader); +``` + + * `implicitHeader` - (optional) `true` enables implicit header mode, `false` enables explicit header mode (default) + +Returns `1` if radio is ready to transmit, `0` if busy or on failure. + +### Writing + +Write data to the packet. Each packet can contain up to 255 bytes. + +```arduino +LoRa.write(byte); + +LoRa.write(buffer, length); +``` +* `byte` - single byte to write to packet + +or + +* `buffer` - data to write to packet +* `length` - size of data to write + +Returns the number of bytes written. + +**Note:** Other Arduino `Print` API's can also be used to write data into the packet + +### End packet + +End the sequence of sending a packet. + +```arduino +LoRa.endPacket(); + +LoRa.endPacket(async); +``` + * `async` - (optional) `true` enables non-blocking mode, `false` waits for transmission to be completed (default) + +Returns `1` on success, `0` on failure. + +### Tx Done + +**WARNING**: TxDone callback uses the interrupt pin on the `dio0` check `setPins` function! + +### Register callback + +Register a callback function for when a packet transmission finish. + +```arduino +LoRa.onTxDone(onTxDone); + +void onTxDone() { + // ... +} +``` + + * `onTxDone` - function to call when a packet transmission finish. + +## Receiving data + +### Parsing packet + +Check if a packet has been received. + +```arduino +int packetSize = LoRa.parsePacket(); + +int packetSize = LoRa.parsePacket(size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + + +Returns the packet size in bytes or `0` if no packet was received. + +### Continuous receive mode + +**WARNING**: Receive callback uses the interrupt pin on the `dio0`, check `setPins` function! + +#### Register callback + +Register a callback function for when a packet is received. + +```arduino +LoRa.onReceive(onReceive); + +void onReceive(int packetSize) { + // ... +} +``` + + * `onReceive` - function to call when a packet is received. + +#### Receive mode + +Puts the radio in continuous receive mode. + +```arduino +LoRa.receive(); + +LoRa.receive(int size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + +The `onReceive` callback will be called when a packet is received. + +### Packet RSSI + +```arduino +int rssi = LoRa.packetRssi(); +``` + +Returns the averaged RSSI of the last received packet (dBm). + +### Packet SNR + +```arduino +float snr = LoRa.packetSnr(); +``` + +Returns the estimated SNR of the received packet in dB. + +## RSSI + +```arduino +int rssi = LoRa.rssi(); +``` + +Returns the current RSSI of the radio (dBm). RSSI can be read at any time (during packet reception or not) + +### Packet Frequency Error + +```arduino +long freqErr = LoRa.packetFrequencyError(); +``` + +Returns the frequency error of the received packet in Hz. The frequency error is the frequency offset between the receiver centre frequency and that of an incoming LoRa signal. + +### Available + +```arduino +int availableBytes = LoRa.available() +``` + +Returns number of bytes available for reading. + +### Peeking + +Peek at the next byte in the packet. + +```arduino +byte b = LoRa.peek(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +### Reading + +Read the next byte from the packet. + +```arduino +byte b = LoRa.read(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet + +## Other radio modes + +### Idle mode + +Put the radio in idle (standby) mode. + +```arduino +LoRa.idle(); +``` + +### Sleep mode + +Put the radio in sleep mode. + +```arduino +LoRa.sleep(); +``` + +## Radio parameters + +### TX Power + +Change the TX power of the radio. + +```arduino +LoRa.setTxPower(txPower); + +LoRa.setTxPower(txPower, outputPin); +``` + * `txPower` - TX power in dB, defaults to `17` + * `outputPin` - (optional) PA output pin, supported values are `PA_OUTPUT_RFO_PIN` and `PA_OUTPUT_PA_BOOST_PIN`, defaults to `PA_OUTPUT_PA_BOOST_PIN`. + +Supported values are `2` to `20` for `PA_OUTPUT_PA_BOOST_PIN`, and `0` to `14` for `PA_OUTPUT_RFO_PIN`. + +Most modules have the PA output pin connected to PA BOOST, + +### Frequency + +Change the frequency of the radio. + +```arduino +LoRa.setFrequency(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +### Spreading Factor + +Change the spreading factor of the radio. + +```arduino +LoRa.setSpreadingFactor(spreadingFactor); +``` + * `spreadingFactor` - spreading factor, defaults to `7` + +Supported values are between `6` and `12`. If a spreading factor of `6` is set, implicit header mode must be used to transmit and receive packets. + +### Signal Bandwidth + +Change the signal bandwidth of the radio. + +```arduino +LoRa.setSignalBandwidth(signalBandwidth); +``` + + * `signalBandwidth` - signal bandwidth in Hz, defaults to `125E3`. + +Supported values are `7.8E3`, `10.4E3`, `15.6E3`, `20.8E3`, `31.25E3`, `41.7E3`, `62.5E3`, `125E3`, `250E3`, and `500E3`. + +### Coding Rate + +Change the coding rate of the radio. + +```arduino +LoRa.setCodingRate4(codingRateDenominator); +``` + + * `codingRateDenominator` - denominator of the coding rate, defaults to `5` + +Supported values are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. The coding rate numerator is fixed at `4`. + +### Preamble Length + +Change the preamble length of the radio. + +```arduino +LoRa.setPreambleLength(preambleLength); +``` + + * `preambleLength` - preamble length in symbols, defaults to `8` + +Supported values are between `6` and `65535`. + +### Sync Word + +Change the sync word of the radio. + +```arduino +LoRa.setSyncWord(syncWord); +``` + + * `syncWord` - byte value to use as the sync word, defaults to `0x12` + +### CRC + +Enable or disable CRC usage, by default a CRC is not used. + +```arduino +LoRa.enableCrc(); + +LoRa.disableCrc(); +``` + +### Invert IQ Signals + +Enable or disable Invert the LoRa I and Q signals, by default a invertIQ is not used. + +```arduino +LoRa.enableInvertIQ(); + +LoRa.disableInvertIQ(); +``` +### LNA Gain + +Set LNA Gain for better RX sensitivity, by default AGC (Automatic Gain Control) is used and LNA gain is not used. + +```arduino +LoRa.setGain(gain); +``` + + * `gain` - LNA gain + +Supported values are between `0` and `6`. If gain is 0, AGC will be enabled and LNA gain will not be used. Else if gain is from 1 to 6, AGC will be disabled and LNA gain will be used. + +## Other functions + +### Random + +Generate a random byte, based on the Wideband RSSI measurement. + +``` +byte b = LoRa.random(); +``` + +Returns random byte. diff --git a/libraries/arduino-LoRa-master/LICENSE b/libraries/arduino-LoRa-master/LICENSE new file mode 100644 index 0000000..1e85b95 --- /dev/null +++ b/libraries/arduino-LoRa-master/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Sandeep Mistry + +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. diff --git a/libraries/arduino-LoRa-master/README.md b/libraries/arduino-LoRa-master/README.md new file mode 100644 index 0000000..51341ff --- /dev/null +++ b/libraries/arduino-LoRa-master/README.md @@ -0,0 +1,91 @@ +# Arduino LoRa + +[![Build Status](https://travis-ci.org/sandeepmistry/arduino-LoRa.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-LoRa) + +An [Arduino](https://arduino.cc/) library for sending and receiving data using [LoRa](https://www.lora-alliance.org/) radios. + +## Compatible Hardware + + * [Semtech SX1276/77/78/79](https://www.semtech.com/apps/product.php?pn=SX1276) based boards including: + * [Dragino Lora Shield](https://www.dragino.com/products/lora/item/102-lora-shield.html) + * [HopeRF](https://www.hoperf.com/modules/lora/index.html) [RFM95W](https://www.hoperf.com/modules/lora/RFM95.html), [RFM96W](https://www.hoperf.com/modules/lora/RFM96.html), and [RFM98W](https://www.hoperf.com/modules/lora/RFM98.html) + * [Modtronix](http://modtronix.com/) [inAir4](http://modtronix.com/inair4.html), [inAir9](http://modtronix.com/inair9.html), and [inAir9B](http://modtronix.com/inair9b.html) + * [Arduino MKR WAN 1300](https://store.arduino.cc/usa/mkr-wan-1300) + * **NOTE:** Requires firmware v1.1.6 or later on the on-board Murata module. Please use the [MKRWANFWUpdate_standalone example](https://github.com/arduino-libraries/MKRWAN/blob/master/examples/MKRWANFWUpdate_standalone/MKRWANFWUpdate_standalone.ino) from latest [MKRWAN library](https://github.com/arduino-libraries/MKRWAN) release to update the firmware. + * **WARNING**: [LoRa.onReceive(...)](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#register-callback) and [LoRa.recieve()](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#receive-mode) is not compatible with this board! + +### Semtech SX1276/77/78/79 wiring + +| Semtech SX1276/77/78/79 | Arduino | +| :---------------------: | :------:| +| VCC | 3.3V | +| GND | GND | +| SCK | SCK | +| MISO | MISO | +| MOSI | MOSI | +| NSS | 10 | +| NRESET | 9 | +| DIO0 | 2 | + + +`NSS`, `NRESET`, and `DIO0` pins can be changed by using `LoRa.setPins(ss, reset, dio0)`. `DIO0` pin is optional, it is only needed for receive callback mode. If `DIO0` pin is used, it **must** be interrupt capable via [`attachInterrupt(...)`](https://www.arduino.cc/en/Reference/AttachInterrupt). + +**NOTES**: + * Some boards (like the Arduino Nano), cannot supply enough current for the SX127x in TX mode. This will cause lockups when sending, be sure to use an external 3.3V supply that can provide at least 120mA's when using these boards. + * If your Arduino board operates at 5V, like the Arduino Uno, Leonardo or Mega, you will need to use a level converter for the wiring to the Semtech SX127x module. Most Semtech SX127x breakout boards do not have logic level converters built-in. + +## Installation + +### Using the Arduino IDE Library Manager + +1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...` +2. Type `LoRa` into the search box. +3. Click the row to select the library. +4. Click the `Install` button to install the library. + +### Using Git + +```sh +cd ~/Documents/Arduino/libraries/ +git clone https://github.com/sandeepmistry/arduino-LoRa LoRa +``` + +## API + +See [API.md](API.md). + +## Examples + +See [examples](examples) folder. + +## FAQ + +**1) Initilizating the LoRa radio is failing** + +Please check the wiring you are using matches what's listed in [Semtech SX1276/77/78/79 wiring](#semtech-sx1276777879-wiring). You can also use `LoRa.setPins(ss, reset, dio0)` to change the default pins used. Some logic level converters cannot operate at 8 MHz, you can call `LoRa.setSPIFrequency(frequency)` to lower the SPI frequency used by the library. Both API's must be called before `LoRa.begin(...)`. + +**2) Can other radios see the packets I'm sending?** + +Yes, any LoRa radio that are configured with the same radio parameters and in range can see the packets you send. + +**3) Is the data I'm sending encrypted?** + +No, all data is sent unencrypted. If want your packet data to be encrypted, you must encrypt it before passing it into this library, followed by decrypting on the receiving end. + +**4) How does this library differ from LoRaWAN libraries?** + +This library exposes the LoRa radio directly, and allows you to send data to any radios in range with same radio parameters. All data is broadcasted and there is no addressing. LoRaWAN builds on top of LoRA, but adds addressing, encryption, and additional layers. It also requires a LoRaWAN gateway and LoRaWAN network and application server. + +**5) Does this library honor duty cycles?** + +No, you have to manage it by your self. + +**6) Which frequencies can I use?** + +You can use [this table](https://www.thethingsnetwork.org/wiki/LoRaWAN/Frequencies/By-Country) to lookup the available frequencies by your country. The selectable frequency also depends on your hardware. You can lookup the data sheet or ask your supplier. + +Please also notice the frequency dependent duty cycles for legal reasons! + +## License + +This libary is [licensed](LICENSE) under the [MIT Licence](https://en.wikipedia.org/wiki/MIT_License). diff --git a/libraries/arduino-LoRa-master/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino b/libraries/arduino-LoRa-master/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino new file mode 100644 index 0000000..5a23d2e --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino @@ -0,0 +1,30 @@ +/* + LoRa register dump + + This examples shows how to inspect and output the LoRa radio's + registers on the Serial interface +*/ +#include // include libraries +#include + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Dump Registers"); + + // override the default CS, reset, and IRQ pins (optional) + // LoRa.setPins(7, 6, 1); // set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.dumpRegisters(Serial); +} + + +void loop() { +} + diff --git a/libraries/arduino-LoRa-master/examples/LoRaDuplex/LoRaDuplex.ino b/libraries/arduino-LoRa-master/examples/LoRaDuplex/LoRaDuplex.ino new file mode 100644 index 0000000..c914254 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaDuplex/LoRaDuplex.ino @@ -0,0 +1,106 @@ +/* + LoRa Duplex communication + + Sends a message every half second, and polls continually + for new incoming messages. Implements a one-byte addressing scheme, + with 0xFF as the broadcast address. + + Uses readString() from Stream class to read payload. The Stream class' + timeout may affect other functuons, like the radio's callback. For an + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +String outgoing; // outgoing message + +byte msgCount = 0; // count of outgoing messages +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + if (incomingLength != incoming.length()) { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/arduino-LoRa-master/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino b/libraries/arduino-LoRa-master/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino new file mode 100644 index 0000000..0511f3e --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino @@ -0,0 +1,110 @@ +/* + LoRa Duplex communication wth callback + + Sends a message every half second, and uses callback + for new incoming messages. Implements a one-byte addressing scheme, + with 0xFF as the broadcast address. + + Note: while sending, LoRa radio is not listening for incoming messages. + Note2: when using the callback method, you can't use any of the Stream + functions that rely on the timeout, such as readString, parseInt(), etc. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#error "This example is not compatible with the Arduino MKR WAN 1300 board!" +#endif + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +String outgoing; // outgoing message +byte msgCount = 0; // count of outgoing messages +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex with callback"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.onReceive(onReceive); + LoRa.receive(); + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + LoRa.receive(); // go back into receive mode + } +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; // payload of packet + + while (LoRa.available()) { // can't use readString() in callback, so + incoming += (char)LoRa.read(); // add bytes one by one + } + + if (incomingLength != incoming.length()) { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/arduino-LoRa-master/examples/LoRaReceiver/LoRaReceiver.ino b/libraries/arduino-LoRa-master/examples/LoRaReceiver/LoRaReceiver.ino new file mode 100644 index 0000000..ccbb453 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaReceiver/LoRaReceiver.ino @@ -0,0 +1,32 @@ +#include +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + // try to parse packet + int packetSize = LoRa.parsePacket(); + if (packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + while (LoRa.available()) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); + } +} diff --git a/libraries/arduino-LoRa-master/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino b/libraries/arduino-LoRa-master/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino new file mode 100644 index 0000000..ff32318 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino @@ -0,0 +1,45 @@ +#include +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#error "This example is not compatible with the Arduino MKR WAN 1300 board!" +#endif + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver Callback"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + + // Uncomment the next line to disable the default AGC and set LNA gain, values between 1 - 6 are supported + // LoRa.setGain(6); + + // register the receive callback + LoRa.onReceive(onReceive); + + // put the radio into receive mode + LoRa.receive(); +} + +void loop() { + // do nothing +} + +void onReceive(int packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + for (int i = 0; i < packetSize; i++) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); +} diff --git a/libraries/arduino-LoRa-master/examples/LoRaSender/LoRaSender.ino b/libraries/arduino-LoRa-master/examples/LoRaSender/LoRaSender.ino new file mode 100644 index 0000000..a252ee5 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSender/LoRaSender.ino @@ -0,0 +1,31 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + Serial.print("Sending packet: "); + Serial.println(counter); + + // send packet + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + counter++; + + delay(5000); +} diff --git a/libraries/arduino-LoRa-master/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino b/libraries/arduino-LoRa-master/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino new file mode 100644 index 0000000..0f39077 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino @@ -0,0 +1,35 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender non-blocking"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + // wait until the radio is ready to send a packet + while (LoRa.beginPacket() == 0) { + Serial.print("waiting for radio ... "); + delay(100); + } + + Serial.print("Sending packet non-blocking: "); + Serial.println(counter); + + // send in async / non-blocking mode + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(true); // true = async / non-blocking mode + + counter++; +} diff --git a/libraries/arduino-LoRa-master/examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino b/libraries/arduino-LoRa-master/examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino new file mode 100644 index 0000000..aa79499 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino @@ -0,0 +1,50 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender non-blocking Callback"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + + LoRa.onTxDone(onTxDone); +} + +void loop() { + if (runEvery(5000)) { // repeat every 5000 millis + + Serial.print("Sending packet non-blocking: "); + Serial.println(counter); + + // send in async / non-blocking mode + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(true); // true = async / non-blocking mode + + counter++; + } +} + +void onTxDone() { + Serial.println("TxDone"); +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} diff --git a/libraries/arduino-LoRa-master/examples/LoRaSetSpread/LoRaSetSpread.ino b/libraries/arduino-LoRa-master/examples/LoRaSetSpread/LoRaSetSpread.ino new file mode 100644 index 0000000..99d0e8d --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSetSpread/LoRaSetSpread.ino @@ -0,0 +1,87 @@ +/* + LoRa Duplex communication with Spreading Factor + + Sends a message every half second, and polls continually + for new incoming messages. Sets the LoRa radio's spreading factor. + + Spreading factor affects how far apart the radio's transmissions + are, across the available bandwidth. Radios with different spreading + factors will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + Spreading factor affects reliability of transmission at high rates, however, + so avoid a hugh spreading factor when you're sending continually. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Spreading Factor. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex - Set spreading factor"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin); // set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.setSpreadingFactor(8); // ranges from 6-12,default 7 see API docs + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/arduino-LoRa-master/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino b/libraries/arduino-LoRa-master/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino new file mode 100644 index 0000000..69af87d --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino @@ -0,0 +1,82 @@ +/* + LoRa Duplex communication with Sync Word + + Sends a message every half second, and polls continually + for new incoming messages. Sets the LoRa radio's Sync Word. + + Spreading factor is basically the radio's network ID. Radios with different + Sync Words will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Sync Word. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex - Set sync word"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.setSyncWord(0xF3); // ranges from 0-0xFF, default 0x34, see API docs + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/libraries/arduino-LoRa-master/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino b/libraries/arduino-LoRa-master/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino new file mode 100644 index 0000000..95d84a7 --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino @@ -0,0 +1,117 @@ +/* + LoRa Simple Gateway/Node Exemple + + This code uses InvertIQ function to create a simple Gateway/Node logic. + + Gateway - Sends messages with enableInvertIQ() + - Receives messages with disableInvertIQ() + + Node - Sends messages with disableInvertIQ() + - Receives messages with enableInvertIQ() + + With this arrangement a Gateway never receive messages from another Gateway + and a Node never receive message from another Node. + Only Gateway to Node and vice versa. + + This code receives messages and sends a message every second. + + InvertIQ function basically invert the LoRa I and Q signals. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on InvertIQ register 0x33. + + created 05 August 2018 + by Luiz H. Cassettari +*/ + +#include // include libraries +#include + +const long frequency = 915E6; // LoRa Frequency + +const int csPin = 10; // LoRa radio chip select +const int resetPin = 9; // LoRa radio reset +const int irqPin = 2; // change for your board; must be a hardware interrupt pin + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + LoRa.setPins(csPin, resetPin, irqPin); + + if (!LoRa.begin(frequency)) { + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + Serial.println("LoRa Simple Gateway"); + Serial.println("Only receive messages from nodes"); + Serial.println("Tx: invertIQ enable"); + Serial.println("Rx: invertIQ disable"); + Serial.println(); + + LoRa.onReceive(onReceive); + LoRa.onTxDone(onTxDone); + LoRa_rxMode(); +} + +void loop() { + if (runEvery(5000)) { // repeat every 5000 millis + + String message = "HeLoRa World! "; + message += "I'm a Gateway! "; + message += millis(); + + LoRa_sendMessage(message); // send a message + + Serial.println("Send Message!"); + } +} + +void LoRa_rxMode(){ + LoRa.disableInvertIQ(); // normal mode + LoRa.receive(); // set receive mode +} + +void LoRa_txMode(){ + LoRa.idle(); // set standby mode + LoRa.enableInvertIQ(); // active invert I and Q signals +} + +void LoRa_sendMessage(String message) { + LoRa_txMode(); // set tx mode + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(true); // finish packet and send it +} + +void onReceive(int packetSize) { + String message = ""; + + while (LoRa.available()) { + message += (char)LoRa.read(); + } + + Serial.print("Gateway Receive: "); + Serial.println(message); +} + +void onTxDone() { + Serial.println("TxDone"); + LoRa_rxMode(); +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} + diff --git a/libraries/arduino-LoRa-master/examples/LoRaSimpleNode/LoRaSimpleNode.ino b/libraries/arduino-LoRa-master/examples/LoRaSimpleNode/LoRaSimpleNode.ino new file mode 100644 index 0000000..db8c6fa --- /dev/null +++ b/libraries/arduino-LoRa-master/examples/LoRaSimpleNode/LoRaSimpleNode.ino @@ -0,0 +1,117 @@ +/* + LoRa Simple Gateway/Node Exemple + + This code uses InvertIQ function to create a simple Gateway/Node logic. + + Gateway - Sends messages with enableInvertIQ() + - Receives messages with disableInvertIQ() + + Node - Sends messages with disableInvertIQ() + - Receives messages with enableInvertIQ() + + With this arrangement a Gateway never receive messages from another Gateway + and a Node never receive message from another Node. + Only Gateway to Node and vice versa. + + This code receives messages and sends a message every second. + + InvertIQ function basically invert the LoRa I and Q signals. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on InvertIQ register 0x33. + + created 05 August 2018 + by Luiz H. Cassettari +*/ + +#include // include libraries +#include + +const long frequency = 915E6; // LoRa Frequency + +const int csPin = 10; // LoRa radio chip select +const int resetPin = 9; // LoRa radio reset +const int irqPin = 2; // change for your board; must be a hardware interrupt pin + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + LoRa.setPins(csPin, resetPin, irqPin); + + if (!LoRa.begin(frequency)) { + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + Serial.println("LoRa Simple Node"); + Serial.println("Only receive messages from gateways"); + Serial.println("Tx: invertIQ disable"); + Serial.println("Rx: invertIQ enable"); + Serial.println(); + + LoRa.onReceive(onReceive); + LoRa.onTxDone(onTxDone); + LoRa_rxMode(); +} + +void loop() { + if (runEvery(1000)) { // repeat every 1000 millis + + String message = "HeLoRa World! "; + message += "I'm a Node! "; + message += millis(); + + LoRa_sendMessage(message); // send a message + + Serial.println("Send Message!"); + } +} + +void LoRa_rxMode(){ + LoRa.enableInvertIQ(); // active invert I and Q signals + LoRa.receive(); // set receive mode +} + +void LoRa_txMode(){ + LoRa.idle(); // set standby mode + LoRa.disableInvertIQ(); // normal mode +} + +void LoRa_sendMessage(String message) { + LoRa_txMode(); // set tx mode + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(true); // finish packet and send it +} + +void onReceive(int packetSize) { + String message = ""; + + while (LoRa.available()) { + message += (char)LoRa.read(); + } + + Serial.print("Node Receive: "); + Serial.println(message); +} + +void onTxDone() { + Serial.println("TxDone"); + LoRa_rxMode(); +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} + diff --git a/libraries/arduino-LoRa-master/issue_template.md b/libraries/arduino-LoRa-master/issue_template.md new file mode 100644 index 0000000..3fa6d74 --- /dev/null +++ b/libraries/arduino-LoRa-master/issue_template.md @@ -0,0 +1,3 @@ +Are you receiving `Starting LoRa failed` while using the demo code? + +PLEASE see the [FAQ #1](https://github.com/sandeepmistry/arduino-LoRa#faq) about using [setPins](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#set-pins) **BEFORE** submitting an issue. diff --git a/libraries/arduino-LoRa-master/keywords.txt b/libraries/arduino-LoRa-master/keywords.txt new file mode 100644 index 0000000..2e74cff --- /dev/null +++ b/libraries/arduino-LoRa-master/keywords.txt @@ -0,0 +1,64 @@ +####################################### +# Syntax Coloring Map For LoRa +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LoRa KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 + +beginPacket KEYWORD2 +endPacket KEYWORD2 + +parsePacket KEYWORD2 +packetRssi KEYWORD2 +packetSnr KEYWORD2 +packetFrequencyError KEYWORD2 + +rssi KEYWORD2 + +write KEYWORD2 + +available KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 + +onReceive KEYWORD2 +onTxDone KEYWORD2 +receive KEYWORD2 +idle KEYWORD2 +sleep KEYWORD2 + +setTxPower KEYWORD2 +setFrequency KEYWORD2 +setSpreadingFactor KEYWORD2 +setSignalBandwidth KEYWORD2 +setCodingRate4 KEYWORD2 +setPreambleLength KEYWORD2 +setSyncWord KEYWORD2 +enableCrc KEYWORD2 +disableCrc KEYWORD2 +enableInvertIQ KEYWORD2 +disableInvertIQ KEYWORD2 +setGain KEYWORD2 + +random KEYWORD2 +setPins KEYWORD2 +setSPIFrequency KEYWORD2 +dumpRegisters KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +PA_OUTPUT_RFO_PIN LITERAL1 +PA_OUTPUT_PA_BOOST_PIN LITERAL1 diff --git a/libraries/arduino-LoRa-master/library.properties b/libraries/arduino-LoRa-master/library.properties new file mode 100644 index 0000000..e869420 --- /dev/null +++ b/libraries/arduino-LoRa-master/library.properties @@ -0,0 +1,10 @@ +name=LoRa +version=0.8.0 +author=Sandeep Mistry +maintainer=Sandeep Mistry +sentence=An Arduino library for sending and receiving data using LoRa radios. +paragraph=Supports Semtech SX1276/77/78/79 based boards/shields. +category=Communication +url=https://github.com/sandeepmistry/arduino-LoRa +architectures=* +includes=LoRa.h diff --git a/libraries/arduino-LoRa-master/src/LoRa.cpp b/libraries/arduino-LoRa-master/src/LoRa.cpp new file mode 100644 index 0000000..210a589 --- /dev/null +++ b/libraries/arduino-LoRa-master/src/LoRa.cpp @@ -0,0 +1,754 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +// registers +#define REG_FIFO 0x00 +#define REG_OP_MODE 0x01 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define REG_OCP 0x0b +#define REG_LNA 0x0c +#define REG_FIFO_ADDR_PTR 0x0d +#define REG_FIFO_TX_BASE_ADDR 0x0e +#define REG_FIFO_RX_BASE_ADDR 0x0f +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_RX_NB_BYTES 0x13 +#define REG_PKT_SNR_VALUE 0x19 +#define REG_PKT_RSSI_VALUE 0x1a +#define REG_RSSI_VALUE 0x1b +#define REG_MODEM_CONFIG_1 0x1d +#define REG_MODEM_CONFIG_2 0x1e +#define REG_PREAMBLE_MSB 0x20 +#define REG_PREAMBLE_LSB 0x21 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_MODEM_CONFIG_3 0x26 +#define REG_FREQ_ERROR_MSB 0x28 +#define REG_FREQ_ERROR_MID 0x29 +#define REG_FREQ_ERROR_LSB 0x2a +#define REG_RSSI_WIDEBAND 0x2c +#define REG_DETECTION_OPTIMIZE 0x31 +#define REG_INVERTIQ 0x33 +#define REG_DETECTION_THRESHOLD 0x37 +#define REG_SYNC_WORD 0x39 +#define REG_INVERTIQ2 0x3b +#define REG_DIO_MAPPING_1 0x40 +#define REG_VERSION 0x42 +#define REG_PA_DAC 0x4d + +// modes +#define MODE_LONG_RANGE_MODE 0x80 +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x01 +#define MODE_TX 0x03 +#define MODE_RX_CONTINUOUS 0x05 +#define MODE_RX_SINGLE 0x06 + +// PA config +#define PA_BOOST 0x80 + +// IRQ masks +#define IRQ_TX_DONE_MASK 0x08 +#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 +#define IRQ_RX_DONE_MASK 0x40 + +#define RF_MID_BAND_THRESHOLD 525E6 +#define RSSI_OFFSET_HF_PORT 157 +#define RSSI_OFFSET_LF_PORT 164 + +#define MAX_PKT_LENGTH 255 + +#if (ESP8266 || ESP32) + #define ISR_PREFIX ICACHE_RAM_ATTR +#else + #define ISR_PREFIX +#endif + +LoRaClass::LoRaClass() : + _spiSettings(LORA_DEFAULT_SPI_FREQUENCY, MSBFIRST, SPI_MODE0), + _spi(&LORA_DEFAULT_SPI), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), + _frequency(0), + _packetIndex(0), + _implicitHeaderMode(0), + _onReceive(NULL), + _onTxDone(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +int LoRaClass::begin(long frequency) +{ +#if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) + pinMode(LORA_IRQ_DUMB, OUTPUT); + digitalWrite(LORA_IRQ_DUMB, LOW); + + // Hardware reset + pinMode(LORA_BOOT0, OUTPUT); + digitalWrite(LORA_BOOT0, LOW); + + pinMode(LORA_RESET, OUTPUT); + digitalWrite(LORA_RESET, HIGH); + delay(200); + digitalWrite(LORA_RESET, LOW); + delay(200); + digitalWrite(LORA_RESET, HIGH); + delay(50); +#endif + + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + if (_reset != -1) { + pinMode(_reset, OUTPUT); + + // perform reset + digitalWrite(_reset, LOW); + delay(10); + digitalWrite(_reset, HIGH); + delay(10); + } + + // start SPI + _spi->begin(); + + // check version + uint8_t version = readRegister(REG_VERSION); + if (version != 0x12) { + return 0; + } + + // put in sleep mode + sleep(); + + // set frequency + setFrequency(frequency); + + // set base addresses + writeRegister(REG_FIFO_TX_BASE_ADDR, 0); + writeRegister(REG_FIFO_RX_BASE_ADDR, 0); + + // set LNA boost + writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); + + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + + // set output power to 17 dBm + setTxPower(17); + + // put in standby mode + idle(); + + return 1; +} + +void LoRaClass::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + _spi->end(); +} + +int LoRaClass::beginPacket(int implicitHeader) +{ + if (isTransmitting()) { + return 0; + } + + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + // reset FIFO address and paload length + writeRegister(REG_FIFO_ADDR_PTR, 0); + writeRegister(REG_PAYLOAD_LENGTH, 0); + + return 1; +} + +int LoRaClass::endPacket(bool async) +{ + + if ((async) && (_onTxDone)) + writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE + + // put in TX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); + + if (!async) { + // wait for TX done + while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { + yield(); + } + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return 1; +} + +bool LoRaClass::isTransmitting() +{ + if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) { + return true; + } + + if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) { + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return false; +} + +int LoRaClass::parsePacket(int size) +{ + int packetLength = 0; + int irqFlags = readRegister(REG_IRQ_FLAGS); + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + + // read packet length + if (_implicitHeaderMode) { + packetLength = readRegister(REG_PAYLOAD_LENGTH); + } else { + packetLength = readRegister(REG_RX_NB_BYTES); + } + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + // put in standby mode + idle(); + } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { + // not currently in RX mode + + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + + // put in single RX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); + } + + return packetLength; +} + +int LoRaClass::packetRssi() +{ + return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT : RSSI_OFFSET_HF_PORT)); +} + +float LoRaClass::packetSnr() +{ + return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; +} + +long LoRaClass::packetFrequencyError() +{ + int32_t freqError = 0; + freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); + + if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on + freqError -= 524288; // B1000'0000'0000'0000'0000 + } + + const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) + const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 + + return static_cast(fError); +} + +int LoRaClass::rssi() +{ + return (readRegister(REG_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT : RSSI_OFFSET_HF_PORT)); +} + +size_t LoRaClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t LoRaClass::write(const uint8_t *buffer, size_t size) +{ + int currentLength = readRegister(REG_PAYLOAD_LENGTH); + + // check size + if ((currentLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - currentLength; + } + + // write data + for (size_t i = 0; i < size; i++) { + writeRegister(REG_FIFO, buffer[i]); + } + + // update length + writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); + + return size; +} + +int LoRaClass::available() +{ + return (readRegister(REG_RX_NB_BYTES) - _packetIndex); +} + +int LoRaClass::read() +{ + if (!available()) { + return -1; + } + + _packetIndex++; + + return readRegister(REG_FIFO); +} + +int LoRaClass::peek() +{ + if (!available()) { + return -1; + } + + // store current FIFO address + int currentAddress = readRegister(REG_FIFO_ADDR_PTR); + + // read + uint8_t b = readRegister(REG_FIFO); + + // restore FIFO address + writeRegister(REG_FIFO_ADDR_PTR, currentAddress); + + return b; +} + +void LoRaClass::flush() +{ +} + +#ifndef ARDUINO_SAMD_MKRWAN1300 +void LoRaClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::onTxDone(void(*callback)()) +{ + _onTxDone = callback; + + if (callback) { + pinMode(_dio0, INPUT); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::receive(int size) +{ + + writeRegister(REG_DIO_MAPPING_1, 0x00); // DIO0 => RXDONE + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); +} +#endif + +void LoRaClass::idle() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); +} + +void LoRaClass::sleep() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); +} + +void LoRaClass::setTxPower(int level, int outputPin) +{ + if (PA_OUTPUT_RFO_PIN == outputPin) { + // RFO + if (level < 0) { + level = 0; + } else if (level > 14) { + level = 14; + } + + writeRegister(REG_PA_CONFIG, 0x70 | level); + } else { + // PA BOOST + if (level > 17) { + if (level > 20) { + level = 20; + } + + // subtract 3 from level, so 18 - 20 maps to 15 - 17 + level -= 3; + + // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) + writeRegister(REG_PA_DAC, 0x87); + setOCP(140); + } else { + if (level < 2) { + level = 2; + } + //Default value PA_HF/LF or +17dBm + writeRegister(REG_PA_DAC, 0x84); + setOCP(100); + } + + writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); + } +} + +void LoRaClass::setFrequency(long frequency) +{ + _frequency = frequency; + + uint64_t frf = ((uint64_t)frequency << 19) / 32000000; + + writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); + writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); + writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); +} + +int LoRaClass::getSpreadingFactor() +{ + return readRegister(REG_MODEM_CONFIG_2) >> 4; +} + +void LoRaClass::setSpreadingFactor(int sf) +{ + if (sf < 6) { + sf = 6; + } else if (sf > 12) { + sf = 12; + } + + if (sf == 6) { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); + writeRegister(REG_DETECTION_THRESHOLD, 0x0c); + } else { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); + writeRegister(REG_DETECTION_THRESHOLD, 0x0a); + } + + writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); + setLdoFlag(); +} + +long LoRaClass::getSignalBandwidth() +{ + byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); + + switch (bw) { + case 0: return 7.8E3; + case 1: return 10.4E3; + case 2: return 15.6E3; + case 3: return 20.8E3; + case 4: return 31.25E3; + case 5: return 41.7E3; + case 6: return 62.5E3; + case 7: return 125E3; + case 8: return 250E3; + case 9: return 500E3; + } + + return -1; +} + +void LoRaClass::setSignalBandwidth(long sbw) +{ + int bw; + + if (sbw <= 7.8E3) { + bw = 0; + } else if (sbw <= 10.4E3) { + bw = 1; + } else if (sbw <= 15.6E3) { + bw = 2; + } else if (sbw <= 20.8E3) { + bw = 3; + } else if (sbw <= 31.25E3) { + bw = 4; + } else if (sbw <= 41.7E3) { + bw = 5; + } else if (sbw <= 62.5E3) { + bw = 6; + } else if (sbw <= 125E3) { + bw = 7; + } else if (sbw <= 250E3) { + bw = 8; + } else /*if (sbw <= 250E3)*/ { + bw = 9; + } + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); + setLdoFlag(); +} + +void LoRaClass::setLdoFlag() +{ + // Section 4.1.1.5 + long symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ; + + // Section 4.1.1.6 + boolean ldoOn = symbolDuration > 16; + + uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); + bitWrite(config3, 3, ldoOn); + writeRegister(REG_MODEM_CONFIG_3, config3); +} + +void LoRaClass::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + int cr = denominator - 4; + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); +} + +void LoRaClass::setPreambleLength(long length) +{ + writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); + writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); +} + +void LoRaClass::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD, sw); +} + +void LoRaClass::enableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); +} + +void LoRaClass::disableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); +} + +void LoRaClass::enableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x66); + writeRegister(REG_INVERTIQ2, 0x19); +} + +void LoRaClass::disableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x27); + writeRegister(REG_INVERTIQ2, 0x1d); +} + +void LoRaClass::setOCP(uint8_t mA) +{ + uint8_t ocpTrim = 27; + + if (mA <= 120) { + ocpTrim = (mA - 45) / 5; + } else if (mA <=240) { + ocpTrim = (mA + 30) / 10; + } + + writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim)); +} + +void LoRaClass::setGain(uint8_t gain) +{ + // check allowed range + if (gain > 6) { + gain = 6; + } + + // set to standby + idle(); + + // set gain + if (gain == 0) { + // if gain = 0, enable AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + } else { + // disable AGC + writeRegister(REG_MODEM_CONFIG_3, 0x00); + + // clear Gain and set LNA boost + writeRegister(REG_LNA, 0x03); + + // set gain + writeRegister(REG_LNA, readRegister(REG_LNA) | (gain << 5)); + } +} + +byte LoRaClass::random() +{ + return readRegister(REG_RSSI_WIDEBAND); +} + +void LoRaClass::setPins(int ss, int reset, int dio0) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; +} + +void LoRaClass::setSPI(SPIClass& spi) +{ + _spi = &spi; +} + +void LoRaClass::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void LoRaClass::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 128; i++) { + out.print("0x"); + out.print(i, HEX); + out.print(": 0x"); + out.println(readRegister(i), HEX); + } +} + +void LoRaClass::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); +} + +void LoRaClass::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); +} + +void LoRaClass::handleDio0Rise() +{ + int irqFlags = readRegister(REG_IRQ_FLAGS); + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + + if ((irqFlags & IRQ_RX_DONE_MASK) != 0) { + // received a packet + _packetIndex = 0; + + // read packet length + int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + if (_onReceive) { + _onReceive(packetLength); + } + } + else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) { + if (_onTxDone) { + _onTxDone(); + } + } + } +} + +uint8_t LoRaClass::readRegister(uint8_t address) +{ + return singleTransfer(address & 0x7f, 0x00); +} + +void LoRaClass::writeRegister(uint8_t address, uint8_t value) +{ + singleTransfer(address | 0x80, value); +} + +uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) +{ + uint8_t response; + + digitalWrite(_ss, LOW); + + _spi->beginTransaction(_spiSettings); + _spi->transfer(address); + response = _spi->transfer(value); + _spi->endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} + +ISR_PREFIX void LoRaClass::onDio0Rise() +{ + LoRa.handleDio0Rise(); +} + +LoRaClass LoRa; diff --git a/libraries/arduino-LoRa-master/src/LoRa.h b/libraries/arduino-LoRa-master/src/LoRa.h new file mode 100644 index 0000000..b312db5 --- /dev/null +++ b/libraries/arduino-LoRa-master/src/LoRa.h @@ -0,0 +1,130 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LORA_H +#define LORA_H + +#include +#include + +#if defined(ARDUINO_SAMD_MKRWAN1300) +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 200000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN -1 +#elif defined(ARDUINO_SAMD_MKRWAN1310) +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 200000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN LORA_IRQ +#else +#define LORA_DEFAULT_SPI SPI +#define LORA_DEFAULT_SPI_FREQUENCY 8E6 +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 +#endif + +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +class LoRaClass : public Stream { +public: + LoRaClass(); + + int begin(long frequency); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(bool async = false); + + int parsePacket(int size = 0); + int packetRssi(); + float packetSnr(); + long packetFrequencyError(); + + int rssi(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + +#ifndef ARDUINO_SAMD_MKRWAN1300 + void onReceive(void(*callback)(int)); + void onTxDone(void(*callback)()); + + void receive(int size = 0); +#endif + void idle(); + void sleep(); + + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + void setFrequency(long frequency); + void setSpreadingFactor(int sf); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + void enableCrc(); + void disableCrc(); + void enableInvertIQ(); + void disableInvertIQ(); + + void setOCP(uint8_t mA); // Over Current Protection control + + void setGain(uint8_t gain); // Set LNA gain + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); + void setSPI(SPIClass& spi); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + bool isTransmitting(); + + int getSpreadingFactor(); + long getSignalBandwidth(); + + void setLdoFlag(); + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + +private: + SPISettings _spiSettings; + SPIClass* _spi; + int _ss; + int _reset; + int _dio0; + long _frequency; + int _packetIndex; + int _implicitHeaderMode; + void (*_onReceive)(int); + void (*_onTxDone)(); +}; + +extern LoRaClass LoRa; + +#endif diff --git a/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/00-support-request.md b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/00-support-request.md new file mode 100644 index 0000000..996b3ac --- /dev/null +++ b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/00-support-request.md @@ -0,0 +1,27 @@ +--- +name: Support request +about: Ask for help on a problem +title: '' +labels: question +assignees: terrillmoore + +--- + +**Consider raising support questions on the forum first** + +There is a discussion site, [forum.mcci.io](https://forum.mcci.io), which includes a [category focused on the Arduino LMIC](https://forum.mcci.io/c/device-software/arduino-lmic/5). Unless you're sure that your problem is a bug in the LMIC, it would be great if you can try for help there first. That will keep issues focused on work that needs to be done by the developers. + +**Describe your question or issue** + +Please give a clear and concise description of the problem you're facing and what you'd like help with. + +**Environment** + +This information is very important; it's hard to help without a complete set of answers. + +- Version of LMIC being used. If using the latest github mainline, let us know, otherwise state the version. +- Version of Arduino IDE being used: +- Network provider (The Things Network, Swisscom, ChirpStack, etc.) +- Region (EU868, US915, etc.) +- Board (MCCI Catena, Adafruit Feather M0, Heltec Wi-Fi LoRa 32 v2, etc.) +- Radio (HopeRF, SX1276, etc.) diff --git a/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/10-bug_report.md b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/10-bug_report.md new file mode 100644 index 0000000..320064f --- /dev/null +++ b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/10-bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Report a code defect +title: '' +labels: bug +assignees: terrillmoore + +--- + +**Describe the bug** + +A clear and concise description of what the bug is. _If you're not sure it's a bug, please consider opening a discussion at [forum.mcci.io](https://forum.mcci.io/c/device-software/arduino-lmic/5), or file a Support Request instead._ A bug is a problem in the code, where you do something that the documentation says should work, but doesn't; _and_ you have reason to believe it's not working due to a defect in the code. + +Wireless is tricky; if in doubt, start with a support request, rather than a bug report. + +**Environment** + +- Version of LMIC being used. If using the latest GitHub mainline, let us know, otherwise state the version. +- Version of Arduino IDE being used. +- Network provider (The Things Network, Swisscom, ChirpStack, etc.) +- Region (EU868, US915, etc.) +- Board (MCCI Catena, Adafruit Feather M0, Heltec, etc.) +- CPU (AVR, STM32L0, etc.) +- Radio (HopeRF, SX1276, etc.) + +**To Reproduce** + +Steps to reproduce the problem. + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Screenshots** + +If applicable, add screenshots to help explain your problem. + +**Additional context** + +Add any other context about the problem here. diff --git a/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/20-feature_request.md b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/20-feature_request.md new file mode 100644 index 0000000..eaff98f --- /dev/null +++ b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/20-feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: terrillmoore + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/30-doc_issue.md b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/30-doc_issue.md new file mode 100644 index 0000000..44573f2 --- /dev/null +++ b/libraries/arduino-lmic-master/.github/ISSUE_TEMPLATE/30-doc_issue.md @@ -0,0 +1,22 @@ +--- +name: Documentation issue +about: Report a problem in the documentation +title: '' +labels: docs +assignees: terrillmoore + +--- + +Thanks for your contribution! + +**What document is involved?** + +If this is an existing document, please include the full path to the file. + +If there's a need for a new document, please suggest the places in existing docs that should link to it. + +Make sure you mention the version, and please also check the current version at GitHub, to be sure that the problem hasn't already been corrected for a pending release. + +**What changes are needed?** + +Indicate the changes you think are needed. Explicit help is always better. File a PR with your changes if possible! diff --git a/libraries/arduino-lmic-master/.gitignore b/libraries/arduino-lmic-master/.gitignore new file mode 100644 index 0000000..34b5350 --- /dev/null +++ b/libraries/arduino-lmic-master/.gitignore @@ -0,0 +1,48 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Backup files +*.BAK +*.CKP +*.wbk +~$*.docx + +# files from Visual Micro +Release +*.vcxproj +*.vcxproj.filters +*.vcxitems +vs-readme.txt +__vm +.vs +*.sln + +# files from vscode +.vscode diff --git a/libraries/arduino-lmic-master/.hgignore b/libraries/arduino-lmic-master/.hgignore new file mode 100644 index 0000000..d164264 --- /dev/null +++ b/libraries/arduino-lmic-master/.hgignore @@ -0,0 +1,17 @@ +^CVS +.*/CVS +.*/CVS/.* +\.\#.*$ +^\.DS_Store$ +.*\.BAK$ +.*\.bak$ +.*\.CKP$ +^build +^build/.* +^.*\.o$ +^.*\.d$ +^.*\.td$ +\.a$ +^core$ +.*/core$ +.*\.rej$ diff --git a/libraries/arduino-lmic-master/.travis.yml b/libraries/arduino-lmic-master/.travis.yml new file mode 100644 index 0000000..09274a7 --- /dev/null +++ b/libraries/arduino-lmic-master/.travis.yml @@ -0,0 +1,342 @@ +############################################################################## +# +# File: .travis.yml +# +# Function: +# Travis script for test-building this library. +# +# Copyright Notice: +# See LICENSE file accompanying this project. +# +# Author: +# Terry Moore, MCCI Corporation April 2018 +# +############################################################################## + +language: c +dist: bionic +sudo: true + +env: + global: + - IDE_VERSION=1.8.12 + matrix: + - TARGET=samd + - TARGET=stm32l0 + - TARGET=esp32 + - TARGET=avr + +cache: + directories: + - $HOME/.arduino15 + - $HOME/arduino_ide + +before_install: + # + # PlatformIO setup + - uname --all + - sudo apt-get update -qq && sudo apt-get -y install python3-pip + - python3 --version + - pip3 install --user --upgrade pip + - pip3 --version + - pip3 install --user --upgrade setuptools + - pip3 install --user --upgrade platformio + - platformio update + - platformio --version + # + # Arduino IDE setup + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" + - sleep 3 + - export DISPLAY=:1.0 + - export ARDUINO_IDE=arduino_ide/${IDE_VERSION} + - export ARDUINO_LIBS="$HOME/Arduino/libraries" + - if [ ! -d "$HOME/arduino_ide" ] ; then mkdir "$HOME/arduino_ide" ; fi + - if [ ! -d "$HOME/$ARDUINO_IDE" ] ; then { echo "fetch" && wget https://downloads.arduino.cc/arduino-${IDE_VERSION}-linux64.tar.xz && echo "untar" && tar xf arduino-${IDE_VERSION}-linux64.tar.xz && echo "move" && mv -f arduino-${IDE_VERSION} $HOME/${ARDUINO_IDE} ; } || { echo "IDE install failed"; exit 1; } ; else echo "IDE already installed" ; fi + - ln -sf $PWD $HOME/${ARDUINO_IDE}/libraries/Test_Library + - export PATH="$HOME/${ARDUINO_IDE}:$PATH" + + # Arduino IDE adds a lot of noise caused by network traffic, trying to firewall it off + # (see https://github.com/per1234/arduino-ci-script/issues/1#issuecomment-504158113) + - sudo iptables -P INPUT DROP + - sudo iptables -P FORWARD DROP + - sudo iptables -P OUTPUT ACCEPT + - sudo iptables -A INPUT -i lo -j ACCEPT + - sudo iptables -A OUTPUT -o lo -j ACCEPT + - sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + + # + # functions to generate the board settings for SAMD, STM32L0, ... + # keep args for these aligned for any common options. $1 is always board name, $2 is region. + # + # Changes to the BSP may break this build, sorry! + # + - "function _samdopts { echo mcci:samd:${1:-mcci_catena_4450}:lorawan_region=${2:-us915} ; }" + - "function _stm32l0opts { echo mcci:stm32:${1:-mcci_catena_4551}:lorawan_region=${2:-us915},opt=${3:-osstd},xserial=${4:-generic},upload_method=${6:-STLink},sysclk=${7:-pll32m} ; }" + - "function _avropts { echo adafruit:avr:${1:-feather32u4} ; }" + - "function _esp32opts { echo esp32:esp32:${1:-heltec_wifi_lora_32}:FlashFreq=80 ; }" + # + # Select based on target + # + - function _notsamd { test "$TARGET" != "samd"; } + - function _notstm32l0 { test "$TARGET" != "stm32l0"; } + - function _notavr { test "$TARGET" != "avr"; } + - function _notesp32 { test "$TARGET" != "esp32"; } + - function _notany { for i in "$@" ; do if [ "$TARGET" = "$i" ]; then return 1 ; fi ; done; return 0; } + # + # Put one or more arguments into lmic_project_config.h as `#define $i 1\n` or `#define $i $arg` + - function _splitdef { if [ "$1" = "${1/=/}" ]; then echo "$1" 1 ; else echo "${1/=/ }" ; fi ; } + - function _projcfg { for i in "$@" ; do printf '#define %s %s\n' $(_splitdef "$i") ; done > $PWD/project_config/lmic_project_config.h ; } + - function _projcfg_class_a { for i in "$@" "DISABLE_PING" "DISABLE_BEACONS"; do printf '#define %s %s\n' $(_splitdef "$i") ; done > $PWD/project_config/lmic_project_config.h ; } + # + # Handy macro to deal with expected failures. + - 'function _expect_failure { if [ $? -eq 0 ]; then echo "Suceeded, but should have failed!" ; echo project_config/lmic_project_config.h ; cat $PWD/project_config/lmic_project_config.h ; return 1 ; else echo "Failed, as expected"; return 0 ; fi ; }' + # + # modify the board manager preferences to point to our BSPs. + - BSPMCCI=https://github.com/mcci-catena/arduino-boards/raw/master/BoardManagerFiles/package_mcci_index.json + - BSPADAFRUIT=https://adafruit.github.io/arduino-board-index/package_adafruit_index.json + - BSPESP32=https://dl.espressif.com/dl/package_esp32_index.json + - arduino --pref "boardsmanager.additional.urls=$BSPMCCI,$BSPADAFRUIT,$BSPESP32" --save-prefs + # + # Centralize the STM32 config override. See the following: + # https://github.com/mcci-catena/Arduino_Core_STM32/issues/30 + # https://github.com/mcci-catena/arduino-lorawan/issues/45 + - export MCCI_STM32_OPTS="--pref recipe.hooks.objcopy.postobjcopy.1.pattern=true" + + # + # show the output of the config commands for reference. + - "echo $(_stm32l0opts) $(_stm32l0opts '' projcfg)" + - "echo $(_samdopts) $(_samdopts '' projcfg)" + - "echo $(_avropts) $(_avropts '' PROJCFG)" + - "echo $(_esp32opts) $(_esp32opts '' PROJCFG)" + +install: + - git clone --depth=1 https://github.com/adafruit/Adafruit_Sensor.git $ARDUINO_LIBS/Adafruit_Sensor + - git clone --depth=1 https://github.com/adafruit/DHT-sensor-library.git $ARDUINO_LIBS/DHT-sensor-library + - git clone --depth=1 --branch v1.5 https://github.com/PaulStoffregen/Time.git $ARDUINO_LIBS/Time + # be careful when installing; with caching, we usually won't update. Select most recent. + - _notavr || arduino --install-boards adafruit:avr || echo "assume adafruit:avr already installed, continue" + - _notsamd || arduino --install-boards mcci:samd || echo "assume mcci:samd already installed, continue" + - _notstm32l0 || arduino --install-boards mcci:stm32 || echo "assume mcci:samd already installed, continue" + - _notesp32 || arduino --install-boards esp32:esp32 || echo "assume esp32:esp32 already installed, condtinue" + - 'if [ "$TARGET" = stm32l0 -a -d $HOME/.arduino15/packages/mcci/hardware/stm32/1.1.0 ]; then echo "Work around broken BSP version 1.1.0" ; export MCCI_STM32_OPTS="$MCCI_STM32_OPTS --pref build.board=CATENA_4551" ; fi' + +script: + + # Run PlatformIO tests + - ci/platformio.sh + + # Run Arduino IDE tests + + # simple header file test + - _notsamd || arduino --verify --board $(_samdopts) $PWD/examples/header_test/header_test.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts) $MCCI_STM32_OPTS $PWD/examples/header_test/header_test.ino + +# +# *** Tests for ESP32 +# + - _notesp32 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notesp32 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/ttn-abp/ttn-abp.ino ; } + # make sure debug works + - _notesp32 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio LMIC_DEBUG_LEVEL=2 LMIC_PRINTF_TO=Serial && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + # make sure the compliance sketch compiles on AVR in all regions. + - _notesp32 || { _projcfg CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_au915 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_as923 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_as923jp CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + + # + # *** Tests for Feather 32u4 + # The Adafruit BSP doesn't support the BSP selection options; so we have to use projcfg + # + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + + # make sure the compliance sketch compiles on AVR in all regions. This also requires class-A only + # unfortunately EU currently is a little too large, so we don't do EU. + - _notavr || { _projcfg_class_a CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + # - _notavr || { _projcfg_class_a CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_au915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_as923 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_as923jp CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + + # test the raw sketch + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw/raw.ino ; } + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw/raw.ino ; } + +# make sure debug prints work + - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio LMIC_DEBUG_LEVEL=2 LMIC_PRINTF_TO=Serial && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + +# +# *** Tests for SAMD +# +# These are the bulk of the tests; they use the MCCI BSP +# +# test raw-feather in each of the regions. + - _notsamd || arduino --verify --board $(_samdopts '' us915) $PWD/examples/raw-feather/raw-feather.ino + - _notsamd || arduino --verify --board $(_samdopts '' eu868) $PWD/examples/raw-feather/raw-feather.ino +# V1.1.0 of the samd bsp doesn't support au915 correctly -- test with projcfg +#- arduino --verify --board $(_samdopts '' au915) $PWD/examples/raw-feather/raw-feather.ino + - _notsamd || { _projcfg CFG_au915 CFG_sx1276_radio && arduino --verify --board $(_samdopts '' projcfg) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notsamd || arduino --verify --board $(_samdopts '' as923) $PWD/examples/raw-feather/raw-feather.ino + - _notsamd || arduino --verify --board $(_samdopts '' as923jp) $PWD/examples/raw-feather/raw-feather.ino +# V2.0.0 of the samd bsp doesn't include kr920 support in menus, use projcfg +# - _notsamd || arduino --verify --board $(_samdopts '' kr920) $PWD/examples/raw-feather/raw-feather.ino + - _notsamd || { _projcfg CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_samdopts '' projcfg) $PWD/examples/raw-feather/raw-feather.ino ; } + - _notsamd || arduino --verify --board $(_samdopts '' in866) $PWD/examples/raw-feather/raw-feather.ino + +# test raw in us915 and eu868 + - _notsamd || arduino --verify --board $(_samdopts '' us915) $PWD/examples/raw/raw.ino + - _notsamd || arduino --verify --board $(_samdopts '' eu868) $PWD/examples/raw/raw.ino + +# +# some tests using the projcfg file that should pass - test the examples +# +# test ttn-otaa-feather-us915 in all relevant regions with sx1276 + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + +# +# test ttn-otaa-feather-us915 with interrupts + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio LMIC_USE_INTERRUPTS && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + +# +# check that debug prints work + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio LMIC_DEBUG_LEVEL=1 LMIC_PRINTF_TO=Serial && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio LMIC_DEBUG_LEVEL=2 LMIC_PRINTF_TO=Serial && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + +# +# test ttn-otaa with all regions + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } +# +# test ttn-abp with all regions + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } + +# test ttn-otaa-feather-us915-dht22 in all relevant regions with sx1276 + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; } + +# test ttn-abp-feather-us915-dht22 in all relevant regions with sx1276 + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } +#- _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino ; } + +# test ttn-otaa-network-time in all regions + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-network-time/ttn-otaa-network-time.ino ; } + + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4410' us915 ) $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4420' us915 ) $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4450' us915 ) $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4460' us915 ) $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4470' us915 ) $PWD/examples/raw-halconfig/raw-halconfig.ino + + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4410' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4420' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4450' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4460' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4470' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + + # make sure the compliance sketch compiles on SAMD in EU region. + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4450' eu868 ) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino + +# +# some tests that should generate build failures. +# +# COMPILE_REGRESSION_TEST must be defined for ttn-otaa-feather-us915, ttn-otaa, ttn-abp, etc. + - _notsamd || { _projcfg CFG_us915 CFG_sx1272_radio && { arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; _expect_failure; } ; } + - _notsamd || { _projcfg CFG_us915 CFG_sx1276_radio && { arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; _expect_failure; } ; } + - _notsamd || { _projcfg CFG_us915 CFG_sx1276_radio && { arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; _expect_failure; } ; } + - _notsamd || { _projcfg CFG_us915 CFG_sx1272_radio && { arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; _expect_failure; } ; } + - _notsamd || { _projcfg CFG_us915 CFG_sx1272_radio && { arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino ; _expect_failure; } ; } +# +# Only one radio may be defined + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1272_radio CFG_sx1276_radio && { arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; _expect_failure; } ; } + +# +# *** TESTS FOR STM32L0 / Catena 4551 **** +# + - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' eu868 ) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' au915 ) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' as923 ) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' as923jp) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino +# V2.4.0 of the stm bsp doesn't include kr920 support in menus, use projcfg +# - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' kr920 ) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino + - _notstm32l0 || { _projcfg CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts '' projcfg) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino ; } + - _notstm32l0 || arduino --verify --board $(_stm32l0opts '' in866 ) $MCCI_STM32_OPTS $PWD/examples/raw-feather/raw-feather.ino + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts '' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts '' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts '' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-abp/ttn-abp.ino ; } + +# +# *** test all boards *** + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4610' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4611' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4612' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4617' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4618' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4630' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4801' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + - _notstm32l0 || arduino --verify --board $(_stm32l0opts 'mcci_catena_4802' us915 ) $MCCI_STM32_OPTS $PWD/examples/raw-halconfig/raw-halconfig.ino + + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4551' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4610' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4611' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4612' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4617' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4618' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4630' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4801' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + - _notstm32l0 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_stm32l0opts 'mcci_catena_4802' projcfg) $MCCI_STM32_OPTS $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + + + ### end of file ### diff --git a/libraries/arduino-lmic-master/HOWTO-ADD-REGION.md b/libraries/arduino-lmic-master/HOWTO-ADD-REGION.md new file mode 100644 index 0000000..97327a0 --- /dev/null +++ b/libraries/arduino-lmic-master/HOWTO-ADD-REGION.md @@ -0,0 +1,135 @@ +# Adding a new region to Arduino LMIC + +This variant of the Arduino LMIC code supports adding additional regions beyond the eu868 and us915 bands supported by the original IBM LMIC 1.6 code. + +This document outlines how to add a new region. + + + + + + + +- [Planning](#planning) + - [Determine the region/region category](#determine-the-regionregion-category) + - [Check whether the region is already listed in `lmic_config_preconditions.h`](#check-whether-the-region-is-already-listed-in-lmic_config_preconditionsh) +- [Make the appropriate changes in `lmic_config_preconditions.h`](#make-the-appropriate-changes-in-lmic_config_preconditionsh) +- [Document your region in `README.md`](#document-your-region-in-readmemd) +- [Add the definitions for your region in `lorabase.h`](#add-the-definitions-for-your-region-in-lorabaseh) +- [Edit `lmic_bandplan.h`](#edit-lmic_bandplanh) +- [Create lmic_newregion.c](#create-codelmic_emnewregionemccode) +- [General Discussion](#general-discussion) +- [Adding the region to the Arduino_LoRaWAN library](#adding-the-region-to-the-arduino_lorawan-library) + + + + + +## Planning + +### Determine the region/region category + +Compare the target region (in the LoRaWAN regional specification) to the EU868 and US915 regions. There are three possibilities. + +1. The region is like the EU region. There are a limited number of channels (up to 8), and only a small number of channels are used for OTAA join operations. The response masks refer to individual channels, and the JOIN-response can send frequencies of specific channels to be added. + +2. The region is like the US region. There are many channels (the US has 64) with fixed frequencies, and the channel masks refer to subsets of the fixed channels. + +3. The region is not really like either the EU or US. At the moment, it seems that CN470-510MHz (section 2.6 of LoRaWAN Regional Parameters spec V1.0.2rB) falls into this category. + +Band plans in categories (1) and (2) are easily supported. Band plans in category (3) are not supported by the current code. + +### Check whether the region is already listed in `lmic_config_preconditions.h` + +Check `src/lmic/lmic_config_preconditions.h` and scan the `LMIC_REGION_...` definitions. The numeric values are assigned based on the subchapter in section 2 of the LoRaWAN 1.0.2 Regional Parameters document. If your symbol is already there, then the first part of adaptation has already been done. There will already be a corresponding `CFG_...` symbol. But if your region isn't supported, you'll need to add it here. + +- `LMIC_REGION_myregion` must be a distinct integer, and must be less than 32 (so as to fit into a bitmask) + +## Make the appropriate changes in `lmic_config_preconditions.h` + +- `LMIC_REGION_SUPPORTED` is a bit mask of all regions supported by the code. Your new region must appear in this list. +- `CFG_LMIC_REGION_MASK` is a bit mask that, when expanded, returns a bitmask for each defined `CFG_...` variable. You must add your `CFG_myregion` symbol to this list. +- `CFG_region` evaluates to the `LMIC_REGION_...` value for the selected region (as long as only one region is selected). The header files check for this, so you don't have to. +- `CFG_LMIC_EU_like_MASK` is a bitmask of regions that are EU-like, and `CFG_LMIC_US_like_MASK` is a bitmask of regions that are US-like. Add your region to the appropriate one of these two variables. + +## Document your region in `README.md` + +You'll see where the regions are listed. Add yours. + +## Add the definitions for your region in `lorabase.h` + +- If your region is EU like, copy the EU block. Document any duty-cycle limitations. +- if your region is US like, copy the US block. +- As appropriate, copy `lorabase_eu868.h` or `lorabase_us915.h` to make your own lorabase_newregion.h. Fill in the symbols. + +At time of writing, you need to duplicate some code to copy some settings from `lorabase_eu868.h` or `lorabase_us915.h` to the new file; and you need to put some region-specific knowledge into the `lorabase.h` header file. The long-term direction is to put all the regional knowledge into the region-specific header, and then the central code will just copy. The architectural impulse is that we'll want to be able to reuse the regional header files in other contexts. On the other hand, because it's error prone, we don't want to `#include` files that aren't being used; otherwise you could accidentally use EU parameters in US code, etc. + +- Now's a good time to test-compile and clean out errors introduced. Make sure you set the region to your new target region. You'll have problems compiling, but they should look like this: + + ```console + lmic.c:29: In file included from + + lmic_bandplan.h: 52:3: error: #error "LMICbandplan_maxFrameLen() not defined by bandplan" + # error "LMICbandplan_maxFrameLen() not defined by bandplan" + + lmic_bandplan.h: 56:3: error: #error "pow2dBm() not defined by bandplan" + # error "pow2dBm() not defined by bandplan" + ``` + +- If using an MCCI BSP, you might want to edit your local copy of `boards.txt` to add the new region. + +- If using an MCCI BSP, you should definitely edit the template files to add the new region to the list. + +- Modify the `.travis.yml` file to test the new region. + +## Edit `lmic_bandplan.h` + +The next step is to add the region-specific interfaces for your region. + +Do this by editing `lmic_bandplan.h` and adding the appropriate call to a (new) region-specific file `lmic_bandplan_myregion.h`, where "myregion" is the abbreviation for your region. + +Then, if your region is eu868-like, copy `lmic_bandplan_eu868.h` to create your new region-specific header file; otherwise copy `lmic_bandplan_us915.h`. + +Edit the file. + +Try to compile again; you should now get link errors related to your new band-plan, like this: + +```console +c:\tmp\buildfolder\libraries\arduino-lmic\lmic\lmic.c.o: In function `lowerDR': + +C:\Users\tmm\Documents\Arduino\libraries\arduino-lmic\src\lmic/lorabase.h:667: undefined reference to `constant_table__DR2RPS_CRC' +``` + +## Create lmic_newregion.c + +Once again, you will start by copying either `lmic_eu868.c` or `lmic_us915.c` to create your new file. Then touch it up as necessary. + +## General Discussion + +- You'll find it easier to do the test compiles using the example scripts in this directory, rather than trying to get all the Catena framework going too. On the other hand, working with the Catena framework will expose more problems. + +- Don't forget to check and update the examples. + +- You will also need to update the `boards.template` file for MCCI BSPs, in order to get the region to show up in the Arduino IDE menu. + +- You will need to update the [`arduino-lorawan`](https://github.com/mcci-catena/arduino-lorawan) library to include support for the new region. (See [below](#adding-the-region-to-the-arduino_lorawan-library) for instructions.) + +- Please increase the version of `arduino-lmic` (symbol `ARDUINO_LMIC_VERSION` in `src/lmic/lmic.h`), and change `arduino-lorawan`'s `Arduino_LoRaWAN_lmic.h` to check for at least that newer version. + +- Please also increase the version of the `arduino-lorawan` library (symbol `ARDUINO_LORAWAN_VERSION`). + +## Adding the region to the Arduino_LoRaWAN library + +In `Arduino_LoRaWAN_ttn.h`: + +- Add a new class with name `Arduino_LoRaWAN_ttn_myregion`, copied either from the `Arduino_LoRaWAN_ttn_eu868` class or the `Arduino_LoRaWAN_ttn_us915` class. +- Extend the list of `#if defined(CFG_eu868)` etc. to define `Arduino_LoRaWAN_REGION_TAG` to the suffix of your new class if `CFG_myregion` is defined. + +Then copy and edit either `ttn_eu868_netbegin.cpp`/`ttn_eu868_netjoin.cpp` or `ttn_us915_netbegin.cpp`/`ttn_us915_netjoin.cpp` to make your own file(s) for the key functions. diff --git a/libraries/arduino-lmic-master/LICENSE b/libraries/arduino-lmic-master/LICENSE new file mode 100644 index 0000000..676b891 --- /dev/null +++ b/libraries/arduino-lmic-master/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (C) 2014-2016 IBM Corporation +Copyright (c) 2016-2019 MCCI Corporation +Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + +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. diff --git a/libraries/arduino-lmic-master/README.md b/libraries/arduino-lmic-master/README.md new file mode 100644 index 0000000..4687d07 --- /dev/null +++ b/libraries/arduino-lmic-master/README.md @@ -0,0 +1,1354 @@ +# Arduino-LMIC library + +This repository contains the IBM LMIC (LoRaWAN-MAC-in-C) library, slightly +modified to run in the Arduino environment, allowing using the SX1272, +SX1276 transceivers and compatible modules (such as some HopeRF RFM9x +modules and the Murata LoRa modules). + +Information about the LoRaWAN protocol is summarized in [LoRaWAN-at-a-glance](doc/LoRaWAN-at-a-glance.pdf). Full information is available from the [LoRa Alliance](https://lora-alliance.org). + +A support forum is available at [forum.mcci.io](https://forum.mcci.io/c/device-software/arduino-lmic/5). + +The base Arduino library mostly exposes the functions defined by LMIC. It makes no +attempt to wrap them in a higher level API that is more in the Arduino +style. To find out how to use the library itself, see the examples, or +see the PDF files in the doc subdirectory. + +A separate library, [MCCI `arduino-lorawan`](https://github.com/mcci-catena/arduino-lorawan), provides a higher level, more Arduino-like wrapper which may be useful. + +This library requires Arduino IDE version 1.6.6 or above, since it +requires C99 mode to be enabled by default. + +[![GitHub release](https://img.shields.io/github/release/mcci-catena/arduino-lmic.svg)](https://github.com/mcci-catena/arduino-lmic/releases/latest) [![GitHub commits](https://img.shields.io/github/commits-since/mcci-catena/arduino-lmic/latest.svg)](https://github.com/mcci-catena/arduino-lmic/compare/v3.3.0...master) [![Build Status](https://travis-ci.com/mcci-catena/arduino-lmic.svg?branch=master)](https://travis-ci.com/mcci-catena/arduino-lmic) + +**Contents:** + + + + + + + +- [Installing](#installing) +- [Getting Help](#getting-help) + - [If it's not working](#if-its-not-working) + - [If you've found a bug](#if-youve-found-a-bug) +- [Features](#features) +- [Additional Documentation](#additional-documentation) + - [PDF/Word Documentation](#pdfword-documentation) + - [Adding Regions](#adding-regions) + - [Known bugs and issues](#known-bugs-and-issues) + - [Timing Issues](#timing-issues) + - [Working with MCCI Murata-based boards](#working-with-mcci-murata-based-boards) + - [Event-Handling Issues](#event-handling-issues) +- [Configuration](#configuration) + - [Selecting the LoRaWAN Version](#selecting-the-lorawan-version) + - [Selecting V1.0.2](#selecting-v102) + - [Selecting V1.0.3](#selecting-v103) + - [Selecting the LoRaWAN Region Configuration](#selecting-the-lorawan-region-configuration) + - [eu868, as923, in866, kr920](#eu868-as923-in866-kr920) + - [us915, au915](#us915-au915) + - [Selecting the target radio transceiver](#selecting-the-target-radio-transceiver) + - [Controlling use of interrupts](#controlling-use-of-interrupts) + - [Disabling PING](#disabling-ping) + - [Disabling Beacons](#disabling-beacons) + - [Enabling Network Time Support](#enabling-network-time-support) + - [Rarely changed variables](#rarely-changed-variables) + - [Changing debug output](#changing-debug-output) + - [Getting debug from the RF library](#getting-debug-from-the-rf-library) + - [Selecting the AES library](#selecting-the-aes-library) + - [Defining the OS Tick Frequency](#defining-the-os-tick-frequency) + - [Setting the SPI-bus frequency](#setting-the-spi-bus-frequency) + - [Changing handling of runtime assertion failures](#changing-handling-of-runtime-assertion-failures) + - [Disabling JOIN](#disabling-join) + - [Disabling Class A MAC commands](#disabling-class-a-mac-commands) + - [Disabling Class B MAC commands](#disabling-class-b-mac-commands) + - [Disabling user events](#disabling-user-events) + - [Disabling external reference to `onEvent()`](#disabling-external-reference-to-onevent) + - [Enabling long messages](#enabling-long-messages) + - [Enabling LMIC event logging calls](#enabling-lmic-event-logging-calls) + - [Special purpose](#special-purpose) +- [Supported hardware](#supported-hardware) +- [Pre-Integrated Boards](#pre-integrated-boards) +- [PlatformIO](#platformio) +- [Manual configuration](#manual-configuration) + - [Power](#power) + - [SPI](#spi) + - [DIO pins](#dio-pins) + - [Reset](#reset) + - [RXTX](#rxtx) + - [RXTX Polarity](#rxtx-polarity) + - [Pin mapping](#pin-mapping) + - [Advanced initialization](#advanced-initialization) + - [HalConfiguration_t methods](#halconfiguration_t-methods) + - [LoRa Nexus by Ideetron](#lora-nexus-by-ideetron) +- [Example Sketches](#example-sketches) +- [Timing](#timing) + - [Controlling protocol timing](#controlling-protocol-timing) + - [`LMIC_setClockError()`](#lmic_setclockerror) + - [Interrupts and Arduino system timing](#interrupts-and-arduino-system-timing) +- [Downlink data rate](#downlink-data-rate) +- [Encoding Utilities](#encoding-utilities) + - [sflt16](#sflt16) + - [JavaScript decoder](#javascript-decoder) + - [uflt16](#uflt16) + - [uflt16 JavaScript decoder](#uflt16-javascript-decoder) + - [sflt12](#sflt12) + - [sflt12f JavaScript decoder](#sflt12f-javascript-decoder) + - [uflt12](#uflt12) + - [uflt12f JavaScript decoder](#uflt12f-javascript-decoder) +- [Release History](#release-history) +- [Contributions](#contributions) +- [Trademark Acknowledgements](#trademark-acknowledgements) +- [License](#license) + - [Support Open Source Hardware and Software](#support-open-source-hardware-and-software) + + + + + +## Installing + +To install this library: + +- install it using the Arduino Library manager ("Sketch" -> "Include + Library" -> "Manage Libraries..."), or +- download a zip file from GitHub using the "Download ZIP" button and + install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP + Library..." +- clone this git repository into your sketchbook/libraries folder. + +For more info, see [https://www.arduino.cc/en/Guide/Libraries](https://www.arduino.cc/en/Guide/Libraries). + +## Getting Help + +### If it's not working + +Ask questions at [`forum.mcci.io`](https://forum.mcci.io/c/device-software/arduino-lmic/5). Wireless is tricky, so don't be afraid to ask. The LMIC has been used successfully in a lot of applications, but it's common to have problems getting it working. To keep the code size down, there are not a lot of debugging features, and the features are not always easy to use. + +### If you've found a bug + +Raise a GitHub issue at [`github.com/mcci-catena/arduino-lmic`](https://github.com/mcci-catena/arduino-lmic/issues/). + +## Features + +The LMIC library provides a fairly complete LoRaWAN Class A and Class B +implementation, supporting the EU-868, US-915, AU-921, AS-923, and IN-866 bands. Only a limited +number of features was tested using this port on Arduino hardware, so be careful when using any of the untested features. + +The library has only been tested with LoRaWAN 1.0.2/1.03 networks and does not have the separated key structure defined by LoRaWAN 1.1. + +What certainly works: + +- Sending packets uplink, taking into account duty cycling. +- Encryption and message integrity checking. +- Custom frequencies and data rate settings. +- Over-the-air activation (OTAA / joining). +- Receiving downlink packets in the RX1 and RX2 windows. +- MAC command processing. + +What has not been tested: + +- Class B operation. +- FSK has not been extensively tested. (Testing with the RedwoodComm RWC5020A analyzer in 2019 indicated that FSK downlink is stable but not reliable. This prevents successful completion of LoRaWAN pre-certification in regions that require support for FSK.) + +If you try one of these untested features and it works, be sure to let +us know (creating a GitHub issue is probably the best way for that). + +## Additional Documentation + +### PDF/Word Documentation + +The `doc` directory contains [LMIC-v3.3.0.pdf](doc/LMIC-v3.3.0.pdf), which documents the library APIs and use. It's based on the original IBM documentation, but has been adapted for this version of the library. However, as this library is used for more than Arduino, that document is supplemented by practical details in this document. + +### Adding Regions + +There is a general framework for adding support for a new region. [HOWTO-ADD-REGION.md](./HOWTO-ADD-REGION.md) has step-by-step instructions for adding a region. + +### Known bugs and issues + +See the list of bugs at [`mcci-catena/arduino-lmic`](https://github.com/mcci-catena/arduino-lmic/issues). + +#### Timing Issues + +The LoRaWAN technology for class A devices requires devices to meet hard real-time deadlines. The Arduino environment doesn't provide built-in support for this, and this port of the LMIC doesn't really ensure it, either. It is your responsibility, when constructing your application, to ensure that you call `os_runloop_once()` "often enough". + +How often is often enough? + +It depends on what the LMIC is doing. For Class A devices, when the LMIC is idle, `os_runloop_once()` need not be called at all. However, during a message transmit, it's critical to ensure that `os_runloop_once()` is called frequently prior to hard deadlines. The API `os_queryTimeCriticalJobs()` can be used to check whether there are any deadlines due soon. Before doing work that takes `n` milliseconds, call `os_queryTimeCriticalJobs(ms2osticks(n))`, and skip the work if the API indicates that the LMIC needs attention. + +However, in the current implementation, the LMIC is tracking the completion of uplink transmits. This is done by checking for transmit-complete indications, which is done by polling. So you must also continually call `os_runloop_once()` while waiting for a transmit to be completed. This is an area for future improvement. + +#### Working with MCCI Murata-based boards + +The Board Support Package V2.5.0 for the MCCI Murata-based boards ([MCCI Catena 4610](https://mcci.io/catena4610), [MCCI Catena 4612](https://mcci.io/catena4612), etc.) has a defect in clock calibration that prevents the compliance script from being used without modification. Versions V2.6.0 and later solve this issue. + +#### Event-Handling Issues + +The LMIC has a simple event notification system. When an interesting event occurs, it calls a user-provided function. + +This function is sometimes called at time critical moments. + +This means that your event function should avoid doing any time-critical work. + +Furthermore, in versions of the LMIC prior to v3.0.99.3, the event function may be called in situations where it's not safe to call the general LMIC APIs. In those older LMIC versions, please be careful to defer all work from your event function to your `loop()` function. See the compliance example sketch for an elaborate version of how this can be done. + +## Configuration + +A number of features can be enabled or disabled at compile time. +This is done by adding the desired settings to the file +`project_config/lmic_project_config.h`. The `project_config` +directory is the only directory that contains files that you +should edit to match your project; we organize things this way +so that your local changes are more clearly separated from +the distribution files. The Arduino environment doesn't give +us a better way to do this, unless you change `BOARDS.txt`. + +Unlike other ports of the LMIC code, in this port, you should not edit `src/lmic/config.h` to configure this package. The intention is that you'll edit the `project_config/lmic_project_config.h` (if using the Arduino environment), or change compiler command-line input (if using PlatformIO, make, etc.). + +The following configuration variables are available. + +### Selecting the LoRaWAN Version + +This library implements V1.0.3 of the LoRaWAN specification. However, it can also be used with V1.0.2. The only significant change when selecting V1.0.2 is that the US accepted power range in MAC commands is 10 dBm to 30 dBm; whereas in V1.0.3 the accepted range 2 dBm to 30 dBm. + +The default LoRaWAN version, if no version is explicitly selected, is V1.0.3. + +`LMIC_LORAWAN_SPEC_VERSION` is defined as an integer reflecting the targeted spec version; it will be set to `LMIC_LORAWAN_SPEC_VERSION_1_0_2` or `LMIC_LORAWAN_SPEC_VERSION_1_0_3`. Arithmetic comparisons can be done on these version numbers: and we guarantee `LMIC_LORAWAN_SPEC_VERSION_1_0_3 > LMIC_LORAWAN_SPEC_VERSION_1_0_2`, but the details of the how the versions are encoded may change, and your code should not rely upon the details. + +#### Selecting V1.0.2 + +In `project_config/lmic_project_config.h`, add: + +```c +#define LMIC_LORAWAN_SPEC_VERSION LMIC_LORAWAN_SPEC_VERSION_1_0_2 +``` + +On your compiler command line, add: + +```shell +-D LMIC_LORAWAN_SPEC_VERSION=LMIC_LORAWAN_SPEC_VERSION_1_0_2 +``` + +#### Selecting V1.0.3 + +In `project_config/lmic_project_config.h`, add: + +```c +#define LMIC_LORAWAN_SPEC_VERSION LMIC_LORAWAN_SPEC_VERSION_1_0_3 +``` + +On your compiler command line, add: + +```shell +-D LMIC_LORAWAN_SPEC_VERSION=LMIC_LORAWAN_SPEC_VERSION_1_0_3 +``` + +This is the default. + +### Selecting the LoRaWAN Region Configuration + +The library supports the following regions: + +`-D` variable | CFG region name | CFG region value | LoRaWAN Regional Spec 1.0.3 Reference| Frequency +------------|-----------------|:----------------:|:-------------------:|-------- +`-D CFG_eu868` | `LMIC_REGION_eu868` | 1 | 2.2 | EU 863-870 MHz ISM +`-D CFG_us915` | `LMIC_REGION_us915` | 2 | 2.3 | US 902-928 MHz ISM +`-D CFG_au915` | `LMIC_REGION_au915` | 5 | 2.6 | Australia 915-928 MHz ISM +`-D CFG_as923` | `LMIC_REGION_as923` | 7 | 2.8 | Asia 923 MHz ISM +`-D CFG_as923jp` | `LMIC_REGION_as923` and `LMIC_COUNTRY_CODE_JP` | 7 | 2.8 | Asia 923 MHz ISM with Japan listen-before-talk (LBT) rules +`-D CFG_kr920` | `LMIC_REGION_kr920` | 8 | 2.9 | Korea 920-923 MHz ISM +`-D CFG_in866` | `LMIC_REGION_in866` | 9 | 2.10 | India 865-867 MHz ISM + +The library requires that the compile environment or the project config file define exactly one of `CFG_...` variables. As released, `project_config/lmic_project_config.h` defines `CFG_us915`. If you build with PlatformIO or other environments, and you do not provide a pointer to the platform config file, `src/lmic/config.h` will define `CFG_eu868`. + +MCCI BSPs add menu entries to the Arduino IDE so you can select the target region interactively. + +The library changes configuration pretty substantially according to the region selected, and this affects the symbols in-scope in your sketches and `.cpp` files. Some of the differences are listed below. This list is not comprehensive, and is subject to change in future major releases. + +#### eu868, as923, in866, kr920 + +If the library is configured for EU868, AS923, or IN866 operation, we make +the following changes: + +- Add the API `LMIC_setupBand()`. +- Add the constants `MAX_CHANNELS`, `MAX_BANDS`, `LIMIT_CHANNELS`, `BAND_MILLI`, +`BAND_CENTI`, `BAND_DECI`, and `BAND_AUX`. + +#### us915, au915 + +If the library is configured for US915 operation, we make the following changes: + +- Add the APIs `LMIC_enableChannel()`, +`LMIC_enableSubBand()`, `LMIC_disableSubBand()`, and `LMIC_selectSubBand()`. +- Add the constants `MAX_XCHANNELS`. +- Add a number of additional `DR_...` symbols. + +### Selecting the target radio transceiver + +You should define one of the following variables. If you don't, the library assumes +sx1276. There is a runtime check to make sure the actual transceiver matches the library +configuration. + +`#define CFG_sx1272_radio 1` + +Configures the library for use with an sx1272 transceiver. + +`#define CFG_sx1276_radio 1` + +Configures the library for use with an sx1276 transceiver. + +### Controlling use of interrupts + +`#define LMIC_USE_INTERRUPTS` + +If defined, configures the library to use interrupts for detecting events from the transceiver. If left undefined, the library will poll for events from the transceiver. See [Timing](#timing) for more info. Be aware that interrupts are not tested or supported on many platforms. + +### Disabling PING + +`#define DISABLE_PING` + +If defined, removes all code needed for Class B downlink during ping slots (PING). Removes the APIs `LMIC_setPingable()` and `LMIC_stopPingable()`. +Class A devices don't support PING, so defining `DISABLE_PING` is often a good idea. + +By default, PING support is included in the library. + +### Disabling Beacons + +`#define DISABLE_BEACONS` + +If defined, removes all code needed for handling beacons. Removes the APIs `LMIC_enableTracking()` and `LMIC_disableTracking()`. + +Enabling beacon handling allows tracking of network time, and is required if you want to enable downlink during ping slots. However, many networks don't support Class B devices. Class A devices don't support tracking beacons, so defining `DISABLE_BEACONS` might be a good idea. + +By default, beacon support is included in the library. + +### Enabling Network Time Support + +`#define LMIC_ENABLE_DeviceTimeReq number /* boolean: 0 or non-zero */` + +Disable or enable support for device network-time requests (LoRaWAN MAC request 0x0D). If zero, support is disabled. If non-zero, support is enabled. + +If disabled, stub routines are provided that will return failure (so you don't need conditional compiles in client code). + +### Rarely changed variables + +The remaining variables are rarely used, but we list them here for completeness. + +#### Changing debug output + +`#define LMIC_PRINTF_TO SerialLikeObject` + +This variable should be set to the name of a `Serial`-like object (any subclass of Arduino's `Print` class), used for printing messages. If this variable is set, any calls to the standard `printf` function (or more generally all writes to the global `stdout` file descriptor) will redirected to the specified stream. + +When this is not defined, `printf` and `stdout` are untouched and their behavior might vary among boards (and could print to somewhere, but also throw away output or crash). So *if* you want to use `printf` or `LMIC_DEBUG_LEVEL`, make sure to also define this. + +#### Getting debug from the RF library + +`#define LMIC_DEBUG_LEVEL number /* 0, 1, or 2 */` + +This variable determines the amount of debug output to be produced by the library. The default is `0`. + +If `LMIC_DEBUG_LEVEL` is zero, no output is produced. If `1`, limited output is produced. If `2`, more extensive output is produced. + +Note that debug output will influence the timing of various parts of the library and could introduce timing problems (especially in the RX window timing), so use it carefully. + +Debug output is generated using the standard `printf` function, so unless your environment already redirects `printf` / `stdout` somewhere, you should also configure `LIMC_PRINTF_TO`. + +#### Selecting the AES library + +The library comes with two AES implementations. The original implementation is better on +ARM processors because it's faster, but it's larger. For smaller AVR8 processors, a +second library ("IDEETRON") is provided that has a smaller code footprint. +You may define one of the following variables to choose the AES implementation. If you don't, +the library uses the IDEETRON version. + +`#define USE_ORIGINAL_AES` + +If defined, the original AES implementation is used. + +`#define USE_IDEETRON_AES` + +If defined, the IDEETRON AES implementation is used. + +#### Defining the OS Tick Frequency + +`#define US_PER_OSTICK_EXPONENT number` + +This variable should be set to the base-2 logarithm of the number of microseconds per OS tick. The default is 4, +which indicates that each tick corresponds to 16 microseconds (because 16 == 2^4). + +#### Setting the SPI-bus frequency + +`#define LMIC_SPI_FREQ floatNumber` + +This variable sets the default frequency for the SPI bus connection to the transceiver. The default is `1E6`, meaning 1 MHz. However, this can be overridden by the contents of the `lmic_pinmap` structure, and we recommend that you use that approach rather than editing the `project_config/lmic_project_config.h` file. + +#### Changing handling of runtime assertion failures + +The variables `LMIC_FAILURE_TO` and `DISABLE_LMIC_FAILURE_TO` +control the handling of runtime assertion failures. By default, assertion messages are displayed using +the `Serial` object. You can define LMIC_FAILURE_TO to be the name of some other `Print`-like object. You can +also define `DISABLE_LMIC_FAILURE_TO` to any value, in which case assert failures will silently halt execution. + +#### Disabling JOIN + +`#define DISABLE_JOIN` + +If defined, removes code needed for OTAA activation. Removes the APIs `LMIC_startJoining()` and `LMIC_tryRejoin()`. + +#### Disabling Class A MAC commands + +`DISABLE_MCMD_DutyCycleReq`, `DISABLE_MCMD_RXParamSetupReq`, `DISABLE_MCMD_RXTimingSetupReq`, `DISABLE_MCMD_NewChannelReq`, and `DISABLE_MCMD_DlChannelReq` respectively disable code for various Class A MAC commands. + +#### Disabling Class B MAC commands + +`DISABLE_MCMD_PingSlotChannelReq` disables the PING_SET MAC commands. It's implied by `DISABLE_PING`. + +`ENABLE_MCMD_BeaconTimingAns` enables the next-beacon start command. It's disabled by default, and overridden (if enabled) by `DISABLE_BEACON`. (This command is deprecated.) + +#### Disabling user events + +Code to handle registered callbacks for transmit, receive, and events can be suppressed by setting `LMIC_ENABLE_user_events` to zero. This C preprocessor macro is always defined as a post-condition of `#include "config.h"`; if non-zero, user events are supported, if zero, user events are not-supported. The default is to support user events. + +#### Disabling external reference to `onEvent()` + +In V3 of the LMIC, you do not need to define a function named `onEvent`. The LMIC will notice that there's no such function, and will suppress the call. However, be cautious -- in a large software package, `onEvent()` may be defined for some other purpose. The LMIC has no way of knowing that this is not the LMIC's `onEvent`, so it will call the function, and this may cause problems. + +All reference to `onEvent()` can be suppressed by setting `LMIC_ENABLE_onEvent` to 0. This C preprocessor macro is always defined as a post-condition of `#include "config.h"`; if non-zero, a weak reference to `onEvent()` will be used; if zero, the user `onEvent()` function is not supported, and the client must register an event handler explicitly. See the PDF documentation for details on `LMIC_registerEventCb()`. + +#### Enabling long messages + +By default, LMIC allows messages up to 255 bytes, as defined in the LoRaWAN standard and required by compliance testing. To save RAM for simple devices, this can be limited using the `LMIC_MAX_FRAME_LENGTH` macro. This macro defines the length of the full frame, the maximum payload size is a bit smaller (and can be read from the `MAX_LEN_PAYLOAD` constant). + +This value controls both the TX and RX buffers, so reducing it by 1 saves 2 bytes of RAM. The value should be not be set too small, since that can prevent properly receiving network downlinks (e.g. join accepts or MAC commands). Using `#define LMIC_MAX_FRAME_LENGTH 64` is common and should be big enough for most operation, while saving 384 bytes of RAM. + +Originally, this was configured using the `LMIC_ENABLE_long_messages` macro, which is still supported for compatibility. Setting `LMIC_ENABLE_long_messages` to 0 is equivalent to setting `LMIC_MAX_FRAME_LENGTH` to 64. + +#### Enabling LMIC event logging calls + +When debugging the LMIC, debug prints change timing, and can make things not work at all. The LMIC has embedded optional calls to capture debug information that can be printed out later, when the LMIC is not active. Logging is enabled by setting `LMIC_ENABLE_event_logging` to 1. The default is not to log. This C preprocessor macro is always defined as a post-condition of `#include "config.h"`. + +The compliance test script includes a suitable logging implementation; the other example scripts do not. + +#### Special purpose + +`#define DISABLE_INVERT_IQ_ON_RX` disables the inverted Q-I polarity on RX. **Use of this variable is deprecated, see issue [#250](https://github.com/mcci-catena/arduino-lmic/issues/250).** Rather than defining this, set the value of `LMIC.noRXIQinversion`. If set non-zero, receive will be non-inverted. End-devices will be able to receive messages from each other, but will not be able to hear the gateway (other than Class B beacons)aa. If set zero, (the default), end devices will only be able to hear gateways, not each other. + +## Supported hardware + +This library is intended to be used with plain LoRa transceivers, +connecting to them using SPI. In particular, the SX1272 and SX1276 +families are supported (which should include SX1273, SX1277, SX1278 and +SX1279 which only differ in the available frequencies, bandwidths and +spreading factors). It has been tested with both SX1272 and SX1276 +chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92 +and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip +respectively). + +This library contains a full LoRaWAN stack and is intended to drive +these Transceivers directly. It is *not* intended to be used with +full-stack devices like the Microchip RN2483 and the Embit LR1272E. +These contain a transceiver and microcontroller that implements the +LoRaWAN stack and exposes a high-level serial interface instead of the +low-level SPI transceiver interface. + +This library is intended to be used inside the Arduino environment. It +should be architecture-independent. Users have tested this on AVR, ARM, Xtensa-based, and RISC-V based system. + +This library can be quite heavy on small systems, especially if the fairly small ATmega +328p (such as in the Arduino Uno) is used. In the default configuration, +the available 32K flash space is nearly filled up (this includes some +debug output overhead, though). By disabling some features in `project_config/lmic_project_config.h` +(like beacon tracking and ping slots, which are not needed for Class A devices), +some space can be freed up. + +## Pre-Integrated Boards + +There are two ways of using this library, either with pre-integrated boards or with manually configured boards. + +The following boards are pre-integrated. + +- Adafruit [Feather 32u4 LoRa 900 MHz][1] (SX1276) +- Adafruit [Feather M0 LoRa 900 MHz][2] (SX1276) +- MCCI Catena 4410, 4420, [4450][3], [4460][4] and [4470][5] boards (based on Adafruit Feather boards plus wings) (SX1276) +- MCCI Catena 4551, [4610][6], 4611, [4612][7], 4617, [4618][7a], 4630, [4801][8] and 4802[12] boards (based on the Murata CMWX1ZZABZ-078 module) (SX1276) +- [TTGo LoRa32 V1][10] (based on the ESP32) +- [Heltec WiFi LoRa 32 V2][11] (based on the ESP32) + +[1]: https://www.adafruit.com/products/3078 +[2]: https://www.adafruit.com/products/3178 +[3]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4450-lorawan-iot-device +[4]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4460-sensor-wing-w-bme680 +[5]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/mcci-catena-4470-modbus-node-for-lorawan-technology +[6]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/mcci-catena-4610-integrated-node-for-lorawan-technology +[7]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4612-integrated-lorawan-node +[7a]: https://mcci.com/lorawan/products/catena-4618/ +[8]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4801 +[10]: https://makeradvisor.com/tools/ttgo-lora32-sx1276-esp32-oled/ +[11]: https://heltec.org/project/wifi-lora-32/ +[12]: https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4802 + +> To help you know if you have to worry, we'll call such boards "pre-integrated" and prefix each section with suitable guidance. + +## PlatformIO + +For use with PlatformIO, the `lmic_project_config.h` has to be disabled with the flag `ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS`. +The settings are defined in PlatformIO by `build_flags`. + +```ini +lib_deps = + MCCI LoRaWAN LMIC library + +build_flags = + -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS + -D CFG_eu868=1 + -D CFG_sx1276_radio=1 +``` + +## Manual configuration + +If your desired transceiver board is not pre-integrated, you need to provide the library with the required information. + +You may need to wire up your transceiver. The exact +connections are a bit dependent on the transceiver board and Arduino +used, so this section tries to explain what each connection is for and +in what cases it is (not) required. + +Note that the SX127x module runs at 3.3V and likely does not like 5V on +its pins (though the datasheet is not say anything about this, and my +transceiver did not obviously break after accidentally using 5V I/O for +a few hours). To be safe, make sure to use a level shifter, or an +Arduino running at 3.3V. The Semtech evaluation board has 100 ohm resistors in +series with all data lines that might prevent damage, but I would not +count on that. + +### Power + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +The SX127x transceivers need a supply voltage between 1.8V and 3.9V. +Using a 3.3V supply is typical. Some modules have a single power pin +(like the HopeRF modules, labeled 3.3V) but others expose multiple power +pins for different parts (like the Semtech evaluation board that has +`VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together. +Any *GND* pins need to be connected to the Arduino *GND* pin(s). + +### SPI + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +The primary way of communicating with the transceiver is through SPI +(Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and +SS. The former three need to be directly connected: so MOSI to MOSI, +MISO to MISO, SCK to SCK. Where these pins are located on your Arduino +varies, see for example the "Connections" section of the [Arduino SPI +documentation](SPI). + +The SS (slave select) connection is a bit more flexible. On the SPI +slave side (the transceiver), this must be connect to the pin +(typically) labeled *NSS*. On the SPI master (Arduino) side, this pin +can connect to any I/O pin. Most Arduinos also have a pin labeled "SS", +but this is only relevant when the Arduino works as an SPI slave, which +is not the case here. Whatever pin you pick, you need to tell the +library what pin you used through the pin mapping (see [below](#pin-mapping)). + +[SPI]: https://www.arduino.cc/en/Reference/SPI + +### DIO pins + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +The DIO (digital I/O) pins on the SX127x can be configured +for various functions. The LMIC library uses them to get instant status +information from the transceiver. For example, when a LoRa transmission +starts, the DIO0 pin is configured as a TxDone output. When the +transmission is complete, the DIO0 pin is made high by the transceiver, +which can be detected by the LMIC library. + +The LMIC library needs only access to DIO0, DIO1 and DIO2, the other +DIOx pins can be left disconnected. On the Arduino side, they can +connect to any I/O pin. If interrupts are used, the accuracy of timing +will be improved, particularly the rest of your `loop()` function has +lengthy calculations; but in that case, the enabled DIO pins must all +support rising-edge interrupts. See the [Timing](#timing) section below. + +In LoRa mode the DIO pins are used as follows: + +* DIO0: TxDone and RxDone +* DIO1: RxTimeout + +In FSK mode they are used as follows:: + +* DIO0: PayloadReady and PacketSent +* DIO2: TimeOut + +Both modes need only 2 pins, but the transceiver does not allow mapping +them in such a way that all needed interrupts map to the same 2 pins. +So, if both LoRa and FSK modes are used, all three pins must be +connected. + +The pins used on the Arduino side should be configured in the pin +mapping in your sketch, by setting the values of `lmic_pinmap::dio[0]`, `[1]`, and `[2]` (see [below](#pin-mapping)). + +### Reset + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +The transceiver has a reset pin that can be used to explicitly reset +it. The LMIC library uses this to ensure the chip is in a consistent +state at startup. In practice, this pin can be left disconnected, since +the transceiver will already be in a sane state on power-on, but +connecting it might prevent problems in some cases. + +On the Arduino side, any I/O pin can be used. The pin number used must +be configured in the pin mapping `lmic_pinmap::rst` field (see [below](#pin-mapping)). + +### RXTX + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +The transceiver contains two separate antenna connections: One for RX +and one for TX. A typical transceiver board contains an antenna switch +chip, that allows switching a single antenna between these RX and TX +connections. Such a antenna switcher can typically be told what +position it should be through an input pin, often labeled *RXTX*. + +The easiest way to control the antenna switch is to use the *RXTX* pin +on the SX127x transceiver. This pin is automatically set high during TX +and low during RX. For example, the HopeRF boards seem to have this +connection in place, so they do not expose any *RXTX* pins and the pin +can be marked as unused in the pin mapping. + +Some boards do expose the antenna switcher pin, and sometimes also the +SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the +former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these +together with a jumper wire is the easiest solution. + +Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be +configured to control the antenna switch. Connect the antenna switch +control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O +pin on the Arduino side, and configure the pin used in the pin map (see +[below](#pin-mapping)). + +The configuration entry `lmic_pinmap::rxtx` configures the pin to be used for the *RXTX* control function, in terms of the Arduino `wire.h` digital pin number. If set to `LMIC_UNUSED_PIN`, then the library assumes that software does not need to control the antenna switch. + +### RXTX Polarity + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +If an external switch is used, you also must specify the polarity. Some modules want *RXTX* to be high for transmit, low for receive; Others want it to be low for transmit, high for receive. The Murata module, for example, requires that *RXTX* be *high* for receive, *low* for transmit. + +The configuration entry `lmic_pinmap::rxtx_rx_active` should be set to the state to be written to the *RXTX* pin to make the receiver active. The opposite state is written to make the transmitter active. If `lmic_pinmap::rxtx` is `LMIC_UNUSED_PIN`, then the value of `lmic_pinmap::rxtx_rx_active` is ignored. + +### Pin mapping + +> If you're using a [pre-integrated board](#pre-integrated-boards), you can skip this section. + +Refer to the documentation on your board for the required settings. + +Remember, for pre-integrated boards, you don't worry about this. + +We have details for the following manually-configured boards here: + +- [LoRa Nexus by Ideetron](#lora-nexus-by-ideetron) + +If your board is not configured, you need at least to provide your own `lmic_pinmap`. As described above, a variety of configurations are possible. To tell the LMIC library how your board is configured, you must declare a variable containing a pin mapping struct in your sketch file. If you call `os_init()` to initialize the LMIC, you must name this structure `lmic_pins`. If you call `os_init_ex()`, you may name the structure what you like, but you pass a pointer as the parameter to `os_init_ex()`. + +Here's an example of a simple initialization: + +```c++ + lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, + // optional: set polarity of rxtx pin. + .rxtx_rx_active = 0, + // optional: set RSSI cal for listen-before-talk + // this value is in dB, and is added to RSSI + // measured prior to decision. + // Must include noise guardband! Ignored in US, + // EU, IN, other markets where LBT is not required. + .rssi_cal = 0, + // optional: override LMIC_SPI_FREQ if non-zero + .spi_freq = 0, + }; +``` + +The names refer to the pins on the transceiver side, the numbers refer +to the Arduino pin numbers (to use the analog pins, use constants like +`A0`). For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2 +respectively. Any pins that are not needed should be specified as +`LMIC_UNUSED_PIN`. The NSS and dio0 pins are required. The others can +potentially left out (depending on the environments and requirements, +see the notes above for when a pin can or cannot be left out). + +#### Advanced initialization + +In some boards require much more advanced management. The LMIC has a very flexible framework to support this, but it requires you to do some C++ work. + +1. You must define a new class derived from `Arduino_LMIC::HalConfiguration_t`. (We'll call this `cMyHalConfiguration_t`). + +2. This class *may* define overrides for several methods (discussed below). + +3. You must create an instance of your class, e.g. + + ```c++ + cMyHalConfiguration_t myHalConfigInstance; + ``` + +4. You add another entry in your `lmic_pinmap`, `pConfig = &myHalConfigInstance`, to link your pin-map to your object. + +The full example looks like this: + +```c++ +class cMyHlaConfiguration_t : public Arduino_LMIC::HalConfiguration_t + { +public: + // ... + // put your method function override declarations here. + + // this example uses RFO at 10 dBm or less, PA_BOOST up to 17 dBm, + // or the high-power mode above 17 dBm. In other words, it lets the + // LMIC-determined policy determine what's to be done. + + virutal TxPowerPolicy_t getTxPowerPolicy( + TxPowerPolicy_t policy, + int8_t requestedPower, + uint32_t frequency + ) override + { + return policy; + } + } +``` + +#### HalConfiguration_t methods + +- `ostime_t setModuleActive(bool state)` is called by the LMIC to make the module active or to deactivate it (the value of `state` is true to activate). The implementation must turn power to the module on and otherwise prepare for it to go to work, and must return the number of OS ticks to wait before starting to use the radio. + +- `void begin(void)` is called during initialization, and is your code's chance to do any early setup. + +- `void end(void)` is (to be) called during late shutdown. (Late shutdown is not implemented yet; but we wanted to add the API for consistency.) + +- `bool queryUsingTcxo(void)` shall return `true` if the module uses a TCXO; `false` otherwise. + +- `TxPowerPolicy_t getTxPowerPolicy(TxPowerPolicy_t policy, int8_t requestedPower, uint32_t frequency)` allows you to override the LMIC's selection of transmit power. If not provided, the default method forces the LMIC to use PA_BOOST mode. (We chose to do this because we found empirically that the Hope RF module doesn't support RFO, and because legacy LMIC code never used anything except PA_BOOST mode.) + +Caution: the LMIC has no way of knowing whether the mode you return makes sense. Use of 20 dBm mode without limiting duty cycle can over-stress your module. The LMIC currently does not have any code to duty-cycle US transmissions at 20 dBm. If properly limiting transmissions to 400 milliseconds, a 1% duty-cycle means at most one message every 40 seconds. This shouldn't be a problem in practice, but buggy upper level software still might do things more rapidly. + + +#### LoRa Nexus by Ideetron + +This board uses the following pin mapping: + +```c++ + const lmic_pinmap lmic_pins = { + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET + .dio = {4, 5, 7}, + }; +``` + +## Example Sketches + +This library provides several examples. + +- [`ttn-otaa.ino`](examples/ttn-otaa/ttn-otaa.ino) shows a basic transmission of a "Hello, world!" message + using the LoRaWAN protocol. It contains some frequency settings and + encryption keys intended for use with The Things Network, but these + also correspond to the default settings of most gateways, so it + should work with other networks and gateways as well. + The example uses over-the-air activation (OTAA) to first join the network to establish a + session and security keys. This was tested with The Things Network, + but should also work (perhaps with some changes) for other networks. + OTAA is the preferred way to work with production LoRaWAN networks. + +- [`ttn-otaa-feather-us915.ino`](examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino) is a version of `ttn-otaa.ino` that has + been configured for use with the Feather M0 LoRa, in the US915 region, + with The Things Network. Remember that you may also have to change `config.h` + from defaults. This sketch also works with the MCCI Catena family of products + as well as with the Feather 32u4 LoRa. + +- [`ttn-otaa-halconfig-us915.ino`](examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino) is a version of `ttn-otaa.ino` that uses the LMIC automatic configuration system, in the US915 region, + with The Things Network. Remember that you may also have to change `config.h` + from defaults. This sketch works with all the boards listed above under [Pre-Integrated Boards](#pre-integrated-boards). + +- [`ttn-otaa-feather-us915-dht22.ino`](examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino) + is a further refinement of `ttn-otaa-feather-us915.ino`. It measures and + transmits temperature and relative humidity using a DHT22 sensor. It's only + been tested with Feather M0-family products. + +- [`raw.ino`](examples/raw/raw.ino) shows how to access the radio on a somewhat low level, + and allows to send raw (non-LoRaWAN) packets between nodes directly. + This is useful to verify basic connectivity, and when no gateway is + available, but this example also bypasses duty cycle checks, so be + careful when changing the settings. + +- [`raw-feather.ino`](examples/raw-feather/raw-feather.ino) is a version of `raw.ino` that is completely configured + for the Adafruit [Feather M0 LoRa](https://www.adafruit.com/product/3178), and for a variety + of other MCCI products. + +- [`raw-halconfig.ino`](examples/raw-halconfig/raw-halconfig.ino) is like the other `raw` examples, but is most general. It's configured according to the LMIC's auto-configuration mechanism, and adapts according to the selected region. + +- [`ttn-abp.ino`](examples/ttn-abp/ttn-abp.ino) shows a basic transmission of a "Hello, world!" message + using the LoRaWAN protocol. This example + uses activation-by-personalization (ABP, preconfiguring a device + address and encryption keys), and does not employ over-the-air + activation. + + ABP should not be used if you have access to a production gateway and network; it's + not compliant with LoRaWAN standards, it's not FCC compliant, and it's uses spectrum + in a way that's unfair to other users. However, it's often the most economical way to + get your feet wet with this technology. It's possible to do ABP compliantly with the LMIC + framework, but you need to have FRAM storage and a framework that saves uplink and + downlink counts across reboots and resets. See, for example, + [Catena-Arduino-Platform](https://github.com/mcci-catena/Catena-Arduino-Platform). + +- [`ttn-abp-feather-us915-dht22.ino`](examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino) + refines `ttn-abp.ino` by configuring for use with the Feather M0 LoRa in the US915 region, + with a single-channel gateway on The Things Network; it measures and transmits temperature and relative + humidity using a DHT22 sensor. It's only been tested with Feather M0-family products. + + ABP should not be used if you have access to a production gateway and network; it's + not compliant with LoRaWAN standards, it's not FCC compliant, and it's uses spectrum + in a way that's unfair to other users. However, it's often the most economical way to + get your feet wet with this technology. It's possible to do ABP compliantly with the LMIC + framework, but you need to have FRAM storage and a framework that saves uplink and + downlink counts across reboots and resets. See, for example, + [Catena-Arduino-Platform](https://github.com/mcci-catena/Catena-Arduino-Platform). + +- [`header_test.ino`](examples/header_test/header_test.ino) just tests the header files; it's used for regression testing. + +- [`compliance-otaa-halconfig.ino`](examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino) is a test sketch that is used for LoRaWAN compliance testing. + +- [`helium-us9115.ino`](examples\helium-us915\helium-us915.ino) is a complete example for using the LMIC on the Helium network. It's very similar to the OTAA examples, but sets up the prejoin parameters properly for the initial deployment of Helium LoRaWAN support. + +- [`ttn-otaa-network-time.ino`](examples/ttn-otaa-network-time/ttn-otaa-network-time.ino) demonstrates use of the network time request. Network time requests are not supported by The Things Network as of the time of writing. + +## Timing + +The library is +responsible for keeping track of time of certain network events, and scheduling +other events relative to those events. For Class A uplink transmissions, the library must note +when a packet finishes transmitting, so it can open up the RX1 and RX2 +receive windows at a fixed time after the end of transmission. The library does this +by watching for rising edges on the DIO0 output of the SX127x, and noting the time. + +The library observes and processes rising edges on the pins as part of `os_runloop()` processing. +This can be configured in one of two ways (see +[Controlling use of interrupts](#controlling-use-of-interrupts)). See [Interrupts and Arduino system timing](#interrupts-and-arduino-system-timing) for implementation details. + +By default, the library +polls the enabled pins to determine whether an event has occurred. This approach +allows use of any CPU pin to sense the DIOs, and makes no assumptions about +interrupts. However, it means that the end-of-transmit event is not observed +(and time-stamped) until `os_runloop_once()` is called. + +Optionally, you can configure the LMIC library to use interrupts. The +interrupt handlers capture the time of +the event. Actual processing is done the next time that `os_runloop_once()` +is called, using the captured time. However, this requires that the +DIO pins be wired to Arduino pins that support rising-edge interrupts, +and it may require custom initialization code on your platform to +hook up the interrupts. + +### Controlling protocol timing + +The timing of end-of-transmit interrupts is used to determine when to open the downlink receive window. Because of the considerations above, some inaccuracy in the time stamp for the end-of-transmit interrupt is inevitable. + +Fortunately, the timing of the receive windows at the device need not be extremely accurate; the LMIC has to turn on the receiver early enough to capture a downlink +from the gateway and must leave the receiver on long enough to compensate for timing +errors due to various inaccuracies. To make it easier for the device to catch downlinks, the gateway first transmits a preamble consisting of 8 symbols. The SX127x receiver needs to see at least 4 symbols to detect a message. The Arduino LMIC tries to enable the receiver for 6 +symbol times slightly before the start of the receive window. + +The HAL bases all timing on the Arduino `micros()` timer, which has a platform-specific +granularity and accuracy, and is based on the primary microcontroller clock. + +If using an internal oscillator that is less than 100ppm accurate but better than 4000 ppm accurate, or if your other `loop()` processing +is time consuming, you can use [`LMIC_setClockError()`](#lmic_setclockerror) +to cause the library to leave the radio on longer. Note that for various reasons, it is not practical to set enormous clock errors. Oscillators that are 4000 ppm accurate or worse should be supplemented or disciplined with a better timing source. The LoRaWAN spec, for class B, implicitly assumes 100 ppm accuracy in the clock. + +Users of older versions of the library were advised to set large clock errors if they were experiencing timing problems. However, close analysis and debugging during the preparation of v3.1.0 of this library revealed that the real errors were in the timing calculations in the library. Once those were corrected, the need for large clock error settings was reduced. It's still possible to use large clock errors if needed, but this must be enabled via a compile time switch. + +An even more accurate solution could be to use a dedicated timer with an +input capture unit, that can store the timestamp of a change on the DIO0 +pin (the only one that is timing-critical) entirely in hardware. +Experience shows that this is not normally required, so we leave this as +a customization to be performed on a platform-by-platform basis. We provide +a special API, `radio_irq_handler_v2(u1_t dio, ostime_t tEvent)`. This +API allows you to supply a hardware-captured time for extra accuracy. + +The practical consequence of inaccurate timing is reduced battery life; +the LMIC must turn on the receiver earlier in order to be sure to capture downlink packets. However, this is a second order effect on class A devices; every receive is preceded by a transmit, which takes approximately ten times as much power per millisecond as a receive. + +### `LMIC_setClockError()` + +You may call this routine during initialization to inform the LMIC code about the timing accuracy of your system. + +```c++ +enum { MAX_CLOCK_ERROR = 65535 }; + +void LMIC_setClockError( + u2_t error +); +``` + +This function sets the anticipated relative clock error. `MAX_CLOCK_ERROR` +represents +/- 100%, and 0 represents no additional clock compensation. +To allow for an error of 20%, you would call + +```c++ +LMIC_setClockError(MAX_CLOCK_ERROR * 20 / 100); +``` + +Setting a high clock error causes the RX windows to be opened earlier than it otherwise would be. This causes more power to be consumed. For Class A devices, this extra power is not substantial, but for Class B devices, this can be significant. + +For a variety of reasons, the LMIC normally ignores clock errors greater than 4000 ppm (0.4%). The compile-time flag `LMIC_ENABLE_arbitrary_clock_error` can remove this limit. To do this, define it to a non-zero value. + +This clock error is not reset by `LMIC_reset()`. + +### Interrupts and Arduino system timing + +The IBM LMIC used as the basis for this code disables interrupts while the radio driver is active, to prevent reentrancy via `radio_irq_handler()` at unexpected moments. It uses `os_getTime()`, and assumes that `os_getTime()` still works when interrupts were disabled. This causes problems on Arduino platforms. Most board support packages use interrupts to advance `millis()` and `micros()`, and with these BSPs, `millis()` and `micros()` return incorrect values while interrupts are disabled. Although some BSPs (like the ones provided by MCCI) provide real time correctly while interrupts are disabled, this is not portable. It's not practical to make such changes in every BSP. + +To avoid this, the LMIC processes events in several steps; these steps ensure that `radio_irq_handler_v2()` is only called at predictable times. + +1. If interrupts are enabled via `LMIC_USE_INTERRUPTS`, hardware interrupts catch the time of the interrupt and record that the interrupt occurred. These routines rely on hardware edge-sensitive interrupts. If your hardware interrupts are level-sensitive, you must mask the interrupt somehow at the ISR. You can't use SPI routines to talk to the radio, because this may leave the SPI system and the radio in undefined states. In this configuration, `hal_io_pollIRQs()` exists but is a no-op. + +2. If interrupts are not enabled via `LMIC_USE_INTERRUPTS`, the digital I/O lines are polled every so often by calling the routine `hal_io_pollIRQs()`. This routine watches for edges on the relevant digital I/O lines, and records the time of transition. + +3. The LMIC `os_runloop_once()` routine calls `hal_processPendingIRQs()`. This routine uses the timestamps captured by the hardware ISRs and `hal_io_pollIRQs()` to invoke `radio_irq_hander_v2()` with the appropriate information. `hal_processPendingIRQs()` in turn calls `hal_io_pollIRQs()` (in case interrupts are not configured). + +4. For compatibility with older versions of the Arduino LMIC, `hal_enableIRQs()` also calls `hal_io_pollIRQs()` when enabling interrupts. However, it does not dispatch the interrupts to `radio_irq_handler_v2()`; this must be done by a subsequent call to `hal_processPendingIRQs()`. + +## Downlink data rate + +Note that the data rate used for downlink packets in the RX2 window varies by region. Consult your network's manual for any divergences from the LoRaWAN Regional Parameters. This library assumes that the network follows the regional default. + +Some networks use different values than the specification. For example, in Europe, the specification default is DR0 (SF12, 125 kHz bandwidth). However, iot.semtech.com and The Things Network both used SF9 / 125 kHz or DR3). If using over-the-air activation (OTAA), the network will download RX2 parameters as part of the JoinAccept message; the LMIC will honor the downloaded parameters. + +However, when using personalized activate (ABP), it is your +responsibility to set the right settings, e.g. by adding this to your +sketch (after calling `LMIC_setSession`). `ttn-abp.ino` already does +this. + +```c++ +LMIC.dn2Dr = DR_SF9; +``` + +## Encoding Utilities + +It is generally important to make LoRaWAN messages as small as practical. Extra bytes mean extra transmit time, which wastes battery power and interferes with other nodes on the network. + +To simplify coding, the Arduino header file defines some data encoding utility functions to encode floating-point data into `uint16_t` values using `sflt16` or `uflt16` bit layout. For even more efficiency, there are versions that use only the bottom 12 bits of the `uint16_t`, allowing for other bits to be carried in the top 4 bits, or for two values to be crammed into three bytes. + +- `uint16_t LMIC_f2sflt16(float)` converts a floating point number to a [`sflt16`](#sflt16)-encoded `uint16_t`. +- `uint16_t LMIC_f2uflt16(float)` converts a floating-point number to a [`uflt16`](#uflt16)-encoded `uint16_t`. +- `uint16_t LMIC_f2sflt12(float)` converts a floating-point number to a [`sflt12`](#sflt12)-encoded `uint16_t`, leaving the top four bits of the result set to zero. +- `uint16_t LMIC_f2uflt12(float)` converts a floating-point number to a [`uflt12`](#sflt12)-encoded `uint16_t`, leaving the top four bits of the result set to zero. + +JavaScript code for decoding the data can be found in the following sections. + +### sflt16 + +A `sflt16` datum represents an unsigned floating point number in the range [0, 1.0), transmitted as a 16-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +15 | Sign bit +14..11 | binary exponent `b` +10..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/2048 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. However, it is similar to IEEE format in that it uses sign-magnitude rather than twos-complement for negative values. + +For example, if the data value is 0x8D, 0x55, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0x8D55. +2. Bit 15 is 1, so this is a negative value. +3. `b` is 1, and `b`-15 is -14. 2^-14 is 1/16384 +4. `f` is 0x555. 0x555/2048 = 1365/2048 is 0.667 +5. `f * 2^(b-15)` is therefore 0.667/16384 or 0.00004068 +6. Since the number is negative, the value is -0.00004068 + +Floating point mavens will immediately recognize: + +* This format uses sign/magnitude representation for negative numbers. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 2048..4095, and then transmit only `f - 2048`, saving a bit. However, this complicates the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. + +#### JavaScript decoder + +```javascript +function sflt162f(rawSflt16) + { + // rawSflt16 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFFF + // bit 15 is the sign bit + // bits 14..11 are the exponent + // bits 10..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the open interval (-1.0, 1.0); + // + + // throw away high bits for repeatability. + rawSflt16 &= 0xFFFF; + + // special case minus zero: + if (rawSflt16 == 0x8000) + return -0.0; + + // extract the sign. + var sSign = ((rawSflt16 & 0x8000) != 0) ? -1 : 1; + + // extract the exponent + var exp1 = (rawSflt16 >> 11) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawSflt16 & 0x7FF) / 2048.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +### uflt16 + +A `uflt16` datum represents an unsigned floating point number in the range [0, 1.0), transmitted as a 16-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +15..12 | binary exponent `b` +11..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/4096 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. + +For example, if the transmitted message contains 0xEB, 0xF7, and the transmitted byte order is big endian, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0xEBF7. +2. `b` is therefore 0xE, and `b`-15 is -1. 2^-1 is 1/2 +3. `f` is 0xBF7. 0xBF7/4096 is 3063/4096 == 0.74780... +4. `f * 2^(b-15)` is therefore 0.74780/2 or 0.37390 + +Floating point mavens will immediately recognize: + +* There is no sign bit; all numbers are positive. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 4096..8191, and then transmit only `f - 4096`, saving a bit. However, this complicated the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. + +#### uflt16 JavaScript decoder + +```javascript +function uflt162f(rawUflt16) + { + // rawUflt16 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFFF + // bits 15..12 are the exponent + // bits 11..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the half-open interval [0, 1.0); + // + + // throw away high bits for repeatability. + rawUflt16 &= 0xFFFF; + + // extract the exponent + var exp1 = (rawUflt16 >> 12) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawUflt16 & 0xFFF) / 4096.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +### sflt12 + +A `sflt12` datum represents an signed floating point number in the range [0, 1.0), transmitted as a 12-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +11 | sign bit +11..8 | binary exponent `b` +7..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/128 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. + +For example, if the transmitted message contains 0x8, 0xD5, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0x8D5. +2. The number is negative. +3. `b` is 0x1, and `b`-15 is -14. 2^-14 is 1/16384 +4. `f` is 0x55. 0x55/128 is 85/128, or 0.66 +5. `f * 2^(b-15)` is therefore 0.66/16384 or 0.000041 (to two significant digits) +6. The decoded number is therefore -0.000041. + +Floating point mavens will immediately recognize: + +* This format uses sign/magnitude representation for negative numbers. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 128 .. 256, and then transmit only `f - 128`, saving a bit. However, this complicates the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. +* It can be strongly argued that dropping the sign bit would be worth the effort, as this would get us 14% more resolution for a minor amount of work. + +#### sflt12f JavaScript decoder + +```javascript +function sflt12f(rawSflt12) + { + // rawSflt12 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFF (12 bits). For safety, we mask + // on entry and discard the high-order bits. + // bit 11 is the sign bit + // bits 10..7 are the exponent + // bits 6..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the open interval (-1.0, 1.0); + // + + // throw away high bits for repeatability. + rawSflt12 &= 0xFFF; + + // special case minus zero: + if (rawSflt12 == 0x800) + return -0.0; + + // extract the sign. + var sSign = ((rawSflt12 & 0x800) != 0) ? -1 : 1; + + // extract the exponent + var exp1 = (rawSflt12 >> 7) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawSflt12 & 0x7F) / 128.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +### uflt12 + +A `uflt12` datum represents an unsigned floating point number in the range [0, 1.0), transmitted as a 16-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +11..8 | binary exponent `b` +7..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/256 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. + +For example, if the transmitted message contains 0x1, 0xAB, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0x1AB. +2. `b` is therefore 0x1, and `b`-15 is -14. 2^-14 is 1/16384 +3. `f` is 0xAB. 0xAB/256 is 0.67 +4. `f * 2^(b-15)` is therefore 0.67/16384 or 0.0000408 (to three significant digits) + +Floating point mavens will immediately recognize: + +* There is no sign bit; all numbers are positive. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 256 .. 512, and then transmit only `f - 256`, saving a bit. However, this complicates the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. + +#### uflt12f JavaScript decoder + +```javascript +function uflt12f(rawUflt12) + { + // rawUflt12 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFF (12 bits). For safety, we mask + // on entry and discard the high-order bits. + // bits 11..8 are the exponent + // bits 7..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the half-open interval [0, 1.0); + // + + // throw away high bits for repeatability. + rawUflt12 &= 0xFFF; + + // extract the exponent + var exp1 = (rawUflt12 >> 8) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawUflt12 & 0xFF) / 256.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +## Release History + +- v3.3.0 is primarily a maintenance and roll-up release. + + - Added [LoRaWAN-at-a-glance](doc/LoRaWAN-at-a-glance.pdf) and a [state diagram of the LMIC](doc/LMIC-FSM.pdf). + - Several PRs from Matthijs Kooijman to improve compatibility with the original Arduino LMIC ([#608](https://github.com/mcci-catena/arduino-lmic/issue/608), [#609](https://github.com/mcci-catena/arduino-lmic/issue/609)). + - Numerous documentation improvements contributed by the community ([#431](https://github.com/mcci-catena/arduino-lmic/issue/), [#612](https://github.com/mcci-catena/arduino-lmic/issue/612), [#614](https://github.com/mcci-catena/arduino-lmic/issue/614), [#625](https://github.com/mcci-catena/arduino-lmic/issue/625)). + +- v3.2.0 has the following changes: + + - [#550](https://github.com/mcci-catena/arduino-lmic/issue/550) fixes debug prints in `ttn-otaa.ino`. + - [#553](https://github.com/mcci-catena/arduino-lmic/issue/553) add full regional support to `raw.ino` and `ttn-abp.ino`. + - [#570](https://github.com/mcci-catena/arduino-lmic/issue/570) corrects handling of piggy-back MAC responses when sending an `LMIC_sendAlive()` (`OPMODE_POLL`) message. + - [#524](https://github.com/mcci-catena/arduino-lmic/issue/524) corrects handling of interrupt disable, and slightly refactors the low-level interrupt handling wrappers for clarity. With this change, `radio_irq_handler_v2()` is never called except from the run loop, and so the radio driver need not (and does not) disable interrupts. Version is v3.1.0.20. + - [#568](https://github.com/mcci-catena/arduino-lmic/issue/568) improves documentation for the radio driver. + - [#537](https://github.com/mcci-catena/arduino-lmic/pull/537) fixes a compile error in SX1272 support. (Thanks @ricaun.) Version is v3.1.0.10. + +- v3.1.0 officially adopts the changes from v3.0.99. There were dozens of changes; check the GitHub issue logs and change logs. This was a breaking release (due to changes in data layout in the LMIC structure; the structure is accessed by apps). + +- v3.0.99 (the pre-release for v3.1.0) added the following changes. (This is not an exhaustive list.) Note that the behavior of the LMIC changes in important ways, as it now enforces the LoRaWAN mandated maximum frame size for a given data rate. For Class A devices, this may cause your device to go silent after join, if you're not able to handle the frame size dictated by the parameters downloaded to the device by the network during join. The library will attempt to find a data rate that will work, but there is no guarantee that the network has provided such a data rate. + + - [#470](https://github.com/mcci-catena/arduino-lmic/pull/470) corrects the name of AU915 region. [#516](https://github.com/mcci-catena/arduino-lmic/pull/516) makes sure that `LMIC_REGION_au921` is defined (but deprecated) for backward compatibility. + - [#452](https://github.com/mcci-catena/arduino-lmic/pull/452) fixes a bug [#450](https://github.com/mcci-catena/arduino-lmic/issues/450) in `LMIC_clrTxData()` that would cause join hiccups if called while (1) a join was in progress and (2) a regular data packet was waiting to be uplinked after the join completes. Also fixes AS923- and AU915-specific bugs [#446](https://github.com/mcci-catena/arduino-lmic/issues/446), [#447](https://github.com/mcci-catena/arduino-lmic/issues/447), [#448](https://github.com/mcci-catena/arduino-lmic/issues/448). Version is `v3.0.99.5`. + - [#443](https://github.com/mcci-catena/arduino-lmic/pull/443) addresses a number of problems found in cooperation with [RedwoodComm](https://redwoodcomm.com). They suggested a timing improvement to speed testing; this lead to the discovery of a number of problems. Some were in the compliance framework, but one corrects timing for very high spreading factors, several ([#442](https://github.com/mcci-catena/arduino-lmic/issues/442), [#436](https://github.com/mcci-catena/arduino-lmic/issues/438), [#435](https://github.com/mcci-catena/arduino-lmic/issues/435), [#434](https://github.com/mcci-catena/arduino-lmic/issues/434) fix glaring problems in FSK support; [#249](https://github.com/mcci-catena/arduino-lmic/issues/249) greatly enhances stability by making API calls much less likely to crash the LMIC if it's active. Version is v3.0.99.3. + - [#388](https://github.com/mcci-catena/arduino-lmic/issues/388), [#389](https://github.com/mcci-catena/arduino-lmic/issues/390), [#390](https://github.com/mcci-catena/arduino-lmic/issues/390) change the LMIC to honor the maximum frame size for a given DR in the current region. This proves to be a breaking change for many applications, especially in the US, because DR0 in the US supports only an 11-byte payload, and many apps were ignoring this. Additional error codes were defined so that apps can detect and recover from this situation, but they must detect; otherwise they run the risk of being blocked from the network by the LMIC. Because of this change, the next version of the LMIC will be V3.1 or higher, and the LMIC version for development is bumped to 3.0.99.0. + - [#401](https://github.com/mcci-catena/arduino-lmic/issues/401) adds 865 MHz through 868 MHz to the "1%" band for EU. + - [#395](https://github.com/mcci-catena/arduino-lmic/pull/395) corrects pin-mode initialization if using `hal_interrupt_init()`. + - [#385](https://github.com/mcci-catena/arduino-lmic/issues/385) corrects an error handling data rate selection for `TxParamSetupReq`, found in US-915 certification testing. (v2.3.2.71) + - [#378](https://github.com/mcci-catena/arduino-lmic/pull/378) completely reworks MAC downlink handling. Resulting code passes the LoRaWAN V1.5 EU certification test. (v2.3.2.70) + - [#360](https://github.com/mcci-catena/arduino-lmic/issues/360) adds support for the KR-920 regional plan. + +- v2.3.2 is a patch release. It incorporates two pull requests. + + - [#204](https://github.com/mcci-catena/arduino-lmic/pull/204) eliminates a warning if using a custom pin-map. + - [#206](https://github.com/mcci-catena/arduino-lmic/pull/206) updates CI testing to Arduino IDE v1.8.8. + +- v2.3.1 is a patch release. It adds ``, which loads the pre-processor LMIC configuration variables into scope (issue [#199](https://github.com/mcci-catena/arduino-lmic/issues/199)). + +- v2.3.0 introduces two important changes. + + 1. The pin-map is extended with an additional field `pConfig`, pointing to a C++ class instance. This instance, if provided, has extra methods for dealing with TCXO control and other fine details of operating the radio. It also gives a natural way for us to extend the behavior of the HAL. + + 2. Pinmaps can be pre-configured into the library, so that users don't have to do this in every sketch. + + Accompanying this was a fairly large refactoring of inner header files. We now have top-level header file ``, which provides much the same info as the original ``, without bringing most of the LMIC internal definitions into scope. We also changed the SPI API based on a suggestion from `@manuelbl`, making the HAL more friendly to structured BSPs (and also making the SPI API potentially faster). + +- Interim bug fixes: added a new API (`radio_irq_handler_v2()`), which allows the caller to provide the timestamp of the interrupt. This allows for more accurate timing, because the knowledge of interrupt overhead can be moved to a platform-specific layer ([#148](https://github.com/mcci-catena/arduino-lmic/issues/148)). Fixed compile issues on ESP32 ([#140](https://github.com/mcci-catena/arduino-lmic/issues/140) and [#153](https://github.com/mcci-catena/arduino-lmic/issues/150)). We added ESP32 and 32u4 as targets in CI testing. We switched CI testing to Arduino IDE 1.8.7. + Fixed issue [#161](https://github.com/mcci-catena/arduino-lmic/issues/161) selecting the Japan version of as923 using `CFG_as923jp` (selecting via `CFG_as923` and `LMIC_COUNTRY_CODE=LMIC_COUNTRY_CODE_JP` worked). + Fixed [#38](https://github.com/mcci-catena/arduino-lmic/issues/38) -- now any call to hal_init() will put the NSS line in the idle (high/inactive) state. As a side effect, RXTX is initialized, and RESET code changed to set value before transitioning state. Likely no net effect, but certainly more correct. + +- V2.2.2 adds `ttn-abp-feather-us915-dht22.ino` example, and fixes some documentation typos. It also fixes encoding of the `Margin` field of the `DevStatusAns` MAC message ([#130](https://github.com/mcci-catena/arduino-lmic/issues/130)). This makes Arduino LMIC work with networks implemented with [LoraServer](https://www.loraserver.io/). + +- V2.2.1 corrects the value of `ARDUINO_LMIC_VERSION` ([#123](https://github.com/mcci-catena/arduino-lmic/issues/123)), allows ttn-otaa-feather-us915 example to compile for the Feather 32u4 LoRa ([#116](https://github.com/mcci-catena/arduino-lmic/issues/116)), and addresses documentation issues ([#122](https://github.com/mcci-catena/arduino-lmic/issues/122), [#120](https://github.com/mcci-catena/arduino-lmic/issues/120)). + +- V2.2.0 adds encoding functions and `tn-otaa-feather-us915-dht22.ino` example. Plus a large number of issues: [#59](https://github.com/mcci-catena/arduino-lmic/issues/59), [#60](https://github.com/mcci-catena/arduino-lmic/issues/60), [#63](https://github.com/mcci-catena/arduino-lmic/issues/63), [#64](https://github.com/mcci-catena/arduino-lmic/issues/47) (listen-before-talk for Japan), [#65](https://github.com/mcci-catena/arduino-lmic/issues/65), [#68](https://github.com/mcci-catena/arduino-lmic/issues/68), [#75](https://github.com/mcci-catena/arduino-lmic/issues/75), [#78](https://github.com/mcci-catena/arduino-lmic/issues/78), [#80](https://github.com/mcci-catena/arduino-lmic/issues/80), [#91](https://github.com/mcci-catena/arduino-lmic/issues/91), [#98](https://github.com/mcci-catena/arduino-lmic/issues/98), [#101](https://github.com/mcci-catena/arduino-lmic/issues/101). Added full Travis CI testing, switched to travis-ci.com as the CI service. Prepared to publish library in the official Arduino library list. + +- V2.1.5 fixes issue [#56](https://github.com/mcci-catena/arduino-lmic/issues/56) (a documentation bug). Documentation was quickly reviewed and other issues were corrected. The OTAA examples were also updated slightly. + +- V2.1.4 fixes issues [#47](https://github.com/mcci-catena/arduino-lmic/issues/47) and [#50](https://github.com/mcci-catena/arduino-lmic/issues/50) in the radio driver for the SX1276 (both related to handling of output power control bits). + +- V2.1.3 has a fix for issue [#43](https://github.com/mcci-catena/arduino-lmic/issues/43): handling of `LinkAdrRequest` was incorrect for US915 and AU915; when TTN added ADR support on US and AU, the deficiency was revealed (and caused an ASSERT). + +- V2.1.2 has a fix for issue [#39](https://github.com/mcci-catena/arduino-lmic/issues/39) (adding a prototype for `LMIC_DEBUG_PRINTF` if needed). Fully upward compatible, so just a patch. + +- V2.1.1 has the same content as V2.1.2, but was accidentally released without updating `library.properties`. + +- V2.1.0 adds support for the Murata LoRaWAN module. + +- V2.0.2 adds support for additional regions. + +## Contributions + +This library started from the IBM V1.5 open-source code. + +- Thomas Telkamp and Matthijs Kooijman ported V1.5 to Arduino and did a lot of bug fixing. + +- Terry Moore, LeRoy Leslie, Frank Rose, and ChaeHee Won did a lot of work on US support. + +- Terry Moore added the AU915, AS923, KR920 and IN866 regions, and created the regionalization framework, and did corrections for LoRaWAN 1.0.3 compliance testing. + +- [`@tanupoo`](https://github.com/tanupoo) of the WIDE Project debugged AS923JP and LBT support. + +- [`@frazar`](https://github.com/frazar) contributed numerous features, e.g. network time support, CI testing, documentation improvements. + +- [`@manuelbl`](https://github.com/manuelbl) contributed numerous ESP32-related patches and improvements. + +- [`@ngraziano`](https://github.com/ngraziano) did extensive testing and contributed numerous ADR-related patches. + +There are many others, who have contributed code and also participated in discussions, performed testing, reported problems and results. Thanks to all who have participated. + +## Trademark Acknowledgements + +LoRa is a registered trademark of Semtech Corporation. LoRaWAN is a registered trademark of the LoRa Alliance. + +MCCI and MCCI Catena are registered trademarks of MCCI Corporation. + +All other trademarks are the properties of their respective owners. + +## License + +The upstream files from IBM v1.6 are based on the Berkeley license, +and the merge which synchronized this repository therefore migrated +the core files to the Berkeley license. However, modifications made +in the Arduino branch were done under the Eclipse license, so the +overall license of this repository is still Eclipse Public License +v1.0. The examples which use a more liberal +license. Some of the AES code is available under the LGPL. Refer to each +individual source file for more details, but bear in mind that until +the upstream developers look into this issue, it is safest to assume +the Eclipse license applies. + +### Support Open Source Hardware and Software + +MCCI invests time and resources providing this open source code, please support MCCI and open-source hardware by purchasing products from MCCI, Adafruit and other open-source hardware/software vendors! + +For information about MCCI's products, please visit [store.mcci.com](https://store.mcci.com/). diff --git a/libraries/arduino-lmic-master/assets/Feather-M0-LoRa-Wire.ai b/libraries/arduino-lmic-master/assets/Feather-M0-LoRa-Wire.ai new file mode 100644 index 0000000..662f104 --- /dev/null +++ b/libraries/arduino-lmic-master/assets/Feather-M0-LoRa-Wire.ai @@ -0,0 +1,8569 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + Feather-M0-LoRa-Wire + + + 2017-12-12T12:23:02-05:00 + 2017-12-12T12:23:02-05:00 + 2017-12-12T12:23:02-04:00 + Adobe Illustrator CC 22.0 (Windows) + + + + 256 + 152 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAmAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A8uYWLsVdiqrbQiaQoTSi SPX/AFEL0/4XFVLFXYq7FXYq7FXYq7FXYq7FXYq7FUw1GO3WGFlgW1kf4khDO7mIiqvLy2DPWo40 qN+IBWoSl+FDsVdirsVdirsVdirsVdirsVXKvIOa/ZFfxA/jiq3FXYq7FXYq7FXYq7FXYqjtHeKO /WSaL1okSVpISSvNRExK8huKjaowJZBD5m8hoE5eTwzqAGY6hcUJpQniVIxVKZNa0lpGYaDaKGJI X1LvavymA/DFUVYNb3wY2+haf8JoBLdTQlmP7MYluULt7LU9PEYqhY9T06RwkegWrueirJeEnv0E +KolZLRrWS6Gi6aI4iQyteSrIadeEZug7j3VTiqE/TGlf9WO0/5G3n/VfCrv0xpX/VjtP+Rt5/1X xVttV01QpbQbUBxVSZLzcVIqP3/iMCtfpjSv+rHaf8jbz/qvhV36Y0r/AKsdp/yNvP8Aqvirv0xp X/VjtP8Akbef9V8VRd75u+vKi32nx3Sx1KCa5v5ApPWnK4NMCoT9MaV/1Y7T/kbef9V8Ku/TGlf9 WO0/5G3n/VfFXfpjSv8Aqx2n/I28/wCq+Ku/TGlf9WO0/wCRt5/1XxV36Y0r/qx2n/I28/6r4q79 MaV/1Y7T/kbef9V8Vd+mNK/6sdp/yNvP+q+Ku/TGlf8AVjtP+Rt5/wBV8Vd+mNK/6sdp/wAjbz/q viqP0zzF5XgWZb7yxBdCTj6fp3NzEVpWu7NMd69qYFW6rrPlW7sZYtM8vDTLjiP9I+uTXB+2h+y4 C9ARirHMKHYq7FXYq7FXYq7FXYqjdHgkuL9YI+PqSxyonJlReTRMBVnKqo9yaYEshsfys853hBjt YREd/W+swOlCKg1jd+vbFaSaXy5qEcrxmWzJRipP120HQ07yg4rSa6PBcWUSK7WqzQzieC5jvbT1 V3QsEb6wgQn0wOVG74q3pUE2nW7LbS20N1MvG5uGudOnBWrqYlSSUfBIjjnU708MVWyW05+uei9v DBcPMVsVvbH0SklfSDESg/uSeS1B36U64qlH+H7/AP37Z/8ASdZ/9VcVT2X8q/Oa28dzHbRTQSIs glS4hC0foKuyj7tsVpItasbixa0tbjh60cHxelJHMnxSyMKPEzodj2OKpbhQ7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FVWEErIAKkqAAP9dcCUybypr8aSPPZvAqcR+9BXkzyLGqiu1av3xWksuIXgnk genOJmRqdKqaHChTxV2KuxV2KuxV2KorT/79/wDjDP8A8mXxVC4qyK4XTZLGOwt3gMkkVtLE9IYx HM4jSVZLhgJGJd3JUvwQD/gAlZb2+gw6Nq0M8nPWIl4xtSNofhuYl/0eQSfESvIk8fs9NgeSqzU2 tjpPpt6H121uFiKwpCgVCJP7uWJnN0p4ryeQ1XYAtyJxVK7eFHhumbrFEHT5mVF/UxwoQ+KuxVF3 n+89j/xgb/k/JiqExV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqsCswkVQWZlAVRuSS67DAlk8Vj qNvdJdXtxHcz3LWkfO2ngmHpJKg5ScOZKgxKvMH7WxNTirHtX/46t7/xnl/4mcKEJirsVdiqNsdH 1K/RntYTIiHizclUV60+IjAlFt5R8xqiu1kwR68GLIA1DQ0PLfFaW/4V17/ll/4eP/mrFaRmkeXt etb9Lj6mrmJJGCSGJ0YiNqBkLEMpPUHFWS2GrebFkRbbylo0lyAeLpaR+psp5H93IvataDFUqlg8 8SSPIdLs6uSx/wBB087k16mMn78VQc03m6GQxvpUHJaV46XZuNxX7SwEYqjtK8yea9PjuIjoFpdx 3HEOk2mqi0UMOkCw8q8/2q+3fFWr7Udb1PT763/w5a2CmJTys9PMMhInjNOZDN9xxVjP6F1n/lgu P+RT/wBMVd+hNZ/5YLn/AJFP/TFUVd6LrBt7KljcbQmv7p/9/Se2KoX9Caz/AMsFz/yKf+mKu/Qm s/8ALBc/8in/AKYq79Caz/ywXP8AyKf+mKu/Qms/8sFz/wAin/pirv0JrP8AywXP/Ip/6Yq79Caz /wAsFz/yKf8Apirv0JrP/LBc/wDIp/6Yq79Caz/ywXH/ACKf+mKu/Qms/wDLBc/8in/pirv0JrP/ ACwXP/Ip/wCmKu/Qms/8sFx/yKf+mKu/Qus/8sFx/wAin/pirv0LrP8AywXH/Ip/6Yq0+kasiM72 VwqICzMYnAAG5JJGKqELMokZSVZVBVhsQQ64qyfRNf8A0hrek6c1jb21rNfW6yLC1xTg86s0aiSW RUUuQxCAbgYqlHmqGOHzPrEMY4xx3tyiLuaKsrADfFUqwodirsVdirLPMtukfkbyhJw4yyLqHNq9 QLgFfwbAlieFCM0lA95wNaNHMDxHJt4mGy1FT7VwJAekS6ppfkjSrOCO1+sX0qv6gZI6GSkYlDGQ SFeDVAZVBbevw8QuuMJZybNR8j799q593Jr1GbJxnDiIjw/XLnZ7h7lulajoHmqWW8k0lDqkARfT CRkFnLKlAgQShjQOXQlduPSjEYZYyAJeknqT8/KnDM8kZiGQ2JHY1X+aaqr6Ub2WW+j6Uoiureyj gLSOEC1LIOvUtypRqA99822bRcBlEyEqHn+L8nrezjDhh6Jcf86tvffmmEDz2V4lxbCIyelJGRND HOpDMjfZmWQA/B1pmtz6OM8fXYjkSOh7i7iY4sgvuP8AvVZvOt3brcJMloHWLmwXT7Q8V9VF5Pxh 2Hz/AIZr49nxP87/AE8/+Ka8+THjIBvv6nbvUL/zFf3Vn6TraelI0VWitLWNqeop+F441YfQcOPS QjKxxX5ykfsJbZ440CO+PU94Y7rtpLeCyt47ea7L3Klra3UySuiqzOEVdyeAJzMxTEbJIG3V13bo Jwiud/oKr5s0W9utJt2trdbwq0jWaafEziK3hkkExfgnwheUQap2auSx54iVE17z1PJ47TwNihMe k8XF1N7V9qOXTUsrC2d0itrd7eKa0geP07pQwXlK5Za8ZKSAtXieI45DxBImjdGv2fDZy+zMeYam MgJgiR4ifo4On7EPM2lNY855bX96sv16xcL9cfZvR4A/Hzpw4DalTXfpPGJE0AfJj2rxnUSJ4+My HD/NEa/i8/Ii6Sny5Fb21oYvrdjFcmUNfx3jCjQELxiAdWqR8fIKQQaVOZeox8Jo2TXToet+bg6q +LlMituHofxXNrQbNFnv5LB4bS5latj9cNDDb82qrhlkUO3wU7kVocsngkIgyBNjlHmD0J8k6gmo 8QMh14e/7Nuf2K1pZ6auv3MkUUcVwy8bUuKWqXAH72YVDhYyQ3pVBFe22DwJiAMhL3D6q/Hz3Yy4 /CF2R1/nV0Hv5cSL/RWntqdjJqFobm9mlSJp7ZS1rPMAQCq/CGVecXq8VpX5kGoY5CMiTwgdP4q7 /dsd/wAAx4zilw2O6/qrqPf/ADW9c8uib0Lu99KZoQZpGilWEz2nKiiEGNEkdWR14oeW469oA+oi IkenLkfOrpnprIlwgxHTi79/s5J/c6Df2ca3F3a+lZvIyWhiQxxxqhdXiJ9JBzqqfCxLVDeBzDjn hM8II4qvnv03/Hk7jsDHkjn2E4jgPHxjmb2r7+jIdS/LPQNQ/JnWPNVqss+o6bNHzsUSNGQFl5y1 4eoWk9UMxqV4rtmXhjdcN24GTT5RllKd8fHK+fCRfTy7mF+VtAlOjwy2+ntH6XObVbedCZpIh8aS oWjrREZSrLxWprXKc+aIlRPPYe/lXvu9ubXLT5zPiiJ8RMfDI+keZ+POwdkzXQ9Wu4wdMsSxWsks 06FkeBTRlB9KQEAg+pxp+zvlYzYobTO+w58j+OTs/aCOWWWNic48O3Be0ut18K+5uLSbi/WRNJtJ BOf95ZJk9QKQKsBVJKNQ9aH4a4RKELOQ8IH48tu/4M+2IajwMIycU/54hz4q2uunPu3RAtJLa2U3 FrLDxQNKxqsValWZCyD92XU8Tt7bZkYsgO1g7/j4uz7HnOGCMcsqJJ4RL6uEdD5gc+7qxHzLqOm6 jPFpMkc0PpvHLcS7ER8vhq5p9lVcEkA16D3yAHB13aHjDhAoXaQ3EMfp3MN1DMtxZxqI+bUaSIug VeXD9kEFdvs13oFGF1qD0+6jsb+2vYrab1bWVJo/3i/ajYMP91+IxVfqt5BqGqXl+1nNG15PJO0Y kBCmVy9K+n2riqF423/LNP8A8jB/1Twq0BaFwn1ebm1Aq+oKknYUHp4qrapaW1hqF3YNHJ61rLJA zGRSOUbFa0CeI8cCpfhQnWt3NxJo3l+KSV3ijtJTHGzEqpa7mUkA7CoRR9AwJSXChHaJIkeqQM5o CWUH3ZSo/E4Czx/UPe9X13yzLr0MF9p13PBd2XJpZY0LSK84PqV9MhuBZah6AANxNTudX4oxSMcl UeXdXx6ju61fkD2nosunzyyQiZwnufIqGk6DN5ciuL+/vJ5tRu+Bt5JIyJHeGnBR6jc+FT8T0Iov HY5maTH+ZmBEDw4/UegHX4+XnbrhjyZckTKJjX0jqT02TWHQkmNjbabcm5kuJ3t1MkTQpVCnEhyW ry51PcfibNRrwJTnkjwgREtpCXO72+Hf3+4dtpY5IYwJV6fx+Nt/tQdtB69wEkZYwVYA9anag34j enc5PJMRgQLO4/T7+9u1E5w9fDddxPUjfl0G+26i1qILm89Jlb11ELMyq2yPUEf7eCHBKI4ge/Y1 z/zURxzmRkBETKIsEE9/nHv7kOlrAlqkcdWVAvAcRvxII7U7ZYcN5DxbfLZnAgYgIWQKrzrf7XXG myzJGZrfhHWqmT03BDoy/ZBr99McQwxMgJWfIV1HUguuy5TrP3YBjwyNk+TIfLWkW2t395BeKzm2 sriWGKN44YqSmkoo6S051BrX7+2u7Sy/l8cZw2E8gjuOI7Dblw/j7asulECATYgN9u8yPL4fFZ5l tY7PVr6ytI2t7fnIsschWRZX5twlZVCCidY1NeJ3yPZwGTFCeT1SoHahQr6eR/zpfxDZzsEPEhcJ ctuR2IPv6cgOSDtxpMw5x2b8iY542luEV1aKSSZ29RYk5ORIVQ/s9fHM3LiliMgSDQkNof0RGNC/ TVWdzbrPyHFCMzL6zH7fvSeK+0Cx1O2e4guxLcXl1dp9Wl4vKJOPGCV0jMz8ezchWprStRgHHOUC LFcIG47uvOvs+bX2hpsunEY4/UZE+Xu+39Nbq/lK0uLnS9Vvb+H0jpdv9eMsc9D+7Zj6JqZnJdI2 o4+xt8L9My9TqzjOMRIvJYNxJ5mr86+40CGvNrZQNEDfun7v6XmrWOqWcmr3t3NbvLZyo6CITcXX lJIlfUeJmLDiPj41b/gqnKcgxRMZAS234fTsI/w2P0juGzusGHiJj3e/+fP9Q6oqHXFTUYLiC1aN bakoiNyxikmjSkZ9OKKAItSeXU7nqTmNKBlAgyiTKxtH1CJ52ZXfl0G22wbRp5EkSjXceI/j8dVt 1qVxf2lpFNapAtmHWNIXnZQrvyAJcb06BqciKVJoMvxGWOUiDxGdcwOg/q/GuV3VW2Y9LAfVzP8A SP7F+oa5qV5Zi3uD6ipI1wrFauZGryLOIy5r0yjFpoRnxRFGgP4qoeXIfDzbZQ8OJlEWQOXf5PTv yO8ytcarc+UdTuEvNJ1m0ltwyrwowUhUarP/ALp5LU77qvYDLtPAQkZDYk/j8e89XncmSWfD4shw yjLlv3CxuByO3casMF1tNU0TVdR0WdgGtp3ikoiKG4RtbqygKKAxOaU8a5KeGMpcR5+894l394Dv dNjhwRI7v2rrTV7+PRggEbwW4MaK6szU5AjcGg4GT4R03O2YU9Jj8XqDLfp3fs+5qyZojOMe9y36 efxP0n3bd6hbeYrm2NIYIVjDtIsVG4KXjEbADl+0FBPvls9FGXMm6q+uxsOWcQPNDXerXd1HMkxE nrCh5E7DiEKjrtxUDLsemhEitvx+1oyaOJkJd3Tofft0s8vixLU7aCz1Y3bXl/Z/XJWSE6eBJLKS EEMUn7224gEH7NcygDw30H4/Q83rNIdPRkRRv7FPzBe39pevLf67rdsbhecKekjAICFABS941UUF NsjGYlyddh1EMlmJukutNVW6uobaLzPrZlndY4x6P7TkKOl6T1OSbm9V1FtPvryxfzRrMlzaSyQN SKiM8TFdm+uV4kjrx+jFWk1DzCPJMusHV731G1KK0QfWJdlWCSRu/ckd8VSK01OS41myutXuZbmK GWP1pJXeR/SV+TKCSW8aYVRPnPUNN1HzPqF9pvL6ncyCROahW5MoMlQP8vlgVJMKE21j/jl6F/zB yf8AUZcYEpThQiLD/e62/wCMqf8AEhgZ4/qHveu2M1vBqVubmynurb6xD66QxtJyRm3BI2GyEdds GomTCo5BGfAavaj9vf8Arej1Orw4TKE6jKRHUb3sDudqrfudrOo6XNql49jptzZWUYqqTROAAigO quw+PetPHIaM5BiAnkEp+V/OuTXh7R08zKIqcogkkcPLr1vyZlBqVpL5l8r20LXfMlJ2S7MZAP7p HC8CzV+FeVdq9P2s5yeOY0+cyEN4n6b/AKR3+f42Y6PtLFqBOMOgvl0Y7cQ6PBpdvLcW91NLcW7z O8U6QqtJpYgADDIekVftZutPLNkzmMZRjGMwN4k9In+fHvdjqpkY578gffytJvScRCjKDwp0Pb/Z Zn+sz5/xef60iAGPYfwUpXR0y3DSX1vPPRKxi1lWAjiN6ho5+XanT6czdH+YlCRhMCu+MpfKpx/T bpO2ccIzjQ6IgaWxlFtbCVYZ2t5rKES+oazxu+zKqLyqTyKrQ9/bGOsjOBnQ4hfFtsaOxqzXu3+L g9mDH4nETwiud7/Me/kdx3LtN1fWNJEjWl5LCl16bKWCn1I1do6jmhqAeXTHUaGOoEROFmHFy6Gg egFH6XZRljJnc7FxG5v0k0efMc115e6pqt9/uRmlnMJkiIUmNhFGOVB6QjJ4t/lD59sp/Lw0+L0R riAPxPvHVl4c5jhwyoRmaAPCDH07WBL7j+lAs0RZI5bJ3dj8AUr0UU2kfjxov7TKK9RXLxmmBcp1 R7uvWhyPPlbRm1eCGMwlHinXDYFgkDh9J5nfuGxQttqsOn3sUukUtdRtpXWBERZTHIVKOBQ/FQN8 Y3qK9chk0IyRkJGE4y34d+/uHL4HZA1+CeKMQCJV9VDqOffzfQFx6pJtJmhdpUT6zasVlVUJKSUB CMwkO2+3ttQ+cY+EjiFiro8rPMd/If277YEjLGQJb8X2fw+XM/f5PKvN+s6dJ5kdbXRbOKKxeSC4 R/WHrFJ3Xl/o8tuF+yaV5dc6jT6XJHSwMsspce45en0x29Ql3u902Gc5Exlw7f76fn+LYrpqW8mo 30ixCOdmX1xGZFj2ReAQO8rUCt+0x3+jNvpwYwiSb59AeVe7vb8GIDJONmUxXEbIu+Ww7gmovXhi a3EiKpJNHPJgTSu/NetB1BzN/L8ZEzGR9wof7k/Y0ZIRjk4hOMSOYJ4r51dyB6n9NhqMXDgUZQT+ yUNa+FOWQyeHE1RP+cK/3LnQOWUeLiiPfE7e/wBaYaJf6vpmq29/YyotzaSJKpZKCsbhhy5NSnID KZzxVvGX+m/4606rEZRqco109Mr5dKlfK/hd7W9E/PjT4JtR0nzdZL/oPmC0jZ6UqJo1H2iO/pso p/knDGIkPx+Px0cbs3MYg4zvwn8fr/W8wX4qcd69Kb1rkCK5u4EgRd7LriMwGjkGoqpUhg3+qR18 McXr5fj3tMNTCUOLf3EUb7qO9noOuzVxqMd0Y46gOF+zzBYkU5HY1A9hsMsjpZYrJ5f1f1j7S4ml 8MEgSuUjyuII8vTzPedyepSPzFq82kCxu4kDslwCyNRuS8W5AFgxUnxGOTGJ4z7x5d/cXC7aJ8MQ /nX/AEvvHu6sW8w30bQi0itri3R2e7f60qo5MnBAFVQBxAj+13OUY4087jlKczORiTsPTy2/t5IT ymyJ5p0d3YLGl7bs7MQAFWVSTU+2Wt7XmyRJfNOsyRsGR765ZGHQgzMQRiqg2tam+jpozTV02Ob6 ykHFNpSpXlypz6HpWmFUFihzfaPzxVrFU21j/jl6F/zByf8AUZcYEpThQmHl+OGTXtNjn/uHuoFl 3p8BkUNuPbK8hIiSO5njriF973DR7qB2YJAkLmnpslXQPvxLJIXr18f7cDPGjub28v0NftNOA1UO IEjgHIgdZeRCnrctukCyXMiqwYCaUAIGYA8giKoAP6+vfDh3JrucTsXw5ajKI2BLFIbkf0f6oQ0O tWa+YdI1aIm5SzT1YQlVVoJGjZORYbOfTO1Nu/asMmkM8Usd1xj7Xoexezhisji9WON3yve6/HxW XWsSLYRwwQgLFbtC3qGpIMksvKi/DUer03zYabQwObeX1SB2HWoxrv6c/sdhq8MpR8UipQJI3HKh 5Hu5AixsSgGJMfzWuXxFT+LsDK4f5qjcQwTXKrcOY4lid+a7tyVloKUb9mvh88ydNmy44fuwCTID 8bh0nbmOUiOHmIyPyHvH6fcVlpdi5+rp6ZWVChUsFXgbeBU5IQz0rv3rg1GCUOMnba9hz4pS8/hz 3DjaCURwCUeISlLz3qCZy6pFwsELJC9tD9WuTOnqIrPdSt0CydOQ3C+P06wiMeOW5EpWK2O0Ij7S D1rq7nT5fEgMg5So9eUjQ7uSzU9atTqk7rdwCImVUuAhjjBmjJHqFlg/m/aI37muR08ojCByltz3 5HpzHT5NOtzSxRB3AEgDzNbXZ4bJ7gBW+58hkvlhPrdj/uQn43dxHbwvGEbiXcxcyBKPhXjTiDWn wlRkD2oZY5fu4kCJJ3PTfaxsd+fLru6XFLHmz/SeKPq3MSByPKuVke+kvfy3qFlpeqW+kXCX9608 E0MjwcQWvIPW9VZpiQvwvvzUEAfE1OlENUOM8YMOES67gxmI16efwJ35Lm0mMeuO3KuVb35XXcL2 8+aA02XVRql/ezXt7A9lK9wLqASM7lLiY8ZKq5eIqo+Fl8MycuO4DHwemXIe8Dl9nL4OT2jjlLGT AAmJvf8ArZOW4/stO7zyjqq3drrF4jXV6jTmS3KxM7SyyJCxk3HxiSXf04nTl0IBU5RpdXjymOK+ HGBz6ACJl/uY9ZA17qXUzkIYzymBvRI3PuII79jzPmWNWvl64066ks3eOF42keQW7GSNvUYGMq5o SqxgU275tdDrIiInXFfEKPSuD7e9v0ujOTGIiXCQLJHOW8hvv0rb3lVSwM16LYTfGxI5cWZtlLfZ 2r0/mzeZNcYYROMPtFfj4Oo1+IYZSuYNd/F9vpP6UTpt/NaTLUo5IZY5FJZXr8NVb4T8jtTMPUae Oox8YBHeP5vX5dT38/fn6fKZQjjyfTKiP6W/0m9v6O+wGx6EG9sl3dTSzQU5ljyHYsxLcaGv8vfN Jk4cYAk7jNmwYYRjLkB8uH0k7V31tvvyq3qflWJfOn5O6p5flYtqPl6X65ZPszCNuT7V3Jp6q/dl wyGAsdOnf5OD2h+4y8YGxHu3+Rr5PHLiQWkjqzcghY8kqahTSqhQfuGWEicOLy/F/o+Xm7PFl2BI +oXR58u7vHUD3juQurGe5tXMkp5laKzFjSrdKqynue+HQ4RLIAdrP6+4juau0IxhglwCvd8B923u SKy062ju4hDqrPPKX52bKzNGUBNQ3IlR0+19oGm+X5M88eWUeG4jv+w/i9ti872fi8afDxcEunPu 7+Xf16eYRuriCxuNPubqT1IIJxNJyHQKCKqDWrVYEDLdRnGfEQAIVX3Hy/HwZ9raGeOIHGZGQPP4 eaQ+b9Z028SGK0uXvmHxNcSKyFB6cacPj3NWiLmlBVs1WKBF7U6nS45AkmIgKAofekWkf8dWy/4z xf8AExl7lu1f/jq3v/GeX/iZxVC4q7FXN9o/PFU60u4sltDbxxpLe3KLFCskCzMty1wh9QVRyUMF U4ip5fs98CWvMAUWejhFZEFtMFRtmA+u3FAfcYqk3FvA4UI7Q1b9NWGx/wB6Yf8AiYyvJ9J9yQz/ AEO2lvLKzd6yzmINUrzlJBJrU79q5n+KMeO6+PTkP1u608IyjGU5RiAOtX9UgNzy8kVe6YDCsTVj mjkZ3dR6TgOFFOQ+IVpvQj7icjPLHIBQofO/uZ/kIZBxCUZeY2/WC0wkTgBNsBQKoQVoVGwChR8s YiJIHDXv4vLzcoxljiAJnbkBw/8AE/pTaG+0ePy8baSwSS5CMTqLTPH8NSd0JEYou2+YepGUao5O IxAP0UDuB31fPdjiwkYPr4sfD/Nqx3c9kFcwXKOIeSAshIZCXFK0FCQtfH7vHDinxXM3zHd+op0u tGph6KjzG+/SPcenFve4OyFFpfzarDCrNMJlKJCqgkkinEUWpJOZmM8OnlMGuEg8x8+X4pq1G2pi Mh9Eokd3TcX+Oa3W9MudIDwSW89ndOnqhXDpKCWEYdQ5VhUEio265Xpcw1IsnxBxVYoj6TfLa/pP 9Hy64+vw4I45RhXDVkc+Uo1zvvPvaik09ZEYclCJwQfFQAEbU/jl0Oz84gQB+vz/AB1cj8/peMGy KFda8tvx9yJvLjTI7OIKkpnoXkmryVY6Nu9WrxoB28SfEYuLjhl4ZGo338v1G/g1anOIy4pEcEo+ naz0A4vKVkb/ANGqNqCyQG6lNrbCKM0aOEOXIHMfDzYkn5nNlq8JGGInMn1CyR9tbNXZs6ySEYgn hvY89xtzIQo0nUp2uCLVpY5KBkojLxEZSho3j8XbMGfgxyGXHzJ/h573/OZZJZIx4ZwiLAq5x6R4 fvIK1LOW3eWb6pRW3b4h/vxpP5z/AD4RpsM6iDv/AFfID+e3eJnx8Uzj25/UP5xl+ldpcd5c37Jb RLM8SF2hkFVLNxUHkqznkPUXinHsTh18uAcB25R51y7/AC7t/m6TUTBxmyPUeLevefVxAi6rer97 Vlp9k3GZlFsiiT0Y1IVQs0rPTeu1FHH2/C3AcmIfu/Vz6eUO52Ol0uOeKPjSrhiOtc5T7/m64jtV fiJG4hloQoYdR35DNxjyZpQBMRfvI+zhcHPiwRmQJSr3A/bxD7mReVLq3sfrSTXcdpLPJBFDctDF cBVcvzfjKQOAopZhmp7Xxyz8MoRMuESMo8UoE1VRuPU78IPvTEeFcJ7xPIjl/WHemeqa/FYwy6fp ssQt7qe6Bjjjt3jMJmdU/e/EykIq8ePam+anD2Yc373IJcUIwuzMS4uEEnh2BBJN3+x2Onz8M4xl v/NPSug94+/Y946h5D81+WtK88GKz+qQw6jcw2M0sMgKyRyQyGPiD8AAmVFPDx3zR9mw1EDDxeOu GViQ5Hijz6+6+nJOXHmy45GQ2G8f7bvl3geV82N/mN5R0aDzhfafczx2ZSKOVLrnHHQvNIFBhXjy JiKVK048anrlsJZtPIiIMoiUgBRPpMQfq6dRv1NCqcqOpl+X4hEy35C/LqN9jv1ed6pGsVzdwW1P qsodE4Mr0WQig5gryoDv2JFc3WKQlGEp/UKu7HKvfXLbutxc2hnqoQkbhV7cI77POiCSOfOu6yEq t7s2ifUlkb0ZpndpOC8eaLQrzLEVH39tgMzc2LjyHLECRPSya9+wPuHx5OJpcePFqa8QxI2O0R8z Z7tvO+pQ+q3UNo1g9xMZbKW4Ed2WCMhUqdnoo+En7Xt0zHzYf3ZuPDOxXO+Rv7g5Xa2oyRxfup8Q kJD+E/DYe9jfm+S2dICZrWa8MknH6iytFHb0XhGSqpurcuPenU5j4b8/i81pjcjwiUYbfVzvr+hj OXuWmVvPpQ0+OCeKs0ksnrzqCZI46RekyVZVYgiSqnY17GhClTmsRp+qra6kkgjieM3KKjxyemwD HisqxsCVO1QPuwKjrwQ3Njc3FrbQmCIqWMEUwMKu4ESvIRxBUck+Jjyr1Y0IVSRvtH54UJ/o8mnQ W0cscUD6gg9ZLmeeWExzRuxEaiOWBhVQjI4rRtjtugSo+YChs9HKOZENtMVdvtMPrtxQmvc4qm3k bVvMWoea9JsX1a89AzqXjNxLxKRDmVpUihVKUpiqFsPMHmmDzFb2k2r3jGK7SKQfWJip4yhT1bpk Mn0n3KObOfLev64dOtJmvriRvSU83ldxWrdQxOZGcAwq/wAVF3+i0sMmMCUbuPP/ADpfEHluP0Jn c+YL62smuLm/uo4pmZJCk7RIBt9o7qAeVPw75jxiBQoEDv3H2uVlx4MMKoRHLlv8xv8AFLL0Szzr L6kzHiAVndpGqHVh9rjSlOlMvhkiKHDEDfkBH7gmOmPCJQkSDR6nqCD6pfjyQamGOREYziSZXpEw Jg4qDUoxVQ37OwY969MvOcyyEEg0Y9KPT8H4Vs6XTZJ/mZYuUBE7cXENxtt3c+fl5pm5sLSaH6iq RqF2QxKy8vs0KurKSBTt8hmHp+PhlYv3Ej/c07WOn24ctRuRqq35eW39Xc95KCvLs/WfWNCFZDIV VUCEK4BVUUKDUDt75miUvDAAoysDnuPSeZu+vM0A1nFjhKvqhEgm962mOXyQ+pkenLJxrKiFWcqW +06t23Pj74dFcTG9yTY69JAn5/OvlDtGEBCXBHhoUdq34oEfYjDb36i3muY1jMkZJhqXKseJILhe O3t1+jMDT5CYmIPUfp823Ta6GqkJAco8uf1HY7A/zTsaPl3p0dmdG3RwysvWopTiw4glfiO38DmR LDZEj5fH9F7d+/vDOeOM5bi+ff8AbHawDy2PDsQdxW0+2urvUCTwULCzOKvUAEGvErX7vuyeqnWC O3p4vLn8Ovm48NQMeczmfVwgcI4r3Ir6qH20ndtb3cSzhJIkIqDyliVgVbhX4yGXqd81U8kTVg/K Xv6OxlDHnEZkWOY58jvv39NvJBX1jcpaSNxVlKmhR0cHoNuJNdyMzdHnj4sb2367fe16vNGcJ447 z4eVHqD+oo3yG2oC6vBBYm5dUjj4erNESruincIwHwk/FWiip60zG7aGIkmWThjxE3UD/CT1kL9w 3PLvdeOLw4iUOXfx/wDEn9XwSP01kLCZG4bcKciSe/KgH4jN2cpgB4ZHF1+kbdK3I99E78922OEZ L8WJ4dqriNnqZUAb5VYG307IW6tljR5vRcW6MtZCwUdR0DCrGp6CuZUNaTUTKPF7r+0GnT6qGLHk I4J18vvjtyPM9EXolnb6hqdjZSF/QuLiKKQAjmiySBSymnT4vDI9oZp4Mc8g4eKMJEbbSoXR357b b8rZ4xDJDhFkDpe8fMbbxN+oUN6Rkuj20Wu6vZyu9vp+nSTOJBG0zhIp1hUItV5FmdR1Hj2zDy9o n8thy7HJliNr4RvAyNmjVbkfIc0afaUoHoRXkRIfeOfwZhpenXMTkNdzXUKPbu1y8zwSxW8kAuHk jiEp5SRo1aUfp0zjtVnBPEIiEvVQERIGQlwgGXDsCRz9PPm7/FljwxjtfDdfZ8mc/m5pa+Y/JOi+ fEIinW2jivkKUMhdwFNdqAOWpt0PbNuc/qjW4kTE+RAP6iHG7PPh5pY/k8e1CyW2EJEol9QV2p0q u/U7GuHHl472qnM0OtOcyBiY8J/X5Df58xulF2yerDFMf3MbSGvf4wG7eBO345u9DgkISnj+sge7 nv8AYHS6nSYcep4p3wzkSf8ASju35/jmlOvPaelbW8SrJFdTrDMrgmgYEclr0YdjkdcM3hXl5giu Xcb5fBsEtP4kYYt4z2ld+VEX150WK69oiaY0RSUyJMX4grQqFpSprv8Aa8BmrxZeJq7Q0IwEUbEr SnLnWp7oCaGYlF7HFLLLIySPO7qsCAIY5AiSW5kDEyBgGqKA+zBKhqM1rea+kt3cySQTNB9cuC7T OtUQTBXfmzCM8lX7WwHXFUXqVzYJowg09Et0kYLcqlxI7z+kQYpZELhPiV2+H0wVYE7cuOKsfb7R +eFDWKp1fqj2nl1JCBG1swck0FDez137YEp15YS007V9GurKdI9RS4tFZ0njn9VbwtHMnp8R6ZjW qnrSta7qcVSLSb+W4812eoXk1JZr+Ke5uPs0Zpg7v8A233+EfLBIWOVq9b0/StPtw0nq217JbmsX BC5RY91QM6qeXf8AtzH1kzKUfTKFxHPr57E/gOH2vI/mYxGTgA26gRAka5c/xu1qOmaLd2Mb3EAj Mz+pWJRFLEZFPJq8SeQHbKMUiJHrQXskDx8vFPxBHHI9e8b7ofU7W00+ays7ZGSP6u5jHLnySIxq rMWoQ3xdBtlunmZ8z+P1PRdh9oHMTHilLhhHaoijvyPy5siv7i2ueWmvoovJYUV6mR3RAY6H7Kxs I6yA7nY1FaGmYGLEYevxOGMj3Dv8732Pv222dh+SxxJykn+L7a4tvOksmks7U8n0niHkJiZirqAI x8CsUCuoJruCdxWvXM+EBM/UDQ32onfnV7Hy2GxquScInOIMch393p8je+1Vvv3qOp6Naw6Xp3OR IZbgTied1chmjn4JQIG/Vluk1AOacowvgMaiCOXDZu/n/YjIZkGEpWDQuh15cq93lt3taNptjNqM kctzFdB4bqYxR+ohDRWjlN2CftRg5DUaiccfFGMo1KEb2PPJG+V99N2X0k77k3XXlw96SIbG35en H6atG8jkMB8KKWNfi6+HfNtqBkjHikY7HuH6v7XBw59NjJEIzHEN+f6/t6IezvhPFFN6dwvqyMqG cIitGFYqqJR2agAIP+tXYDMWGWUsoAND5b1vyPf093ucXS6ic8s73x8gOu3Ibg2RZ4u4USd1a0lj F2XBCD0xQ1A3qPAAZtNRikdNEUSeLz7j727S5YDUyJoDh8u8d1BNpfqsVvHIlykhmX4kFKrQgkdf HNLHHkMiDCQp2OHXCc5RIERHkb+r3fsJrqoNOrR+j6gKMwJSuxPStPll0MUonioigd26ZxSN+ky5 dOVjZMfIHqNquoSRx1SNONzIqq7Kj8Y6qiwTMT8fTj06kDfMPtqI4aJ3Mhw8+fDxczOI6d/uieTq jksHh9Mu/wBNfH0/f80iMsLXEpYB7eo9AMyFh/PWrNT4ug5H+A22HT5oR2JB67T+H8Pv6McGcSvx +GX831Y5V37+nn7vJZcLGltJOIA1szKjqrszF+aBAERWH2n8cE51ljxyPEBzMeXPvonr02dfqtRp I5aOIE9DxjuPQHy6fpUrVvRdpULKQlQjApKhqCvJTRlJ/Z8cy9TrI5IAdeL4HY8vxz2cnQ4ogynA 8UDE+8eobH8btpqGrzanLcz3lwZYWZYCZZCUDuoYqSQQWFAaf1yqGix+FG4xojlX9A/jrXTq1g/v 8g8/+nkU9jvtVNrG7yy8OXNZjWvMMTUSda1Hjmpz4cYySAA93l7ndaSGG7BHiVuL8v5t9x7ur1n8 q1HmPyJ5i8kyzCS4mhN/pyVHwyAj4ak1FJUjJFP2j45GJskU4WrzGGoE+EgRNX3/ALN9t+h7nkV3 aS26I0vw8jupqCCKGhqB49umR8QEkdzt8OqjklKI/h93n5306gXzFhZdQ2P1eGUzK5b7aNSgp8/C vXvl2HVSiSBt3G/x+xxzE5MhGSNQj37g9346edio95ot4lGnMoRQLyIMNxWtadKeHiMuy6icsREi T3fI/rcTXaOAlAwqPqo1tz5fcUl8+TLNLZSBESkfCict/TSNa/EzdaZgaQUCPx1cLtLT+Hw+riu/ 0MUzMdW7FW8Vdirm+0fnirWKp3e3H1a28uXHppN6Nu0npSrzjfjfTni6/tKaUIwJTrT9Um1bU9Fl trK3t7a0urNL947e1ib15bp2Tg0aJJw4UWg8N99yqxGxFb23HjIn/EhkomjagWae1+WbOOXV4oJL 8afBK7SS3EoVo1KKzb14n4ivH7WYna2eXh8QhxyjGIERzI+3vd1quwcGWRyy4jK+/wA/cqfo+fUr +9sVvHlA9f0Xt0UtIVJC+iBQ1I3G528cqmY4oCVVsLs/TdXxbdOXTdjo+yMGAccbvJExNnapf2Jh qXlW5urZtSa59aSwjktxbxQgEL9XjuZJZG9UgAEKvw7CvU1AzC02uEM0YEVx0bJ6mfDQ9PvO/wCh dFoMWk4pQJNwrc919wamsJ2v7ni8no3IgkS6RROR6SD4aCSIhDXkaGtVGZuTJI4weGIMOIV9P1H3 Gz0HvLf4uPHAgS4zIHaXqH4+9Dappck0LwwCWWSWRWaQK0PpiJWUKpeSUnly33pttWu09ITE8chs Byu7uugEeVbfby3hp9VCceCR8M2d4jh68/K/Pv70taZzeIn1gyQQTkxtMxmRAZA7fA3JSK9R0PfM o4gASYmMpR5Cxe1c7tzeWE+H+8rf6tzW9X9nku1S8mOpIIJoklWIh5LeKKAcZBIpSlvwU7Nue42y jS4eHGdjVj6rl1BB9RsUY/NjhzHN6THh36k77VyIB+Brlbp/K93zt7i81Gxs1dW/0eaYrIQzCjEB SP2DT/bzGxZ/GnLHGM5cP82N3XxHf8GzUazwzGZG3L57/o5IS68sNp0kGp6pq0d1okk7Qi4seUxt g8ZpROPIv8fgdq9K5eNdYnjhjMc0QPqqOw/TfMumz4DjJz49zIk1XSexPPu910EFq1vZwarOjRTW lvyHox3KzJJ6f2uboRE0SsqtxDDp33pmXpddPwR6x8CZXt5GtjXd9hXRaUTiJZR6j9m/6QJe6ufK wokItl9As7cEloPUiYo8RdGXmd1dx8u2XHNKc9jxi/8AfV1seXxczCcfhHgO4G9Hl6DLp59EWsih ZpJHf04uRajOdldh0G/QZknIDEECNn+iP5sT3d7ZAUZGUp8Mf6Uv58h39wTvytbmC/vHmg420YQT SuguR8TUI5tbzJGehpxP+tuM0+u1JnHY+uRsAS4eQ25ThfXr8HD0dZSesRyP86MgTv5bDY7jqlWo 28mm6jc+v6colYyEooSNQBUmNVjUBe9KCmbLDqPGxRNyiB3mz8bld7d5vc+TZp/Dx3KAE4z33vbh 5/wn9Fbc+aBNtJc31vLbP6Deosiy/FJGGjVyv7tVXma/ZHIb/cY9okxhwk3y7r69eIuFqo4+CWfg BHLcnh3ofzBt8URHFzlnuGkEjP8ADWNuUf7pygZRRRuB2H9cq0/EeGyTR2+TsOz4Q/L8cQRxizxG 5fUdyfNRUUupwP5v+Zgzf/5HH7v94XAH+MZP63/TyKeR/WzpcVUAtwdmoKkgnr3oOR+/Obz8PjS/ nO50fgjJsf3nD50OXwBNDzNWyH8vddm8tebdK1mSqWfq+lcN2MMnwSfcDyHuMgJi66hu1XBmjLGD cwPx+PPzTH87/LY0HzjctGa2eoE3ttF0VfVYGQL2p6nLK5midu74tPZWo8QEGNGNb9/P7RX3POJb qFk4yRJxJApIRxqTQdR44IZOI0BuXZZgBG5VXnyUNTsrzU3tra0tIbgtOvN5aMirQ8noSnLiO1cz Mw4MXqJBv6f0/inS9o5qBnCEZxjvxfo6fpHyYv528r3+jXEclzIz+qSnB4jB6dFVlVUYt8PFtqdN wd8xdNmjMen77efnmyyyEZY8M9jV2K8q7mMZkq7FUc+jammkR6w0NNOlm+rpPyXeUAtx415dFPbF KCxQ5vtH54q1iqe6gqx2XluW5iLWxt35hgwVlF7MWFVKn7J7GuBKbaRLo8+saSlusct/9cs/TFtG yhPTdTM5PpwVD0rQq3GnXFWK2aPHqMCOpV1mVWVhQghgCCDiyx/UPe9b1fzZaeVY7aWKF5r+T1AA yR0JARXRvU9TgIzyAZVqd67UzXSxHOSCaj5H377Vz7uTkdpdqZsmY4sMuCMPqPPfyQ+m+ZbXzV9Y nW3aHU09MMsccfxFuSqqiP0+YduPNmWq7U7g5mnySw1jkbxeZPp5eqzZ9O+10d773XafWZsGYcZ4 uLryv+iem/Tqmg0XUJ9KN/D6ZSErI6HkZeIgErcBxoRxlHepodqAsL8hjDIcVniltdDh+uu/nce6 txvcgHfS7U45xmY7Rvr3j3IeDyxfX1/OIZI2CQiVnUsVAaNSBWgNaOKClT9BIvw9sY4YYxIN8QH+ yv8AQfxQcA6gZ9ROQ5cJ/wByY+XUJhb+W9TaJyzQwlI1EiytSRWKxSMojIDl0EgDCmxqDTaus1Ha OKBIjxHiJ3rarNG+VSo8PfsXf4spyCEqrh/bH4LLjy1qkDpyZGMyO6KrDmoiVXYSgqPTP72lDvUG ldq42LtGGQUOLY9eW+222/L5e4uaMosn3fp6qF/5Z1HSoStxJ6whLJ6sJ5LswU8mVAF3NKeIINDl sddDUy4hxAnodunuYaX0xEO78cky139Kx2CvLp+k6jHYBUF6zStyo1AA8dFapl7e9aUpmN2bjx+L cZZoSlvwjavhwE9PtHm63XxMxwR23/iNfj5pd5ns/NN95Uit7SLS7C2Ux3dbCW4MnIonLnSNwpVX XkGZT8x1npxGOpkQcplKxcx5nlsDvRoge+muXHKAxionYXxd3w/W3o+n6vY6dH6rwTz3kh1G6uZV aSaiIjD6wI3X0kKunFS4+9qHI9BkSOLiHoHQHn9Ox4q4Tysd/Jw9RmlhhUImUpbHY/aNzfQAe/ny uzTzTeAX0t3BPbNcGJLRQyq9ZJ5oi0Mrf3fIyUMjU7fsk5GGPDCRiRLjMQbA5UYcXqH8VVtEE8u8 NmmgYws8+HcS5gcJEYj58+rru71jzBFM0s1pbRWLryV39JSksxgG7FmKR1WpY0UH33ztJjwaQgET JmCQQL3ERP8A0x35D1HbkNp6mZhfD30fMcUxR96N8vLq8EGq6dZTaeYLV5Y3mmkJYSSRvGfqrA/E SiPxqK9a5X2h4GTwsk45eKYBqMdiBIH94K29VcVHbaraMBjEkxIECdwasfPmO6+e90suNEvn1Z7O V15LD6pZTFQAEj4VR96vtRat1IByiOWIwiYB+quU/LvjtQ3s7eYdzg1WKAoHbzkCfeSZE/2IYeX5 TKRFfW0pBYAIzsCVQyNxYIwJVftU6dOpplc80hvwSHv99fCz+KcqGrgRfP5frWXflfVIoJ7xru3W 2iHGVmkI4FW3FPRJ5mtVX9objbfLdPrDKUYCMuInblvfXlyHU8gdjvs4Wp1UQSehH3b/AD5+/ltS Cm8leYreSWeeaKJZHkbi8jKEiimUOzVt9jyZVA712OZ2PtXjiIxs8NDnHcmBqvT3WT3dXU7DJKXW Z2HcOIHf7E2h8u62kENjLdRDmEkWMszL8bMo+IRADdW5CopQk0oc0WXtOBnKdS7r2/ZfSu+xXN3e mhjFZOH18NWiJNDvChgkv7Qm3Lh4Q5Z0MLFWDKqlxxJNK/RlmPWwPqEZb1vW2/dvX43boRxxmZiJ uX4929C++hfJ6Z520lvNv5U6NqyuJNT8vstlqLqkrExkICSFQyE09N/s7cjmRLMDj4q2+Hf7/wBL r9LM4tRKJ5T3DyG60O2s+JuLmOHkokVmS5HwsocH+6/lYHKoZoyFxjI/Lpt/OdtHURJI/m89xt79 2OXfmLTdP816eY6zpptys07yRkIUb4AWLGqfA4PIBqE7V75k4GeEwPpMq86Iv583mO0NYNRQrYX8 bY7+YfmrUPMOvPLdWws44PhgthViFKr8TOQrPyVVoadKUyGk0www4bt1kMUYmx998mL5lM3YqyMa rHJ+Xr6UzH1oNVjuEXf+7kt5FPan2l8e+BKTaVbQ3WqWdrO/pwzzRxSyAgcVdwpap22BwqjfN2kW mj+Y73TLSV5oLV1RZJAAxPBS1aUGzEjAqT4UMw80TGXyH5O5MC6rqCkA7gLOqrX6FwJQv5bXi2nn jSJWpRpTDvsP3yNFX/h8VSm/1P61r1xqjJ/f3T3LRg0+3IXpX4qdcUg0ben6poieaNPs9W0i+mSa 2LtLcJE1Q84BkqUIYIWGz7Ba8D8Q31gy+FIwmBR+nzA9/Ud3lfup1OOcMks2OPHCe8q/hl196rof l6Ty7pF/q99eSzXs8kAt53iPJ5I1coqtIalKirN0+Hj16ZelxHVZRCIHBH6t+n6/Lzvk4oE8shKc TGMdxfMn3dKSzT1vJ34KkssTOY5eE5XhHwXi3BWT7ZLCrVHwdN82nbGecMoA2oWPP8ftWeWMZVOf hjhsem7Pd93LvXrNcSI9raiW8hVlAj9VoucVWq5QMjUHwgLXblU9MjqBwmE5jw+Lc8I5eXvPeWeo ziMREfuxLeRrcyrlfdzr3JbaXo/T13YxB5SkaumnpKUkM/w+unrBgrMi15fzcQK7VzA1GQ5ADKvl z8z5/c2Ydfkx4hvwxkd5VuB3gdOLbmn1jNPVI53ksSwWOSHm0xQuSqxiQE/3gCHevGu/bKSBRMQ7 XSdoZjpMkjKxAipmPT+I114ftROoXEqgmJmVpKetaFiwCh6CUg/Ea/Ft347Y4x8a6/oczQ60zGQ4 pePwxiRY4fVvYvbuB+KVXEl9b28qyRSW7GULGGmMgkQBjypyYVFFNVVQOdOuZ/ZY481H1Cj+Pd5O hw9pzlyyGciCTtXAbr0+8X59yrGLyK0lkkmuYoAkbJMkzMsxkADKY+VFHxEAcarxqSa5DFKUtSBw xJMqrhG39nNjh7RmZjiyz4uIgw32iOvFfP8AXs60N3LEZWaaG2aKQyzxSsVjKEqE9JWWo4KpIYEt y2pTBq5ShnqhIiWwIG/2dfsTk7SkZn94cdEcMADRvnvff17+ala3GoSlDbwTTBXKssVyYnReI4Oa MtCxd9zyFFoBvlnacDDJRobXyFf2bcviy1facuKvEljiI7dTI3/EflyRaw6LPbHi91d3LDkYIY1R Vo7KGANxAzKRxruKFgd+1mryZYwgT6Oe/XkPx7tmGp1pkBxnwxLfYciOURzFbknvKB08+Xf0zc6f He3jTqOYtPSTkZFHxIZRcL6jx1bnuKj9o0NYR10/DANC/wCKunf7ztf7HFlll4YkTQJ+qunQ157e 60xhktIb5Ct7fQ3ciBHiMaNIOVfgEhuHKc9ioLEiu5HazHlnLTTHCJRidj95rrwojnlwSkDdcpeX U/Df30uurq2joDd31hz5/wCjGKNgoZBGz8VuXK81+FqH4uPTxj2djE4zPDHIQAbPO9yBy333Hdu5 WPX5DfBOUogCunTl0G3u2tEz3kdsrvLfX8LSGSIh445DIsqcXYk3LcqqoHw8RQhSD2x+z8Iy5eGg Rz7q4TYrbbf8dU6XtCRIuZybHY8om9vf12P3bF8V0apcPfaiXoSl6URlP1pgZ6J9YP2+R24jjxJY tXfHlXGY8MQLFwrb0fT/AKVdBqZT1EY3xTMiOGvpH86/t7vJVl1SR5lmGoajOyNxaYBB9XWKpUkC 4BAo/KhDkgmlK0zGyabDUhwxHw52N+nkHJ1/aGbHqTAHh4SOGFWJWBz77+FIKGSzUzG1e8vJJjxn McUUZCsanb6zGR6m+9SNtxk8YjVH4e9ye3Ndmx5YxM/Cjw2K3s9fltt5s68pfmHpmh6Tr2m3SXms w61b0WOCOOJkdRwWUO0ybKrLUr8VQCuXRoRAkKBTr9XOOLFPIOAyB3HQ7beXXvYhJcWWoxraPcyL 9Vj4srxREgcQvpM5uFVmWnH4a/Tl2n08McfEndTltX+65FHYeTLOMzEAkncy5Eb0a5+rcmzXuYrq DaHYwzamuq6na3E86LNPaW6RswMbenFtdqQlU3+WZ2p0csXPk4YkCgbvzVHN9Yu5PMutp9aIW1j9 NfgVWHxAC86ALw6Cu9OhGYiUB/iMf9TVrv8AyK/7PcKu/wARj/qatd/5Ff8AZ7irv8Rj/qatd/5F f9nuKoO5u9Hu7mO6u9c1S5uIqCOWe0jlYBTyABe8bYE1pirWuXPl3UdQv9SW9vPrN3LLOkLWkSpy kYsEL/WnIXeleJ27YqldnqVxaKyxJAwY1PrW8E5+gyo5H0YoRsnmrV5YYYJBatDbhhDGbK0IQOeT cR6W1TvilbD5m1SCZJoVtI5YmDxutlZgqymoIPpdjiqz/EF//vqz/wCkGz/6pYqjIvNWrQWUYiW0 UPK5I+o2ZoQqCorD8J3O4xBI3CoxfMurzeV793eIMl9ZBSlvBHs0N1WvBFr9kdcs8af84/NCVWl/ e393HBMwcsGEYS2hmcsFJVVQha8mAHX7+mRlOUuZtIRGqz31vcW9vLeW9xHLHHI0vpQyRozbOKos nLgwIqta9sZZJS5klUul1GQSOIhE0YJ4M1tArFa7EqFah9qnIKqLr+qrD6CyosJBBiEUQWh6jjxp vizGWQFWaag17U7cEQSJEG+1wiiWtPGi4rDLKPIkLY9ZvoiTEYkJ68YYRX7kycZmPI01uXWL5ZDI piEhqS4hhBqeu/DETIN3urm1i+aQSMYjIKEOYYS23TfhiZkm73V0ms38tDKYnp05Qwmn3pjKcpcz atya3qMo4yNG6g1AaGIiv0phlklLmSVU/wBJXH8kH/SPB/zRlaVRdb1FY/TVoxHQjgIYuND124ZY MkgKs0h0et6hECsbRoDuQsMQFfoTBHJKPIkK1HrN9ESYjEhPXjDCK/cmMZmPI0rk1m/jl9ZDGktS fUWGENU9dwld8iTfNlGRibGzb61qDzes7RtMCCJDDCWqOm5Su2CknJImyd3Ta3qM9PWaOXj9nnDE 1K+FUwCIHJM8kpfUSV03mDVZ0CTyrKgNQrxRMK9K0K4VllnIUSS5PMGrIAqSqoA4gLFEBTw2XLPE lVWWAkRyRK63qL6XOZGjk4zwcQ8MLDdJezIRglklLmSUJRLLJLI8sjcpJGLOx6kk1JyKrMVdirsV dirsVdirsVRaSaUEUPbzs9ByZZ0AJ7kAwmn34Ep3aj8uBpcct3+lTqR5epbQvb+mN240kaMbEAV2 xVKtQbTWii/R8c0cHrS09d1d6Ujp9hEG2KozS2sx5Z1H62kjw/X7CoiZUYfurvf4lcHbtiqY2+o+ RtPiOoaQ2qw6zAB9XSdrVoiX+CQE+k1RwLdV3xVK5ddtJZfUe1b7ccgjBtVjBhDBQIxbBAvxnkoH FjuwJxVB6tqK386SLCkAVePFI4I6mpNaQRQL96198KoHFDsVdirsVdiqvBZ3U8U0sMTPFbqGnkA+ FAdhyPQV7Yq1BazTiQx8eMS83LuqClQNuZWp36DfAqyaJopXieheNirFWV1qpoaMpKsPcGmFVmKu xV2KuxV2KuxV2KuxVFxf8cq5/wCM8H/EJsVQmKuxV2KuxV2KuxV2KuxV2KuxVUEhMaR02ViwP+sF H/GuKWW6J5N1jUbKXSoLzTEa6mhno17FJIGgEkaqFgMp+L6x4YFS3U/JeraffzWU09i0kDcWP121 TelfsyyRuP8AZKMVQv8AhrUf9/WP/cQsf+q2Ku/w1qP+/rH/ALiFj/1WxV3+GtR/39Y/9xCx/wCq 2Ku/w1qP+/rH/uIWP/VbFXf4a1H/AH9Y/wDcQsf+q2Ku/wANaj/v6x/7iFj/ANVsVd/hrUf9/WP/ AHELH/qtiqtbeXbtGZpJ7MfCyjjfWLfaUr3uF8cVR9hp89jChtXtFu5EaO8kmvdLuYXXmGRVglfj +yK8idxUUxVAXehapcXU1w89izzO0jN9esEqWJJPFZgq9eg2xVR/w1qP+/rH/uIWP/VbFXf4a1H/ AH9Y/wDcQsf+q2Ku/wANaj/v6x/7iFj/ANVsVd/hrUf9/WP/AHELH/qtirv8Naj/AL+sf+4hY/8A VbFXf4a1H/f1j/3ELH/qtirv8Naj/v6x/wC4hY/9VsVTvSPyw13U9Nl1CO80+KCKQxMXulkFQFP2 4BLGPtjYtX26YrSVahotxp2lTepPazq9xAA1rcw3G/CbqI2Zh9IxVJMKHYq7FXYq7FXYq9T/AOhY Pzz/AOpa/wCn7T/+yjAmnf8AQsH55/8AUtf9P2n/APZRitO/6Fg/PP8A6lr/AKftP/7KMVp3/QsH 55/9S1/0/af/ANlGK02P+cYPzzqP+da/6ftP/wCyjFWv+hYPzz/6lr/p+0//ALKMVp3/AELB+ef/ AFLX/T9p/wD2UYrTv+hYPzz/AOpa/wCn7T/+yjFad/0LB+ef/Utf9P2n/wDZRitO/wChYPzz/wCp a/6ftP8A+yjFad/0LB+ef/Utf9P2n/8AZRitO/6Fg/PP/qWv+n7T/wDsoxWnf9Cwfnn/ANS1/wBP 2n/9lGK07/oWD88/+pa/6ftP/wCyjFad/wBCwfnn/wBS1/0/af8A9lGK07/oWD88/wDqWv8Ap+0/ /soxWnf9Cwfnn/1LX/T9p/8A2UYrTv8AoWD88/8AqWv+n7T/APsoxWnf9Cwfnn/1LX/T9p//AGUY rTv+hYPzz/6lr/p+0/8A7KMVp3/QsH55/wDUtf8AT9p//ZRitO/6Fg/PP/qWv+n7T/8AsoxWnf8A QsH55/8AUtf9P2n/APZRitO/6Fg/PP8A6lr/AKftP/7KMVp3/QsH55/9S1/0/af/ANlGK0iU/wCc ZfzvGnzRHy3+8eWJ1X67YbqqyAn/AHo/yhitIb/oWD88/wDqWv8Ap+0//soxWnf9Cwfnn/1LX/T9 p/8A2UYrTv8AoWD88/8AqWv+n7T/APsoxWnf9Cwfnn/1LX/T9p//AGUYrTv+hYPzz/6lr/p+0/8A 7KMVp3/QsH55/wDUtf8AT9p//ZRitP8A/9k= + + + + uuid:bfb590b9-5254-47d1-b5ed-37bce45989f4 + xmp.did:9ae55c8c-c7ab-5748-a974-f66fde398af7 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + uuid:d1c078a0-2746-42b2-b0d1-25aedff8fb1e + xmp.did:1b6690ed-28a8-c141-9479-b6a9cf6be651 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + + + + saved + xmp.iid:9ae55c8c-c7ab-5748-a974-f66fde398af7 + 2017-12-12T12:22:57-05:00 + Adobe Illustrator CC 22.0 (Windows) + / + + + + Document + Print + False + False + 1 + + 800.000000 + 470.000000 + Points + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + Black + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 100.000000 + + + CMYK Red + CMYK + PROCESS + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + CMYK Yellow + CMYK + PROCESS + 0.000000 + 0.000000 + 100.000000 + 0.000000 + + + CMYK Green + CMYK + PROCESS + 100.000000 + 0.000000 + 100.000000 + 0.000000 + + + CMYK Cyan + CMYK + PROCESS + 100.000000 + 0.000000 + 0.000000 + 0.000000 + + + CMYK Blue + CMYK + PROCESS + 100.000000 + 100.000000 + 0.000000 + 0.000000 + + + CMYK Magenta + CMYK + PROCESS + 0.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=15 M=100 Y=90 K=10 + CMYK + PROCESS + 15.000000 + 100.000000 + 90.000000 + 10.000000 + + + C=0 M=90 Y=85 K=0 + CMYK + PROCESS + 0.000000 + 90.000000 + 85.000000 + 0.000000 + + + C=0 M=80 Y=95 K=0 + CMYK + PROCESS + 0.000000 + 80.000000 + 95.000000 + 0.000000 + + + C=0 M=50 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 50.000000 + 100.000000 + 0.000000 + + + C=0 M=35 Y=85 K=0 + CMYK + PROCESS + 0.000000 + 35.000000 + 85.000000 + 0.000000 + + + C=5 M=0 Y=90 K=0 + CMYK + PROCESS + 5.000000 + 0.000000 + 90.000000 + 0.000000 + + + C=20 M=0 Y=100 K=0 + CMYK + PROCESS + 20.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=50 M=0 Y=100 K=0 + CMYK + PROCESS + 50.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=75 M=0 Y=100 K=0 + CMYK + PROCESS + 75.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=85 M=10 Y=100 K=10 + CMYK + PROCESS + 85.000000 + 10.000000 + 100.000000 + 10.000000 + + + C=90 M=30 Y=95 K=30 + CMYK + PROCESS + 90.000000 + 30.000000 + 95.000000 + 30.000000 + + + C=75 M=0 Y=75 K=0 + CMYK + PROCESS + 75.000000 + 0.000000 + 75.000000 + 0.000000 + + + C=80 M=10 Y=45 K=0 + CMYK + PROCESS + 80.000000 + 10.000000 + 45.000000 + 0.000000 + + + C=70 M=15 Y=0 K=0 + CMYK + PROCESS + 70.000000 + 15.000000 + 0.000000 + 0.000000 + + + C=85 M=50 Y=0 K=0 + CMYK + PROCESS + 85.000000 + 50.000000 + 0.000000 + 0.000000 + + + C=100 M=95 Y=5 K=0 + CMYK + PROCESS + 100.000000 + 95.000000 + 5.000000 + 0.000000 + + + C=100 M=100 Y=25 K=25 + CMYK + PROCESS + 100.000000 + 100.000000 + 25.000000 + 25.000000 + + + C=75 M=100 Y=0 K=0 + CMYK + PROCESS + 75.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=50 M=100 Y=0 K=0 + CMYK + PROCESS + 50.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=35 M=100 Y=35 K=10 + CMYK + PROCESS + 35.000000 + 100.000000 + 35.000000 + 10.000000 + + + C=10 M=100 Y=50 K=0 + CMYK + PROCESS + 10.000000 + 100.000000 + 50.000000 + 0.000000 + + + C=0 M=95 Y=20 K=0 + CMYK + PROCESS + 0.000000 + 95.000000 + 20.000000 + 0.000000 + + + C=25 M=25 Y=40 K=0 + CMYK + PROCESS + 25.000000 + 25.000000 + 40.000000 + 0.000000 + + + C=40 M=45 Y=50 K=5 + CMYK + PROCESS + 40.000000 + 45.000000 + 50.000000 + 5.000000 + + + C=50 M=50 Y=60 K=25 + CMYK + PROCESS + 50.000000 + 50.000000 + 60.000000 + 25.000000 + + + C=55 M=60 Y=65 K=40 + CMYK + PROCESS + 55.000000 + 60.000000 + 65.000000 + 40.000000 + + + C=25 M=40 Y=65 K=0 + CMYK + PROCESS + 25.000000 + 40.000000 + 65.000000 + 0.000000 + + + C=30 M=50 Y=75 K=10 + CMYK + PROCESS + 30.000000 + 50.000000 + 75.000000 + 10.000000 + + + C=35 M=60 Y=80 K=25 + CMYK + PROCESS + 35.000000 + 60.000000 + 80.000000 + 25.000000 + + + C=40 M=65 Y=90 K=35 + CMYK + PROCESS + 40.000000 + 65.000000 + 90.000000 + 35.000000 + + + C=40 M=70 Y=100 K=50 + CMYK + PROCESS + 40.000000 + 70.000000 + 100.000000 + 50.000000 + + + C=50 M=70 Y=80 K=70 + CMYK + PROCESS + 50.000000 + 70.000000 + 80.000000 + 70.000000 + + + + + + Grays + 1 + + + + C=0 M=0 Y=0 K=100 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 100.000000 + + + C=0 M=0 Y=0 K=90 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 89.999400 + + + C=0 M=0 Y=0 K=80 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 79.998800 + + + C=0 M=0 Y=0 K=70 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 69.999700 + + + C=0 M=0 Y=0 K=60 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 59.999100 + + + C=0 M=0 Y=0 K=50 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 50.000000 + + + C=0 M=0 Y=0 K=40 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 39.999400 + + + C=0 M=0 Y=0 K=30 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 29.998800 + + + C=0 M=0 Y=0 K=20 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 19.999700 + + + C=0 M=0 Y=0 K=10 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 9.999100 + + + C=0 M=0 Y=0 K=5 + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 4.998800 + + + + + + Brights + 1 + + + + C=0 M=100 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + C=0 M=75 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 75.000000 + 100.000000 + 0.000000 + + + C=0 M=10 Y=95 K=0 + CMYK + PROCESS + 0.000000 + 10.000000 + 95.000000 + 0.000000 + + + C=85 M=10 Y=100 K=0 + CMYK + PROCESS + 85.000000 + 10.000000 + 100.000000 + 0.000000 + + + C=100 M=90 Y=0 K=0 + CMYK + PROCESS + 100.000000 + 90.000000 + 0.000000 + 0.000000 + + + C=60 M=90 Y=0 K=0 + CMYK + PROCESS + 60.000000 + 90.000000 + 0.003100 + 0.003100 + + + + + + + Adobe PDF library 15.00 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/ProcSet[/PDF/ImageC]/Properties<>/XObject<>>>/Thumb 13 0 R/TrimBox[3.5433 6.30708 803.543 476.307]/Type/Page>> endobj 8 0 obj <>stream +H<1!b. 5b5#XH cث.JfbOx% &Y +3{{nX#/z +5х41%7Q5n2R;z\J a +endstream endobj 13 0 obj <>stream +8;YPl]lKo<&B9i>jIE_m!^sglarQ%bKg;,p=6N5!/mq1LB>9%],=-@k'c3@T?c$)073KoY)b"i/nNs6 +qA^fL'N7VgObjLtX78<;B+171&&aEJXDHQn>%V9mL$oFI]$dEu +f((YRnS3G]/MB"R.VD5g(uj&A".h2%K2((h!X_^Z*qj$3C=ZkA#EAkK'h@p$qa8kK +9j4U#la5NUg^^p`>0\[KcMBcWcW,rPr1&hA?=RcX!tJs:;F[:(o08(hommDc!r3J><4UkQKV9krtQ;m%s\"Uf)T9R2\o>gUHcWpH>Q#Kj?f +eiaOPhI09g8'X1IA);Hb@rGZIKXX.]:16QAlIL"IWFD2u`KjQif+;lEiuE)llqd4G +A*,a?&(@tL`GulB$e%4Dh4srJ,j+MR_ +3WX"B4qd#:,E:oD#hP4R&maE_*VkN9Pn@)>2@hcVe(*)<-b#_C("#f813#cbd">B( +)NbFAK.1Bij-0Y4ppdYG35cDcDb!!5.9QqK2)X?HaqF/D]2%d\#-5JYXC5X,?W1A% +79ms^WE':m;$O4!HmHdU#[.P:$p;U.`W+AAh`1>=ZTrc[uZ[l=Xb>2=$.XdkKYUS$n2u,u&!LE^` +Le]46=hWAI5m-'eQmbskA)'T"Wkt=?]7s&_"=NP"/U\F%[=WMI;]u3.h4P7ug +%K!;.LO4UWfkea,F5_$/9JMu!IV8+FU4h\]ko3U.6io8f3WVg.H[Ncf<`Ut@$*E-s +2Fs1/OhS_'g3!`BU5rgOgnXc^XdBheT_!QJOXHXE@Xjgh +ZAPBt)Rb8;ESq1PG_HZMS,dW.73BeAg:GiiCi83SNBQj7fKWpnQX3J3a7pjcd?Fp\EK8PYIb@TPk +0!00D7P$8sq[k;/OgL_V1=V=>0QIi:):gE"\4kod*%.;%6uELH6<66k-c*\@DqNW[ +ihib.rPk%EUe[M#KJ"UO&=Kn)5CC!!H&(f:G[5':k4(lW]6\.I[eZ4o]1(>Wc%QnC +.hsLG4#19AKhL^u+s0E4U[Z[Okl"?E^^4uPbeLMMYcT4#PZh_ST,fDT>nuY%DX2Jp +f%=in0F$4[ZTct_FEdYSTDIAZ^$Ec3W8eir]^Y+Xf,d5W?YqIdg[@bf6o:'Z#E8A!R$ZCbu`e=6NMs&;;VA(_sUqiCn3`CSWrJ@.OE? +V0[HVs3j+1+.W@^]P[FtCNJs8H.,!mpYXP2iS$&I\,Qmij&l.&PX!s4r>cX]f?R)U +8;t/,@kPPOpRJ-7H[?CjlZ6Crj.#)oe-14-Nc4MI\q&e`4FMpEd10UHc-%l-Dpc@3 +qRCoXcH"\*Ig5u#,j2T~> +endstream endobj 14 0 obj [/Indexed/DeviceRGB 255 15 0 R] endobj 15 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 12 0 obj <>/Filter/FlateDecode/Height 470/Intent/RelativeColorimetric/Length 327782/Name/X/Subtype/Image/Type/XObject/Width 800>>stream +HOTwP&ll6i4&&MvMtU +^ae TE**TQ@`⥈`̦lM[MjtE9+_΅3Ig|2w?/yvNz?|eT֋+5V}|*=þ}ٙ˖L)Oe,]fh(+[8248&y܎:xg0|nsARyƆm#ik** A oC_<.CYUUcNM3Hw_wsA++m`$p| |:xs57\}ldڿy[jq}MK6J5H2`AD:_^ > ߥ{K7^wھ;xIPV̩2-,5Ly_rkcGW楦Z$O>X@\9QWR29όbOwGvMiz = @\:l9bOW?ztG?K\[og.O6:NnTU̢#rl JN?h'd+/d]c_{;x?А[tٿ/-]P!}~uZR*2N}r<tDt@+J_SX4w);D͞L:A.ǃ=oTe$Sl@0SN?Đ_w]yu~  tt:A1hneih ix{x?A>Yqlau~ARMa\#Cc[aܖkcΡo/d yΉ;6͖RXeJ4%rW4l\_姭1yby2o0sbNĪH GP@3?9I%K#k9|`Gý==EFf7:gMgx\gh}z\/i|ЉidtL95uָ6:ԟ5SKwH@t3bdx~w3.t5Wdf%Lf%\>Qu?gE#Ή{NNpx<0+" N̕y e2%36$d^k9W.ǃ)Ur(ܴDցD5Es# +[srITe39FY8QbqT=uߵ׽_.q9FzkaKյAg6~$DfSHu%%gu5Z<߫|ރ[: )4Hf\.Sl8+O5z;8ڭu=&\3.SGq7Ђk79U}3dt+{?ם;B3ϑ!D%`Z. Ix"=2luF&!K;9s@]ve.[N4?n~١([A |sxyl<{{? @@'?|<5Z:A􏩍9m_wvW3IڮZvfw?-2`E0HDATP1\%!$$!@TdNNζޘ1߱9$Dk|9}y ΢sA1+XP 5Hf4:p( *'WFȔҕ2gWݵsmlB;3v9{)$<=CZxC ej|4:Xv0]zJ|DTkZ|~s?˟k:!znONz;3Aɉ(G!P4vO>Dk/W pzҲ +NFtgߥqv=kWbQ4 9Cg ;y̘y54AL"<el\'KgT+]G|#Io9xJz[ZmsMvڗq۔soກ<k0X.MMtiwr!_-KuivpB{m*UB(^]PɏyXr[v0]:ݑ$| vz.caM9xE=jeWno:%=X0DïG ԟr=j{LÊMy/BL;f,}=e3$N'kxBy&Hɒ}e;;L=gkjf}$ǖKپk +jkC? ZO:-8i4~eӥ vW؈C97aYlS(}it0VӌO%3c˔Kإߙ")IMخ_wTfe{.fDb_o<DïS8m_R*7SL_v0=}>N{[- eMIt&R\|B].tW`&HEF򢽤=liÈNoLxk횋`5H"?ekPNubzxs[z>r%i/tzg-IrʬlYM~A\Un@Er}+{C.wϸ\}}TI9$/fLnq^Ӻ:]_ 4Dï~)}-ͻOWU%tiv];! =/B;92t\z'j!/m 9?arwbrMX(p=-o8rHs~@L@$?jj63JBz׍I{%ӆۭ_ +Aϝܳʼ`)?OB474DïԟƑk0SJʬlYo?0[ '՟'lw_GkʋT8ºYq1_-7UUn1\'{o؈MJ8[S鶿s]tcJ? Ʂkh@k齔J,b"W ?jE]|mw!߷?xO)KQKMsyQʨ'&iMۮVo˝8Zgrx)L8~D}CiMQ'?{Ӆ3=m܂UX/"#SzDBSpd 9u\Cg4||>r\׎e&&Igy?`wu?  D(ABIST& .Yy +.YGq\مƆ|k^ZCqh~cYPbMZWE]h;bQ55o^ yvٿoS;xSJYytNAlMV? +'iU*ZbnB)-X,ʅ%꼖 4kc +:9<8~L0dY@qDřʾ X"STƻ "1o59,`kᲴP9ܔavsI=-oJQHy?ͥ{[ʞӦT֠cl-}qG ƆWǠ3t`)E~^ר+$+*_@hBP)hONhq )SRE}~AD\x?M%{jsrcqn3|8K%عoh?Byto ř0..óKo9r?@@:rF\GTZt2ybhjGsi^5[w~O優, +zky`9a<2-M8.Jsaߘ<_MaQz&jBR $l֙0*} tiD?Kۥը:Gr{JsaxM7=,)NxE6Kp΃)}?yέ1}ݪr[L~|0hi~J%v )g6ce2CB) ڔr@9$9*K.1tuy[}?b`,(qcP|sqjSܺhX jrr<{tFRCgI4ḈRVLvN,6X`1r,-=u9Aw}rB˘5_PPN@yܼ]rAnx)}Rnx6gL//A93>,_/>DHdd(͙&sSp8K 95o59`1fyzF-r/?otY#;(a2Z(3O*dP[[ޱ&&{>J_%@q98[tؾ sxG,I`X> <>}IR;N\O?׮ P V _7ڿ}:ckv5y܂<7o59i=򙷘lӶw>o]U-\\#\[ +b%,AL+rERsxYJ۠N.; k0Z +z0ܑ572B#TճqHOAUZ`+T޺\=cjr;+3׺^b_P^0]9{?;[S,kΟn9ϕ+$f؇sIBYA}"[B*Kȏ_?cXtؿ-MKYxˍ sf(ﵰayi߯hr{./WRi v9rg(9&4*YxJ۝ȟ܃ƥ),gcevMeTZ^ڵilj|?@?<9x~1Dxט^ʒK*9AߊIx'^⵱ѱxi˵-i*.S-WH=]?vBQGlYz?{eL<* n~AVlx`yaR,KL9 ~?PK55ⷳ{#h,*QYy?mowpz ǹoHtxܦTG;QB49O]9bw\*!0tuߧ1zecss~ogm4>pMOP]^?f6S#GmNn&s{Sh?Bl9ERj pq|;? 0"tq9Yt䓢cnL~|ó~9GGcQnF}p{F-+$=vˢ?.ؽbobHOcʽ m="ҝk]OʧgҚe[[ӵ 擞vog4Ec:tr<@>YӶ ⚊KWT<9νySMe㉱i1c/yíyyy6w#\czuߏiZ=&',-=F!o߯M۾ꨬ<@>6{f$%LZTɲԿ")G{cZTщ?Ids%瘾.E<7^cʦksjjk}y\~ Jc|]Ӵ~?ztZIOS{ ]G4tv'.^/4UkMmjfkܨԸvU%\: 8\Q+ 000 a4Tinڬ^Vm +{3aP@y~0gw}\;j= +?8gq|嶵b#P>Q9:80O1ALY ;t|}p`6/omz!%}in OpaN&<م""P}Fn A2g!c +58PcnAAK?vft9l:5۫w'={tjRW'gO}_x눵8y~\C8>'%\;^8qXL}g™MquXAȔ9AI 8\1wSD0:П_6liqo﫟|=~} |A/0{yzz<_q;A2ox *18]o?sjdRз wKc uf2j~}~gok1^sro{]wsLAA!S>{~)bJ|* [LXJh+Rfd;װ{))ߡ/O4}&z|mtB\:#|,\Cp'8?Nbo?c6Em'_<3g)lŚﵜ< +g @~ZG 4qy?? dO=5'RR;ʳ*jW)PGj>+4ٓr [; 8w]Wm `>M%3hWorT3uqy9_z:cZOWl@Q9:"oj<$̗cv뷥+( +yV3J>~uGc֙ BHg= ++4tMPZ{>llF zn7y>IA3,!#Mʽaap={~~v wq?˳o{ |Sƾb1#un 8]yIx-Go2qu&'.>-7W>`]mc% !>.5 ?-ca<7^oA=p9)&.t.xL ĢS'?+TS^VW~Yu @9\/D>J#wA2ë݁gԚ xŸWN۽2v5|Pmj(bX u ^b HI].ëJ22霜6~P_X>r6/o-~8:`֑ BHbSiٱqc*MvŖLgG[~VL23\aXpqt{ %&6'nKPbAOgG̮:;pgTVZ/r:`}(:n x BfΟz`ptuxr2PѻC Foʳނ` _}?Aro)/[} ?r<=WRa[Ux99ŻQUCƾ4nw{ \ |2vkP ++K? ϲbb7'3+P\vϗ 5:jbY +Hb#77'ϗ]g{œxFvl\[./)Ɵ'#c;k/ />vSi:ԟҌ_QMY9X9j;ev<+Jn=QNE9w?i4{aS׿ ¯#wNL +.~N{ήh&0{@ B $뱆_:G7 A z?z滾K:3 `a,>`Dѣ;׭^ A 9@ j7ooA4`?P fkR +h@P@a9fr{>l7@lAÄx†|xѺ!@P@a9L=!!oY'A$`[sA, Aäh ǐvor AcQc?z%#G;i'Vikdzof6?P ]z~mVyyȗj b|P@a9?y"84&28v{PЛ`GPh–YAW\ƖR93dò2ٌ뮎0iDg]AAaEjB8>nZ%V03h 6,8Yܘkg'/g sТ6b? 7;`og9PrxrųcF"F ".} |! hH/yM&ϗjs9# DŒy?BZ[UHp}۶]=TxB퀵4,sPטNW6- Z(c7$.tuD\϶Rz/3g%WӼ6y܂JAs#xSQvyaU$^BocƨWJ ٰ,Ҥ9wMoDb} >S?NGFWnr - ]~eT +hb*z.:MXgl2ӄ5Ȱ.1Z6;Uh}c8_)Q!av&]g |TȺK/g{en{LD;#Cm;]{SnZ,5邺tA-cd$fg$us '9S+<L*֍s%J?X>돎z#Ki$wT@(.zڠ#<*Li[7_"}Z^{֒L᝔ ݾ騟9mVN{1qɑ9mKOUРpru|2R%HisZ^_c&ށެ[N z[4TFQ`Z:ϤS1ZxGٙlqz\U+Aں":ab>bӜ>ۦ>ʴ@>>jH"\\vV>Wxf k^hM^ ]];דpwC}MI? b13p`o~⾡zu tk.;wnm3_/K?6GyAu^p4fɤb#sE0?6#&ysbwDw0HLOM,OI.VFǗ\( +Mx"ĬIEMOCCR}j~vcGZz oJ1z&2-?6:Oz>^ݖ{iXd[j}kx**jCMŬ fN{{~0$e ˏ-(/<|Dv*;FcR=)*59jlQgPa>muI< dŞu՝lvl0E]S}pww2;E\T-I2zeO j|GT-kL̖TkT9*[CjGXGm:3u|{M5w*=«-fGm'1'Є_ŏ:lި;ڻÉc3%40,FlZ{uG؅6gr)a¯dQtF}G򥢚_l>I4"T~IkǬo4NqӮ;w&m$IEѿ=>$n_HL^4жuwBQTt5Sj3K3C-|cOM7V6]=5k4כ=dl\o.ԹZq}urk998eRrg9GnϷWOf@KZ<@-Rݮ{/ESlQ|+Mdψ\$n%(FZݛM<}yV:WXj=d9S9C-r@fU\bx޻~K{U٪V!@ށ~P +XLru_i7G6Փ?v鹧?~҈'_t}긊w4*5S*OvgSx4VOЪy=al_6u E? bMx{`֍ 5R-{ ,r#L&~s5VnW5tRvzM@|4mæGzR#:.?nNKkǿtŋB,m{#^ak|[@azԄXkڣM>NɽVgT 7uz!;"΋iYvޏC`A/{ϗ]-B42fKR؝q4vdr<`kJ?w;=)vLjr{Nr;ObyNrobUɱNZt#;YScﭽfS4ahj+ً}bz3gab l-r#z1~,ٺ@(\gz泱\ǸjWsds.}Lo_F{e\#qc}R@=Kp<FKcd2e>]^9 S>9VT^bgy"R cIץg)O\ |W}'ݯIg~aO\L +F'ܼλ-Nxcݒ=|4}VRy>za5^:+UyTx7fAhfܿm`C817s6U; fƞ<kdyB+=~#ץ,,ROޚ&{2^>AL/cΊ*ٰpa'ח?B hh-¸V^m?cA_|q٬:&kǯWXSO=j$!]bׂ!2XX׋qwq'l4W_Lv떒_2w!:ıq{WӜ᏷ Q6g荊y}c*M<\)}?n?r6:hc V4v𴺻aⷋ+]4c>d\ ?V8~d֏ᴲfß0`fk8Y1 ՝:ПGpޖ<JQe̞x6;%74,,dH9s{{|'GN$HKt;* >o?x-^Xw1wb`56tN^Y bJM-:yÁ͎xvUMzy_¬N'+Q11?\^͟M=# VGD.o-ȹwA8$-e㭡vsymϱ:~L83]/Hy*jR6O+9[>_Q0,le`*9"̖Cgdy6re y^̧!׽a]kGvл. Xjul,ꏥ&qLo:אn2絹q%uz!~`d>d65a"ˎ^jM֟:p48%ӥЦ<*MdVG#$EӒtcuX7v=ZOE{i=Zr2ͽ) ֕N;,w%Ǯ Oј_!V>=D/ b k1z ̤ܵl8uǺEa4FgAXE +SAZ@Q84H\MzmU=E/U܋g`dXO/ڒGقnM'0=4? Q.BgoVu] )!Vqj;kj?VO2xzazG%=a FX1cע:?wٳ;*7 )0#_s" .WXk'kزeKk,0/eՑό8*AF( 5.vɮBYD1q Q h0#e$&͢Q3جUQ[p ;uz=,99̑?yUխ[uoۼj+C6.NƸoXR+x}}o&-c۽ƹ_q0c7I-^ٮ\C+`F3jZ |> +Bl+. +/Piwc@S&ht^=о6`oh=vLrmG#4PXc>/&// &S9tdžv= ZqhAEϘ ? CNJNpP5pV;+gP%/&>LN.th#S)>?"E6XQgC*3?i_JNJ@KRߥ-؂}p|@n''C?9r[6F<1%!W^DnyjGv H!#c+T  Hh0&jnqz/9uwEvn{OͮO@V#TʵH׎JWW# ps(~<7!à cCt+\\sIҙ~ה|C3k]}`K@.Nci=71O&?9;1ӹ̇J./.1q&)7unx=x9H}h6<+u7q\>QMsd{*cw콦֢6B}1tqbnccr"5k}4iWL_-z0luLvV)>8\0I=N]Pk5kkk| u mn/./;:[VT[py; .a"˾=I=U~ڶEH^Kvzu-?C%7.b]5Q{m\5?T»-t g*=QpbFL +Fo/,2jil/ 8݁g3e^ .Z9D/TPg֙SM1phxM.0!YÐܧ` +"ݛ +3hI˚ځgDqm?AypLeY=[БlEs=᷁vRF2[Gy{k]ݵ$L: +wmBc߶Vf3#lot/QG彎ʖ .]grRNevHSm4CCG>S=~W1"u(i=F +Ɓ(?\FRo^-C>_[0,#=ɯ!82_%M>.!'Y)sU|(G| H-V=_^eZq"ߛHYhөӷи>UhV)5(A=Ⱦ8¬q-, a/#\STK#7 +3G&t ׋ɾm%P͟4\y w!1 +]X9ͱt20^]Hq|O,iK%D G47U$=d shOZV{`,rƣFdN_36Nmq.qz뇵ÏwN 8K-Vݭv/I]mv;7{nX#>0wZzx6~ӳAp͓VW\Cuwnm5;ppc=/$W]BBAKyKᬈ1Q2NrɯqNoD}җ{X]"M{.gH@!1dC_qAGͨ=03IX +$ȋ+rAdNcz6mO2x?WzҪ}4> jD`^GXG|m4Xݎз  ]м<RYM PDV쇕rC';z6^џ9{q=s l.j5GbǼ0pwgs"dW`)/$sW*nRj*+ZlAҿ+͠L?ba{|o*αШzJ߯D_C.*B뙍cdڏGZKb%`N9c$Y2,V-K,nC}4O$ѹd?2VGPiIv>SĔqU=/s7l5 @;#%[X\/9my#VX$AI$=KZTZ]FZ|4w#3. -"u?baɕ0y=|cKRRk~FEu% htbFͨQQ\[@&F13F-Aݍ~PQYekY"D'RsZ\b2Ly߻U_U/-VRMzXGgZ|BMEDe7ޫk`ch2N >@AjWI5D5b41)!g2 %LWjMlyesUV0 NR[51!VęKzW~:_ YχH*L6rimݜ|d9c䆽sŽ-W#OpƞF'VJbe>EҲYWL4'X3|zM1ɽ_c=^!:c8?Ĕks6^)񯱀!9Vm{8ggomvMfph5ܼsi@϶{R^I%qE ,1WA'ENVL\hȆ5IaeJl^oZGfꪠ)\ܽCT|W;ˋ}d'sBi<+_v]VG *N<^h6Msn Z9i<V|$ԥ.G+l,U!S6>X]9"d9P7u;rVX@b<=ɸh2.Tq`"օֈ‘%'+{ ׫6m@ҿz;د?ɕͿdk*WENtc9郑 %OTxt + jop\ֶ͏bKӎVUj|rd^HKIIV&HjW-3W _Ṫ8oD}%JR{ܲU?S>$WI}:뫜ai1j`9^ ns]nRgAc0?eִ} L-IXë_qD?#7Ha` 9@6BWн`ya"x*3GɬcmIWmEzkbm6nc}ABg/`Gr#x' ;#@YB:gн2í%V^[UAY} DZZͅ ԌFwvEU/?=^`Q<йpGgsgȈx> s Å"m跰?Q{1(|QrQJPR`0QίkMk-Rv6Z|z &XUl-+W سq[a,!9-?R⣯fqޏ9Bϵ[-l͛!cta=_?^ +<5eq/!, J9=НHsAVM`r7'z-Uu6`PNe5Ko\fޖahz̦Y{]z-yi[t|2_3[;emK7|xo5eQsE??>~K[U~k !kdhyAЦܛ+8Z-YiJ/P킼b8YGck;Ϫ(V9wѽuڷũ4rL;33k 7ȕqѽF#-)U 0بH@Zcrpe$6Jz1:b Ǯ|n=x07crC4ΘNF:{0LeYD `O |!FYxnMEX4mz1\c\[>1]Oq"~;O/׈=ɓ^ r% qƒ쑠9,[h_?shp]p`q?Vx +gM'R&h\:pEA6"_^# 8 鍉0#BXw o$&ށ܈?0 抆]ϼ]C텻޶G[$зO:X6 4 MbZ:QgFtp #:P eHC l{,qI و@Kx|8<,6udQ6KE!^V"_(?_CrD!^!!NO5/qK2{~fʣM" #2lRdOؒ K:nƵ EB-@F@b5Њ{?ǎ3;w}߽襆EZjfdZ3-GWjX8m:.9[N:e)#(@r7Ig5ce]<3><7!悹!jPԕݵ|:y]ڻ3vZmRh(3O5yz^UCx9lzPt=Ir,)L78)\#-7wWԐ_Dfo+߽|ԺϷطckh 1b7K@|G=/ϠUhVS7Gsм]u +pN*w4\U}X-P#C,y=k[P?ڳL hɔm^'56ԟ}̑O'4'W۾, ?mm"Snɱ]:hEլVԊ\@Ӻ.+UHpWAv喫?|#.mAr:2ѩ H)":-ߜhjG}GO7IΫ|dS5?'wUƓXR9sSS}|},޽#Y'(%CyOmTh䅉hP ?7GSgc ѳQ sI.+]9*n_Gh@Sd>QoEO:],ty-#s =ƿUl4d?2Ot@s|fE0p! Ԑ53I dI6 f…B P YPqA*u|?{`&/$N1ȁ8.h9P_+S%d}@"uVwB<54X6V<%{2vG + O@d!lʵ $:Ay..PS vF8sG7<ȤΧ=C4Y' |8bb)S9en9gl+p8X/ d|6 * +œ%@$y:WG! 3P I:gYdR5<zQO Ap+d:g|6n=mD<7F±hHfMfNiAga6o +x \^ <\YR >W/&F`!6[:3b 26sy/.A(Ol'gp_?hF<y}њ 6J}"OԾeo\ItB}e%FA 9m[KAjoTݞP(PS!L9\N>~;g9ȣ|8r28gdьc|UPι\ :PH?~DL-XؾAi Θ!jѓӮgGU‹yn$^u_MZ>ȟ鿲_a1kmJbJ!9kQ4m㛃suYbuu-H+9VPmm>3Ei)%9}lJ1&2Syy3s]sޏ}=&6Mk]RGVܧw)n!eUWi b̊Ȉ;)bC1N}%W|c_y@L%諗4Ww1p[cyw|Sݭ!ʧ$k9'i5/.3R`^;H0](XJAKNE5~N22:-BD0*>ŗfُ/,k$η*vhc$ +}YMk5=S現V5JbxsNsz?r>B̅A'ɿywf? +6䍭t=āVG'G&E߳nG,[*:3-^&}rt8Ӗhpb\t>ɋt!#k5@&+|N=Qo^"};_-ɘfεM)-@zy݇CC6/B {=4D]h겿nlq8|j_MefXMe ?_H)m G݁pyARU&Jќzcgz"'bV8R"kyOXHu)%iW.nJK>,#]ΦOM{o}*m+V-cwXlܨGD~h?0dkGk}YLKgFY|&FeI%V`@s~5g赯bRqLAw^ +0?,-2jv+8Aפ,ȕYZFF|cwٜS֨/P0ܭ9go?|gݖc+%B|4}K,w32V^;G=Lz&$אX.mތz\^ [&*_s^zKz{Fk>d͖oy"sԇ509+r^k*Xګ>^|Vcw\TWLJ64n,l(QWE)24!05Y|Ƃ ԈⲸ@4\@@4VDJYq&CAXQsgK4|xs9^{LQ`Oa=T<-ݳGsi꬈"!yxx@{shZjLϤcŤ> pS؜YG˼&7Ꮟ 5M"+֎KVD#e&zN'Yx4' r3?) jlԚ&BxQ:7Lzk[KLSP~z؍|"P z^X4=zwv N(=tR}p}O>v%tj!ZbsF?L ՒdvpcbI,7>k /_QIqx椅u'l٤;QWѺ'L@^H\S1_l߰uyL}J3y;h |P  /ʝu4gb46I} <^@( ^\2X2_XЦaq=]{2{g?>FЗ1V#sj'2{ j8mSc/0d>cucy#hԢO{;O=5gg֤RʏH L׻Ӻ;sp={i14gCw현ڂ=Н;&WLtZz|u岿SD5Ɏ!Rq4GC {uXzy/ʼn. ml 1Lnߍ]1޺egBO5֎G*ݚwvc>xM:yޞ{<ǯ> ( +TClZI2E`z:Rj 3EVDoQXO:Ci:`O񽦃T@إۣ' @eߗuM{A"PtUûm:{?S*#%3P&u3l*R3U`!׏l tޗ<7zzY2tBk`,Β5X"V嚡2Qg=|1ɱR[GY0uI~S\"{<; z"wd!sP_+ɭ7~뢶Z%*nвOҸ>PǺ譝?A:jOނ W#[3~(Əeazݔ1gUP#ծ(èCsفeJ/^kyd.M;0*Th3%pcWNSNмx<&qwZncGze?KH\M.gM΃쬎Q]BVW8$ C4QOVegJ$s +}̭ܭL7lkȪ=~KXZ4w0?FY\ Us[?{W(ǼSiN +Ӿov~Rݩd n5Ba_M)6UƲpj +3#s6TB8L\?'AߟY+S{P k'ضXP_&bF=ma5&eUU{jH3(zw?h.큆5?OP[f)[0˱!zCn52a=[[TtZP+/BB0=ifR806AօzMzhq摔{*IUi斬4F.}xeX0$vGпps@3i-P1ћ余aNj +1Dj)5g:d9=cУT_cò#r.Mc!Fn;.'SXjϜy![SðE7sBtY*D{m73dV(}81?3D'H7PKtpݸc˓VA_+MhG6vWgw]7FyܙW.9>eBwU[}ψNyAB&f1Z7ix8^; .k\6`M.'q*>еYN*>T=^@CQ[)Mc <] 5Bjlש|?uk"޽rHm+|#ϢS|K0i-H0{so/z>88Pkב+w/Ycꕃd߰Hž RIJ vg98ݮ9>q %~w~Ap2 렷XK%I.uuv|;w,Z:}F3l%I_NJ Oz>6 z`]ĥj3CQΖ ?_=5:P1lQWh1n0-OY2kʆSQ 7x߰vDϩ? oqD?4}?siIUm]sInCQ)<~-VGpt,/3UNzȈe:&.ѻ>#w?Ew_9zQ`̙t4^dCcǠ_sMcs\M<zҼ$sWv;M|,>Zo +yyK5єDg;p\TOB*E8&CPc)LALn}{NES%s Y$>z {~J5]<_h$pv[E~qqxBaM}Q@SpL +u s 6l fWK_Ӛ5;A4>ۈuRDշ;\S|t ٹOuW)WNtA\㇎Ѽz!nUX9$gi.c$keWsRT'kgY^uHKwH5Wȝ9L3LNIl\gG8[8˒Eds|Ķ9{0T=T\T'$ߡ}vIr7iS >zLxٳbrAck֯n$p.퓺)#)}毌Vn>rl+;sChh_l/o8,ZS/ +etL Ar @$Ao1]@LMRbe,-FvJ惮^?ϛ{ K,.߂jzWa>B-rw/l\]_IܽYX.ȇq pOMb ,m?gkb#A4|` Am{åȃ}GAIj y6NslbXk Nܡz4 q9Gss?=\yN'︼.t)r$&-duT&){A>1dz 㫛݂1Q&g1$kµ]Y0ƅsPA60-{!W޵vEȶ$oJa(7$Av}%-!A?2w@ Z "ˡ$86SV֬>.8E91C&.xKuePUr,=ŻѬIA/ևR>:bR19  ݲGԪ)3hIpoh|*eJVt;Jk_#J>>=kT>p DnxUM\drqSF{p᥌:G)jnY|~9I <}B1[6yyY Sގ蘣Mv}2 ygBzko#|vs] EWcc@/ +]4U%ạ8p]* AP@6GAE QxFgt"VE  f;** (Z\K~v:=qw~˻}Ϛ4{0dâE?6tw*+r4vYo5X,YK4I(b +=+&PMo#Fޅ0y5Ԍja\ Z:F1DA0ЉkN/Xfy52=0H@o:ڍ Tψ{ͷh1~ɱb7Xx~A'砷Gks!j>˗z{{)~F5tDm*}8!g-@5YC*&j<7&:!ZyKaE2~Uj +,AxHcSD+snhtxE^볱4saloTp{R𓹂M[Q iL$`Ztx؃X =lxCy͍tq(1-NΔKJw|qᏺ'VbJ#.{=XC㜘s ! u">;Ũ6 ﰇGVz:;r2R2ObS*F2 !xuk}=\fM_]sISV/Zeb VJ=i|#l LJ@_h8̔s:_V2 lBj/92M7<U)"n]S!s U?᱘-4{3SOƽ6-['<};)Q ʌ;-1&:ezsH `low5/A;&l6#X#퀵z*a <4e&гlۓjc3"yoC:w5}4Ab{1`mlGקcn3 {Ay_*/qymgۜIqFK?ƬN 2[{[y]#;|fޣsGx.KM--NE4f'E| 2+㽜 N"O0y̲a>=k%-!: lqdD,|"/dnA=8V4wR(كla5Br}`^el= k8dT_ȏd֝?>iָyOw8jgw{ռ+rR'N%Vw(Y5jgkH>#V1f/; xv/~rza}`s֝{y5XI|BQB_ol4f"{z=hVp^񬁩*Q .pȋ'2+NVސE{W .؀QX wro'%\=Q.|lza3 ֜JKyɊڲ¼8ktx3+N#_}kfduz1 ?Vz\pvp뿷b7-ԁWwZ"lmmubE"rQ>tQSm^B Hej05{?6<-9r\E:Y {zAҾ=ʈ=wubբt~n1h.j%Ku?=ᏘC/ٲfMom<]7'32b)11bh[KG`X/ K'k)`u2 =V>՚D S3?S :tk 0&?yr UbKxm`oeb/`3k攵h0o`@s0c U͍w`j@//,(BsN2j2;: :E+N oYDAStG22.8# +&BPل*8 D@Yܨ@HB 8}69=swo{Q_0AuwK<5U#xi9=k`),u_Vgs뾵hpo-(qȭ}4wq%=M9)uoH'Rr>u}bz=dCc .S[q*ED8!9. +>mKzyw7yE˰oFƹu:ϮvW gCd`M%nmoȽWN+u}߸}ts~<0]?{c=[ Hıez{q=~*Az={7;ָ[1k<,@KO6Ƀ}_0/B,GujzF0 TxL,dL`z!ůaIWvl޹jF!)x1-7JO&WoR`,[D0͎E~m'A=ۼ`lz}ex;~ZռL.N•$R䜭f''柫OEvrV0QT̴mNNߧ +f,Bӵ%q7JήY,M`Â\Z=blk%5x: fS\PO'\lZF_ 9' 뭧Ų +>љM6g'K`oߍ@Zbwհ yGKP q@M6@ƲTBkkW`<}:٤*.E"(>&a,M\=A:xj;79#֑G0 -zd'N|>St~T cG!`C۷;z8[?Db5aut@?/R=kO~xu>/SƛR&gǜE@M]'F%dU;Uc~˿zp^-0-u%5 趤M\0zKޟm:`ʹ0a >G)KL9P7 +v 0ğ\T$ dSubEx+:uuq;8U<AvW0AJ<'K#!GXPd j}}̝xjt_I2C,W.E ~ix4b-XYʙb:%9#J6qr_> +30՛¸ka(- h8S('S}%e3<~]ӳ]q.Yd$gT7J_HO޲S%wQ+9j1UP0|ezh@ qc8̰P C5tO"w༉mXzyě|lEAm] = 1*7Oo, P8 4d?}}cE}YEwʢŨڊ(QmyTa]9U݌*D[Qwʣ? {4T$qԵSMطxIXoX?c=yÔ''Zё=%)x=ΏkR]]\??WO"o3ߟ'+*ЍSFƐȲ6x;$'kn~S(*BkvOz~[v+Zd<ʃ=%"aQH9K0ߊ7q@SȘHNOzU*R{Lנ}CO܁`~Nv#@A_jPkf<#O`qS[մ>pX#sKry~hOU5$ +V]p<45(0> F;QLYPkHo)yvv5T!{mԇ-ڢ0E<ijd@yҎc\ <mÇa4#{>t5gEKI #Sn_aMyA}D-ʩWE\ WH@HEEQ ZzU9U(GPςUPZКprv&V\]g?ϼw2ő9}hةOo3qo W~ +;r`㬱22;aZ2`9Wo +* \$/lM7i ]xo?uZ3Tړ5^Ra ^TEj/nSYF;S_{"ۈߵdY=DAR?D!5gAG#' =3&lz_񒳒OFӳ{{D'i,t6߻k'#KUG,cwnKs +*]EPMI,=ԮBt֧iG()w\N>cqT7~0-|GZЪޏ.SLgCjd:i`з@+! 6Bb6mQF|QˠIYhcdƧaJL-? +i0T Hss.ݑ:noAہ9/HeWdd?~}\Vynrz +sn <{odf=~,毧Eqq"6f1z3l ..Oi_/CyϾ)Ҕ}_,}}.]}jtI&lY)`^綕+Xz\*+nnsH(Hj/j{m Z\N 7И ;6˝d dg /JZG/ٚȫMC PEs̽X i8~rd[1bȬsŤ;x{1>UVbsŠC/0M%k2v}(:_#>!y[|4Nܔ;ؖ|4~Vv*=3n3ǃOgr}uu玟dۇ_z1'dFpݞʌ317RKJ/u$*cvŋCw>;ItJh:jiQVz=FL0{{3NĊ4E~D0 V-|IΜE}M-MK_$1_.vbILqop.^wrᙯx_sXG{W g.]cEZv&f+xIpkE+hB^J |u.X&}VLǾ ~_?;'.w f̍?Ƌ=¿HMU,/T*ȖВ37H[#-3~glAeO m[9} Wlsufm_u 6 f޿@R@dE:ŌCA攧VۑNbGA\x*k#- n^ٲoۗ @4J-P2RS,y6O.,]YxM,ފUɈck`z'_Wʖni_ h#3Q* K3'փ ..d$!Dž UÈD3N_%F|0_z Rp/mPw.]]6=|rQ]^m΃ +NqcBPo u#$ (|\wUs=pc::ϓ`FAWrڎ+Ol}O˓+aa>JO*,SȦsQ9@0*zᰕ>lUS6!cE Pҙ4!hwBz +GaGብ/zQZ'Gl9Q$잿GltppWrG/M^Ap%R=A`d45_w۳n4S*4g O Qj骱 fOҟP=c}b94f` +#T?{TqwmwijE@c=e!pF +,r +ZqCk+/4wM96Ap'wkUץxg*bYE~1*IV;o9"$7z:Ei/}n I_: F ocjcE¡Z/l1"i:LOE o/fs=ce908?ggB:KE\V?Pr#'K!G"?˽v{|VCvPyvpcğt1/pn: p%'ٸ MXiMF+ faĮ4t}{hk%s(uWKw7;0ap?,9CSF }Fݿ0@!Ȏ2 ȷڰ% r''d<*E(2paQR`Xa}faYMQBb=n5rJMI &FmjNUx*l }sLOow~(a@# UoYhY6,ͩbSq!TeI"X(Q9*B[B[e!( B>l`9]le5 "a2R70kO1e}Ն+CWD2*̣aKE(CE;.rAd3flxT !?׫g6>Pe5{[)[q47<8-ʆ-X/G^ʖ-ɤCeO rmJF UD41,YtGt%K9V)5ZFBX+7/w<" rĩd6BYLIJӚꆔ\]]Jܷ,=Qra_lfրSvv~mLUS Q >FߍI^I׻=vi3{FƇ5A,*u(Sw\R!!%Ox#* +|2.CnЫgʍ=lޔq]5cJYcөWzBV6fjɜV7O;sFd=c'J˕t!C{8;G-pV<^PҟNWiNJs%oB6EW#i`|\,O$"ӀRwJcbނ*x#}ݲ߽HdoE b]ä;ZSvxV.WމuLB͛ bYLsSo?x;a;fdiv03dI?*ם`3|M$Ja}VNprFV\vw8%-GǁkdLBChjYJe`H Rqi8~- \|AB!~tH5W09,Mל婆[҄]2 p>DRC6,S[zZ\bS~9 .a2o䠝'/|n8ȡ +rˌ0[#+$džsMia)'}ؤb#9D<&ڧu(_ô506`7懵`w=/ym_ ~R@L1Vr .aDp^zԉ9K]A3x2߾#uED \hҳ-νDZuk#w՛EH^w,/ҷ ,ɵ5Ie/^w,:?|gn +)wE nl{x:޿e]hO|NM;rFmDhx1]!`>i8) j-,0;OMU{]fx YںxQc(L3~ cӭ~1?Qnc5mOwK{b-VFW}R%س߅i9_4GktKy`uv&tٚ w^d#rtYCOU;ؤ+*Z mߖZZ/[mPú|mFuf^pp:ؗ8aECMA=Y]qq%`7u;s@64#(%7g.[B#HG8>)@߬ Uڃl."U!Jퟦk +59N0?fI'fm K6!5ln0FvD"lO~l 8<)ףL7|!0 Fྼ㢺8h "EA2a(ӅL % aUhB""( E.*h0**h4fmBEA^{Ygq8޻sۭ;wV"c=O4G{"A͓#YNPN@csJg .e`43~(kmQ}_%ho3Ж@s }!v+M4[8ӈVW?+yWNh]Y޼?(MFMG\!ں7@̓ ?~Flݽg 2:' !ITƐy?~LE/ ^H] (J|z~®{BBNǬОF=ͥ&@8&ݳ1oMAuDoS#x,#h)`衼}C|'lk߾^GC: 8Z ]`fvF^,)z+XH)gKcb3˧-o:ܦJ4]p֪Q(q.}?T ܞj9i_LA鱜C˽37t/̭tֈW~0 +jRz>cs?_k|7;?aH|:LynPfz9`}}cY0A>F9 wp< Deet % my(؅$sOeD4a?7Od~5%osYc= +]1$;`3ޣ-gL4?ׂ[(bEݺ0t#_=Q 3)lkd8cљc_[Y~y$GkDgBM4zCnc#F^1V4ֆW* uHe׍PG: ֬t~s=GqaƎބf{HGfn띄[~|ao"*˼`c`Bzs_P%soFwP]9v\ΕHg jb>|35iiH'.0C.=tړR +2%bCC0iQ񾻖fHӄܷzkÜ_<&6N] >)C_>Y'N^K>8Lڕ5ϕѥ;wgܩ9_5,T3 K$O=8 +^Ζh}/`^e_|7\ڐ|=g;enϨ'2Z05() GW-*h a.M rd^*s#V@,S. +Y&SxV s{*wmr4Vڛ)E6i$lGC^-30A{%̷ _IJC=6$4Tߢ'IbEȑQdqTJh"n+~ӖB -x={S8cSch -lp }1;ڠzDžSTGΑ Kh͎u+y4sab%] +hZ4˝%bmUEy +|.X_.\um9ߖk(ʯ*y3qEق#v=|pLg<=q'GuHqk'sf榧I /b[+.~G5uq"""kDYl,B0 ЃRDŒ⾠Q{@,^0LΙs:w..Ok1N]``Hn=ORx.jv<`Iuc) KUkk/DzL' ѶYܶGH @|~#9HwwENٰ鱽]*Η4j_Qꓧ=-MwjoO,Q!d$h)gYqP^ 2{ d蝤 +jdTXKGMdrL6dh_ձν̪Na6ʿϨ-+e'`Ω7垨 Jr!^w9z=ʊZ]Qabs۴rx^d kE.~zdj{\|]LƜLu9 +^~Ƴ.nM\:eaȩYփmY f *S*e)۟F`rCprb!!R.lzh;9GݹefNV!)B_16A<9@1nf5ZIg2B@5sT,o$0Zh1b@idr <.[4g?f[uSZ@b k` +3Ȕ|hչ=yx\(A9hapYToe=:#ii艠ӑfpxI(n3b0wEX(h@YK^:3YbR~E! ,=":~ ߕitf{Cc?"aAKnJ:U'q~41;o#iiO +ſ;nLۺ/4ܞ @g]#PWD6mΩ>/bp tJldFRׁyo\?1Ɩb7XU.llpY{PBkk:0@tn4."O 5 [!6b zI}a龺뎈|vo++Hľ'VRƳc 7d1V-Lj?׷Gޙ<7@,E;!x{Wׁ~g%-f@رsMfEI t4]()hFXK/ a, Q[V@xk3 :x I W@P)"bEAѮtj5EҊYh\u + +IP!ߞDt߹{~{sP +Imwz[Bf>rbk>#祷-Fᢟ("%ݣLX:nẠpA^/.ҤjPMRe 28w vbJ5{u|imcT_AM6c?ҝV`WHJINN) /]hh2 #v"ZGwY?:MV=+`]}[ŏ)Ka{1Mg`_,:qg;'n1!'iKն"_5z,ujNõNcZ$Q W;6q2n o{9zTmIi#0(O3N=(.q0`/ 3;9d(nmR7ɿ;F~ ~a1Hփe08|E(šnVYqStqe7OzY15\̒sr~%T7Amøz d>x &9cV4urEe^bθ_^Xfdzw CC; +cyY2MEq>XWZ<uE9r^O̱,^<s,As }h@JL ^V7}qI5K_˞||g$gY4|>7WsNbS>}c^C-DaD@I u@ € @ʅ~9V]n(G+IamrA(QԻ,2<oA+Q* וGyڵaw%iƯɅAuǸˉp蓰&xÑ(!Z0n Xp}`$=(?x!_N)/\Gٴ8)%dž<(8<]`pa#OS⅁Dg8Q2 +% s8`"T0R>K#+{@8`#H9՛du\_ +,Б!):Z$W/Dpȍrlx\^y0{X;>/oK9ܳRE)Bv^+njsnTKvʝjt1⅜H*FU{͔uhJNX[1{ +H)2}5UU[e(̅o0zR"Hd5sB%D%Wzʉr])Mv=v@e^E=}^e@k(AD1,Mi !2MN҈6p(U@~Jx7 ڭhEzEoeF>-`Do a1eh7H"Ou2yAawxF="nFQ0ꔊFRIFk7c{67{Px;:OJ.!s`j]\9t B22I,|sIai% Kىlv:,bW*1VqK5dq➭ jbൌ7#ϻ}SRg5R~60l3s`G" ק6xŘs#uҕA-[$zu `9{ yXC~p4%7ضUqs\Zole$kƳ7ooZlԱ=oGzǏ#ʐR<,g*j_n`IHHaB&Uks\cTjutD=psN96C"Pq [%l ܹ/EfLs߻/+PCf6Xkս)*~ (p=_U&lo\?3Zu5"SG%,- uM=nOtjKn`pxA. +Ie 'zyMoETe)z$CC$`pX0L,$ + ,jJ(K-() 3 VxanG:V4.hbDAUybeIMk7)Orͩ_7[, 86n-A4ri5P 9Րewn;SD:RԪ3dP\wO$x)ue7@M1R8w¹3j {n>,J׊QUUr‹v0{%5sg,Νk>Pkr|g ? =G. MڄcKcSȆ|A]mw  ܧǞ|N8__: 9߫]V'=@7oOO>fzw64`LϬ\WSSdv0 ]aȦ+rU|?֨: EA0LV{F9IըVu_JSw5++p&;|XN#B'ߤ_BO뾮HόGȹpfk-. dvɲDʮ&$n$*/)Q"8Yۜhi[R.dD'G1U6^LOhM ʮ$ewCRU+#SӘ#aYΤdf$CW x!{-jң@IsX!`mbH|,P&BHlȂ8[ԧ'k<&& } ͐!"Ŋx ]t/zh89Ď"Gs-;7Rӊ5&;b9Wl` '6 RH Ec!v||MnYwl߻ +n511 >?|b/W~C+W7Ax%i,O΍&ڟ}MfNJeќ?SdJYiLm;G*] )Mpp#G`qkS&ƪ{Z4 j0==8&f*Hm@vJtjm:tS1 + N\¬TtQ22:vx}].Mf C"Y3;/薟 XyRv@ZQ|cq8*h +ؐ†Y= &bJ۷^j\;ܽ$j˯ OF{,;)'ԯD`._2a;>xdM*P\?M?vRdȥ+w_Z^eN߻¹ܞ٨eNo lC_vK8})*EiF*2V%^jfAKoLa}{ڢ1]((X. ?OxBܔ{}ڄ]t|>B75 +K^@ '?IJ̌!Rߎ5 q=p+7nhQkIXDIڏ }*~nD GjA +䨆d+{ J=:NX_*ã,K7v#zQ{z=2잒ěi|@y7{j8qf{.#u98La d*p1;!q*cThǕao ݼ׌_QM]yȢ,"lʒ +Ae,Sr:cVETA;Km]֥֑aPu:e Ț "xU9s?w}~{_~u2s\1~s~4zjAף19;<=>.}<-mpD-%3=b$8.h*oB?K, ]t9D/l,i5tnV yޞp\ Ey~֔8f'f\YPJ=nŃ ""m`Sy"wf*>VnF{60e=aN J0lEM/si^ϏΝU̓xfw[u6I݀ Т,o:G0/Q ++mPvn^[R[tM U'5⩫0.k֌)|T7Ã*?i죮66%jn0 gz+gs0gzw23\0#jocl_%3H +)pTPjƆ"th#k iă5!!5"Hltt$z@? @m'G4_ɚjю+s^$LoPc'*Q,Ox|W(qP BA[ !k]9_+\br,qϻb^ GZ~ߏ4 +h77Pc&=Fa7U>ޒVN`*& jѮnKA[mGUDl uG,$ +2*&?r+D7_ÅQG#YX~ZO{V +8m &z&ee\"]今yړ%_m c/*(#{ɁՅª\aUw{&߮\oTyGɿ2*^vѤtP SAB%T^ܬ2>OJ[Dn}Cn!J&c]ڦyэAc]};ģvRW#gOՋ?SY,'VhR(*(Pf:ev*޴P6 :Q+.B&0+9-Ky&$V nyy|/ 4S)` k^ۏÇ}N-J787;L 5X}Q#Y/}7з~XMWZc#9" E:2g\kP9=@Y)#ØqM[G +A䀘uuє@}f:k<(ql +kPDgj0A߇ϩ +1X/s9<5-Tk1_:xOZכ=[aDKk1h&{8.X?2E`b%GrU@@tg|]R/j)j59?sVnHvK< +oMT$xҚmTΎO ]犯gt2= kk䙴~ٺ|}/p 4z@m_jLPѫw?珩IЛy.F:SsgIW9n r/0\Э3rv^bʕ1U&@f>A 9(νEvڧ-6M64{֢M `Li[w @oZ-z `E<"ᅼpG G⅗Iٺ)WjsO;%_QMw(0$aTEBBճU=ZlJ-(L(TE*(2q/=[Hr`b@~{~7EjWm3>/ߏˉI0ʎ|JAľ[ダ +$gk3&LBnzgԀ jFM>l]dM"sӰtr^3Y5"0cйjlzSɖՐ?r 4^NX!7Ϣd> W\-&Hʕ<__?I69X5`bgFpG1ƚ^+D{&/?NB.Bn ;s̵<xH9 +G"gRk>L\ECV [ [Ju7}Q Y#(RJr9T4Kj^ș]6LuDΠHb3iAjŏ镞UJf~jGI5_D0Uɑ1>{Q%yoYxJԌ?Yxؚ W|]]ٽ1;nq9ӎDZ?^d|'$gzKZzs?0+#u[+ln:M>E1!htP1R +.Nċ=EFHbO&$ : CE)'\h ~*rAQ,H̥7U.J[ۨ8|)LRGѧ2H鱳w+~d{e`sz0=+k} a<|% w,m1~yYcv!rIJ]HwjWiѧ| ac?y=/5 {CŃhĻy;CA:sq1#S$WqpBD%v%Rv&U@t8kѓby-sy p(n}e꠆>qf/0&iL#{:n:\v+3O +In[09Oآ έ `^UJ@f}@~֚օ `]f:~?[U| ?8Yk(PYrYnozbd>@ A@li:nklWRc$I5~W}>4hOٙ.3%ɷnLG{%free +um5CP>1S;? ndpbG9VJ;),8\Q +mQ2'w$ *L3}&TR|E6~z=^eRoad +^tUe)J*ZgP +CkxJDb]޾A +?_~.i +J2Y9gVj+gV'ɧZƹ;wn/>#l?utϮuˮ]#;*H,ޑУ0yL[yV={SYFx/+xk>WK<0|ڰTu E0ꉬw^"u{+|Myrs!\!lW}eP@-@8_RΙ:(dyd~7a1Pm_񔝛~oR=XjgCixLRc6ؖӆ?Gw NM&._Zv;A-P4LDԝߝmh L_ͮ3t˹/ڈhY%<BK"%6(?6p>FskdRa<*9FUI)# +'n{cظ" Y.$^d_ +j>mЍ"sQuG>z[#ZDC +x't>S};n[6dA6>t7<ۯ[˗6 C"mUiH& ?TۅQl4{}QU+#/I[q>1sPojPD3ֽx/OL>ѵ?jlkXߗ˝?^'M'GoaWK?y{+# "  b :#7dP(BPO#IaPY bNi tEv "f9}zZ׎p4O^Bt`:LFthzGe;s|ϧ3 +OrZotʻ̌;bԷVάˬqqj"~SGX?Mn`vz0ro"A'CDk+_} -i֗ ^UGB5yt/ǎѢˁ:fg0/G}|yOsq#)EG2\kG׾H8ct-|t[.םW~ X_Ar:G ?7\<5Տa]8P&^u{hQ0\ CgL/4aKb<Ы?[B%}n]爒w/>pV{;P&D 17 oWn@ sZ#e`BQ^y>׶F%igOg|k=\{=;MlԶЗyk YR[83ǼiCOGkK˴<Xkd ]Mi'دpm:h ZDk\u]ӏ5fҢneć*#*b%{asV/=w dBu 1(: :!(o.`lcv 0o&ٵ%=|$wc~KmxV;t#̣0~$ѡB- BR@ "2(8B + Z:ī +Z[+OeҁX!H@d 8@Vp-Mι}~Lo繴:8J/Yv_)u HgX^ej[HvCL$wpAjU]|&>-ы1jAo3e 8igةNrU-U+ L~?dշ1Q)14AO .\e9ߣgQh21 30 +c&cCpܡ%-Z2h ("r9h Ο.P_R^P}QBQE]P񺩤ߔS[C27QÜ'Xo%ryjZjۂ.ڍŒ (@BTgc p%~ Ko:i' S[:4PAqxrC]QZib;rn܌eHϬI?YJbR+<nZ^FMz~cr~8}j>'"8RR2R:Rf|=yX*\ncTu0ݒ&b:w.!S\Gq7gg_H]4R0PF5H $cbA/!4/'H sY¯xn^g)uoBȕ :(-~NϷI=#RФWjFjytHg[S>ULVT+z#!~sG,.eudžzEB{0!hU(?X-6o % +3kG27w?:ĹO=iÕRy/h?ڛ=?zu#P$9q`X^j;w־CD=Hi(Bhzf:EB8sǚװd[.Af7?H+{%v#NDӀ(b4~ +bx b`zc_i9+vn*)y3?.녡pNh+Zd =Wqڕ|L(a+ Zu&'5, b}`,t1KE6p5 +,Dq]yIx8K~O(%t2l0c5(>Ryͺd+ft|{KYҳ ~nVM{lZ\P* ̳7=Mb>ǩ sZR sXb1Y`lBf%R +'zx՚=~u,P@, fAzA'Kh%S-g[Sc#_2ቫ S[9-Ejvev@ +oayDջmКa] ڴl,6^f0$q otFxo/m,f 'a:C +j:U*\-]`*3P@V$6H/o.7Y=zޤ|Z, + Aݳjٽso5j܎z?q E\/}{RI'( PPn%wZMz}sZ#GXBAY"."BލzsPg7RrLw@*"%=،TB]%1o.3܋'c fzPV Vy}u gҤwDŽdƕ9` cnY(n@E!ZF\ 'Q3uEn/DŁ5Zl2jL®Hp ;I$7p2zӪ=eq( +R)VPDU+VQbZ,p3f6H|_ʨg?{~&;&zTj7Qk ;k ݙ8'c:ùIiPP7}YoY}~R+?kT%;&I`g9 =ABTٔn>!s%юMPv7J pZ:׬iѺf#`4ڳv@XӁwboϺ_u @v /;.McF5j-3 *-+VƠi2QyM7X@R r9 =,Mf/lyK!Slߜ+tdt{s{QElY~mɗf;p+(Ad:#0 cJ?kh:gDVetqٲ؛gƗ\+~Y~O˨/lr⡬M?h|2"0x uHϕBKKⓀ0"tLTD HMyu{-lZk@>I}\ :PޮBq#X_B?[X`x;'ʀaUff]fvΑ:5Wn1`be-0q{܃+KCs?2 +VrM{CYӆd&kX !Ϭ q'x;4Bx&̖8hE>ν0˯h>#SrߓX_  +p<8r&΢Q<09kh3gf ~#ϴ904Zx@A8v#R{c=sė(OӀ#;!vb{{`UQҹ2HyjtY ^Lma+ &SP,羒 B18]Wn`)0 [ Gkn~ƃRO`E*Bd"{ "pbvq>DT\ZRrU䂳%>ʺY6@~9uAcDJC(TIBV;JvB_ ਗ਼JI0_ŸO;klݣ{ͥ~\5z,!po>5 /׾"BJMwWj%V_j˱{Èb~s++4{z-fɣeǎ񖊲y드,\Go]VLw7kpqhl#tٙ8-hS}fUx^3ax Q̀4{> ᰗMC}GF:gލI.gD L #aR%)[6^uJĝUǿwH֤1Cq0/3V!g hqG|Η4]X g9X^{(M;aBqwo?dNTW=q41aџPqVºd`?Q)(q*̖T{ Pu4Lyq}3ӆϠ| .l!-[-SSdZdu[$1_z`&yk|8bm%(hWq2= Za86) D:SX]Q&qhqK-^4@HTw,u?%&kk[[mv*[U9"s9#_$';pd%qROJ! R^[%c5j^佸,e-0jqlCx@ءA,GvS|Kdt@*YANJNK,=Z>ڙԲ$ -M2>~guS^ݾ!h;{!h)bO~| esG!u3 w#bsX^ rb9 ?>cq]j,fB8c>0CYt'uH$ P:?`q:z5gf omqnLzµv+/<*Oq1¾3 ("0 7K0FSUh@@ +%`X9mlIDiMkPa=Q678$NO|޼ͽ#CnG{T~|]W0 &6x 3Εb d)9[S\>XT@V}dL?wv\}Jnx( +oemKyvQT aeO$gdBJb>ߜ 6~"&;5ˈxNo2Pd8ָZxd=:fMx +w<=3mEK,{<s]q[XflazXUy4 i⚟PDnrR֓cAs)6Nv/Bt/ڃg֠ݜ)4F "ZB{ Dh&.=N9Y:&y@LJJb': ?F(N"|Xc#-ˏ]Mj'/?+Ú@lѻH0qZ_@AAAG n8(ᆘKDp=xO:u aZ-6WG5U9.2¿ǚQ?/J1A:;Hwi,^uI΢dΟFCE]L'zm6q;W`΅09Q㔔Bɳ I+V',Ya`kAv 0?nM֋Zďaw}#[.P StSfY'5rCJ!;2@V7dF'aԦ7'Iלray[G 0BŭI%XdŲ.Y,O7%1:h?T1ټ-k=`쯨T9 +?G}}Gyhc?|g"Lg\=A (&ޒC( :f#F4fvW|HN)"{$=j\@/~b/V ѡ[|ӦVl|O$2Ԇ^CkBv.|tk܌s$+Xr_gܿ-D,P?Lf;u;V0ʁ)>0MT&r+쐸CeoEy0 7Jx G' ~u,t+o 0-c &ÅW/\Ag7B5GY[e>(yQJ[o1.]wL<;PcuN|%%c$QEM)a蹏o.@5j2U2l? iNso-qĜj+ǽ?gM8 |_I$=coߣ*?| 1Kۊlg|{B1VYO]AϪgYz2mwG>N Кve6 2?k54ٝu#΃ +>I(S=\2f*nx%\bE u+ms2Q{go ߶3/cMsgw}#6a=VNJ7p=7q_EUprrPr nI2$ }S+I&C^Wqt=o1,lesOhN|A#gЫu0/^Ņm?nb X~܍8]˦:U'7wsW"$^|6f1?K8@&,B SlTN 4ε^0޳ǑmW=hT؀N U, 53>}cblIkUmxw4Tawo$'ڬ S_Ia!xBNqSivU5uW>wO8_;/Nj{yT=a@s0=&ɴE?{1p4P֠ 2eǰyIjx?z2,x4o `wo yay'Dž?x  Kos ae'sb{/)׿owmo9+Y 5a`L껞`}"5\8Sē~厪`y5;о +ƁҭH*;PR'/j4 @E׹0+lv8csByi꡿e<)E0/x?0Fd%؇×o `@}*>U ,^HcEB/ ]JK'j}i6Ws>ۿ.iͮPp{ +FM~ߣB@S706d\R9s݇ eJhMUI_\3m[;9*I=gXz]X\轟~=m#؍@^@p񽑢T{\_zw:}ѫNejO_-l~*BݭvG,Y9-bu5ٛ/<&7%RB\Mo B-?ŠlWEb ơ ;u8A5G%Wy([g+GOPE2ai9=Zc~^d-BIЩ3^ju`fuB$^N7q q |&WDJi`1{U73C 5|_S2AMs%sS^V[_%8r`#~18`Ma1F'W&a'Fmۀ'XG?{8^"Gy7,)OgKû>;}h&Wi' oG _LIt?h sx+g߃gͧ<,=[{Y |b89${ L>ّkqȯ>. F<ۨ8"Xm +C蚸iysĦ5d0! +FIS z@3$l++oBAFɀFyjoPRL@h"?S Q'o=C7ȣę'2Ih}5{);`.YpۜjVRx/6ܖ4NIf\ޘ'`I-xkfVԒaр1Aamm"@L*"z \t܃\sm쒅W<*ؐr'jŅjq A~9fߟn%>ZkC e lI@R$][ɭp&۬}+ղɁXw#K^7V ;bl7DI B~`bImI_D"%UɝERѕIU+=)"iw?ijGjz PJ|Kk|1op]##Ty<ά%ۡ> wkm%KY(LnfDcهo> vgA3j8+IQI{V6~]nTx PyWGD@]Ev$ յ? +N.X^Ǔb|?m=?P!/vo:3R8O؄ @ԪU(;uXNՔ֞9Ur+ۙ̎oqvOw+Üvwyҵw˞U/n,x@.hsw>3`ȬH@Q=¾[-dbL'0Zlz q>G1 k=!r gWŝW#v *Γ.m8}#ރYp "wtF'oռcAgW-6G,W MofZV 㝱N dñvӱӄkofr. 7 y`jJN47R,7X#.sgg4Z.< +Yo@ VV !nH  4[eVIA, `27΁ȄA4,ccd|OK*dE)cN0MT=;^s>s;ma}]VUQf"F؈11FxfvDԏ' +܈8cH']XwMjHC$5 2o]6uf4ZnPo)k#-W1@^LGrkx!]!N tEe_Lsmr^[}#wely[Ѝ ĺĬ"odY dIgB'i/"'G C$N(EHB5gFj;v˫Lͽ[=7k,q\>gC*/(~gV隅 m@Y`͊y--]:z轿\?)5OBz~􌴸#=l/[S7$#x |$}'(X6y edOQ/56Gr'3MXpfG@ߐޮ?AHxCt|~Yq8pdRUC. H+Xz@; \.ӕA؞@󟅌.FRB#AVlۇ*wf* be-CeNKp,a8M8x84K?{e:ȤO X&FLX?M^Y%C(ɤĝ+F1Xڙx?}[`iXϡܴ&K{>ѿ˓9!h,ДI/f֭ԭe&xeV{VOо[cxS+I-]>u BgPR&NQJv:7E*$SZS 1a{%.ϫBp6 ϵNE7|8qoY`VERdʚh=DmUZ}g +TK'j;㑡jv,l ş=Ū=0JM #C[AJLw` *"QEM~B 䉃".tt)Fj5w#&5NP40k:D\O@G4$.s9ޕ=d`R#sc67F.Uq`BLH;/&S2Ϛxʰ 0e78Y'?PbzsN_ t!j5C, |xgU"ƫWjB;C80H$n>M_>οo_kI\S{deOHd.~ yfD3"]?-*Љy1T`.L/DRʁъ-|ޮ w! +/uP0<Êxܡ̈́c?l9U^O~rZxI<cUt}Ī?M?/duc +aEqeTw Fo4gMR*yGʏ8)`U[pnPEs#l<*scQs{ Oqb\8&"5"+\ 4u. YÒ20n`X @;ĵaz!,vyϹ}>Us|w]W],ϽU*r_*ϭ}1Z*οSW}bwme[ MV(+M)Bw%3'ֆ)F!P,7--=xr-/]5ŸbUCXu[}b8 {k0Ƞ`V?~ù/$Rn? e}_ ywwh裔qe<mFV>; $TqIܼS)Wy$j/>\ >f-<{={ w :y_# Q41z$Q7+{V^(Bg{D\B`?f.p8u颗xH|n"ɽߞ&^/nK[|`~3>k눧r0צ-IY83dLʯco׭>0 A}^#2Ƚ 08??tV-.1ލxME<^0grߺ#߹QW_(KEo {vy&N^A0yǜquV9 Q6vodI+ze1. rfJ$|" 5KACX_3hi5Ù";`W<%1:lH btc0fR+w>q-P<1Lׅ u -j(.V Qx$Q7'Ԋu$X$S|?u+r%RGag6:Vˇ!8Y4#M5d<-׊hfw8:$қ}TOFe /+,G y300EmVW1BP1#FDM +4ˤF@-+df{ȨQѸI[93޽~eaP<Zŭp7;I&va'\;*fje'Nk(N=Aut Q5qM"C []iok{a؞W6R_]5:f|7mrf-^&QBR}x%. نק6(]GQB+Wzw+ֽRAHޜ `lXT#$k4-\`3M 6J`^ٜI&FP{<|a LJ?R%7&"'7{M|LD9ytg=FK%tvƳ|iIEgߗiu9;SSqײb`GT"خYמ%NjsjWyJN3|j oI=]TB RBab`iڗ{R<ӳ;DcB9tzQǖW$9-,Q +oү objs=_ePgibg?,/wQKK)/^,^e؋0mNyucn0ٵl++뱷hWf*hp%12`$|FD\$LiXACLCq=|'˟o#5'*}PJϣAOȢ'>Hjbd}7Y367qRIoFebUW^޿,?$nhs=տ ]~GoN(~k+@vF92\wjW?}VLQI魙IόR&$`n&?-wh M-v3Ƚ@fMyf]A'U&2ؘjYy0y^O.;P㼁9y@QQP_)CO7 XAٷ5r?sdOk0 - >;k=U'wN]ZsŌk5y&s`[8Y%#6<66Rr9{dWv3HG?>'Zo}3lD 2Ta0qǁL"yo^epl򱉍O=,rrR'j_~e#HSA$'_Tmg}]u$'-q?Xq\g?͝;=?τ/_?>?>@25b0㌞hp,o|-GbB~sW/`#=93a]l>?6)e'8Hn"K,1J*E EQ(/3cxL0|3fT8 qHf ϫjMGź½1ogK`V-nkcpq`w|=}=Nls=wOwt팫5CWfsFm]+ +$Xg*aHGxek<=a[kH=d2+yo^z̡2k%V{z==eC<ŊҲ {Ɋ*OYx5 +[#;~Gt6 *Xw$w6'+asD@3$\,Ϲ@-*M)xBgLfp87f/3=0qYlɐ%cNn rAݱΠ@p0 ggV{g:H;9USPc3czÓ b㿥66:BU6l -xK̋l8}M(x"ٕ• ,)al,1ee~`ߑ?*r``o:j,՗$遲Z$?lUFb[Ք7 |3}=Z4)F^跹@q{HH!"@5=d`bȌ7\N8d&1 lgoaé0F9dni0D#>JL-bWI$IbY@-]SK^eVJn)T ;@Vx3WmO9Oo_+ϸ;!=Sx^yb @%`?<;^: g}Y5Z} !sNo|nKY}MBr̕=|r~ъcN0 jɭQv aG~e?_|3\cEmkVw!K[?w㞳^??,Gv cp43`VFVAO1^A 4ܸ*p|@}ZH(Uhu#KT* е݁tW}L "" "c)veo?miElHMN^ZG\Q"oJQc[q4AZk@+ >@>{ўj7óu?518Lf&{#@4ig?eBľK5UM +`!hxja*zGw7*zh,=x07!rDEUQft󩒙nIK:X~>>~ ?Sd1Ui:t]#6STVoJϝti,} +ӍtԞFnrDx7nw]w=d 5D0VK\^!1/|Lć|0Q6Xg}%yy!1>@Gʔ$x٘5*Z1e2}ª7-, I%w2ɝ cj[4vk`)fÈ7>w{4Y!cϻPĂp 1_,uy2LB%Q@/(,Ww([DFQ7@Dd::FYʢ`p  \$ģJhpt@UwC7;}6`NTS~*dǫ}N:_vZwthf)WT 4*-}BV ר9X߽T~[%ɽJ`knKA3EE7 5,q&o +RoJFZǁ/QqIW. ?de΍ \F'(o7fF|,C6Z'% s G6/w < h-PA2,@ 3{*W$adTeKL0ٖ)z  .~T澜i6(WD\ +{.(@رBYaӒVxd}F !&1,g9C6G$??v<$vXg4&n8]"\~P倠6Y(SEj2)4`_sø+zk Cp|sv%\S.矹 l谲̌ڟMo|{}م8|4+_Ah&WW(z#Y +7^Cz6G2=W{&tUMQt?,\m#EuEu([7U웈yk fRŁiP+j*WVsNMqL$A>K:eI:,6wIfL᩾䧮mW8GYA^%L4K<89Jg0ӑA]ѡc7!*!̱(԰i@I@% ]HX5g +p_sh34]*Ko=c C~2V˜O^)~WYM^~iiq5C5t@P&KQJ|uSqWim4F<˨~{Z1G-eX}BO1;znjix82)vYxW.?c.jKѪq8dNxP4GbO4!eŤXf(aeΩ,2RK5y+\sEL8 Z,X2l).pMrzk$Uz7~ Oe@w1%Hg(B;1eQq~+85hs +/jMk!d!s}_ ׷0oeoS1n|#^A> Pu1!ZA;O[L_]L qakA48 ;FbC9Q}{<6gj2R.JAT1ĺX3;Oc,"J`_p^@'P[Eћ8sC: ``/y~?^>x^±lzzSj/tcMF =&Sjfan-mZuuB ǒyN|3uzL|-쟭FG~ް6e `E|`1eIieJg6(de|+),I-g=x%;}4?5I#{ O+r3 D{5v=zt1HǨ **fj]˭KGE<;ƂQQiU[ +(c3B:#qG(_{vz b|VYmoQ]?}Go?pM%ؚP# dB:yLj(o[S|Cv 7[Qᔄ>JLm(re#s Ujcp&f`Xh+,DI2]ư̈̌i 9iYfN˪ yw;.?XDL la)K@o8 Zfp&܁r N8d|'苑=97!:A.`Z9.SS3|-}g0qgO5`Fsr&upbL&Y])=U%wsh&>đ/a*4E= +/M>MNO˛:96Iz0gl53 w0H{(nA2eKx`6HT A  M"3篳v$zs-gP0 xZpf8{,%5W|d=}ѻy'hX; I0P4^֧U f)Jy DY0[;v`f :CdԮ?K+VlpvnIBSEvsp9 +!=Ao<ɗ/ ]#Θyk?gUpЯ`[!(<|ӫie-mJ p x| 21?McS@vݿ;{T݊Xw{1[34ljR$1bvv5\Rees'xFћ8:'lLGbڇDS51@^-[5ic%L#ٚt"l,C2<3xTNR. +l"˚gѻ&h /91GeV+rEorXV @D o {{gbf lfޯYש%?դT;璥Щf=A|y8?.C?7nm:1J8Ah'Ȉl1wDDR1@ s$hn 䚏4㪊^aś]@? yUy%/HXOI-0n2 R$Y |>șQB|Es>1:D9 RW qD()XWs6 {vr:*~sk +V(@9=j #)K^4[R~{p=hUZg,4ڸx@5 cn>xwAөw0i`y_!+#6QgsH[G47p#$Ɍٗ /5R`,+?2e0͊,޻_S7TZA[0-Dg'Q`PmĒGn.߻HDb+Vm3 +!Aͧ}Bv}?f hn{ >L~G5ea"- +aO( HHl =62EŕvPvqv* ;I wKHvx;uWO1MmlCh)⋂Q@H@X=:c7rV0`x9|u93Z+Y RQk\1f6;76Wnuf(GW;6貛]W2^ׅSG#ES"S2݇v=5g10CbEpĠF*AAHxQ o=ȝ&>8=Mf\A&a Ή@Uw7xwN|)q-~",BAi*%i_, +hodrNCY}$[ %ٞ/?^m[1Y;[,/~$H{Ր<{MѷY8EEyEkS~`&y:[72 +V:HUl3Lq//d*>spzt^lNP׆AyA)"1Q3џ ƙ4E#T/]Li#_vy6)2~z _h?>}EWZK ?H8$bV׋}^7:˗L.U$^P$*Nr&:qwu|ݻwŽ{(Vk8~q6W~=$KU˷yh `UPGMap%qVڂWVxz]M9{\Ga[A4s bx /5%xuG]Xr{;Cb`ݻMc70\`_l\n +C0aŌ 5 +cwVjw^JaJNi=@}n<1+6-3RbfA(E-yi8rFde$l@F:YWYa> ̖ ZQxiז\oϞگ\l/sl{Ӻtl) D1-fݴ #lv x&G|gWp689{zTPncuA06(WݖјEoR Ξ|ùT*la\TP@q9hO(Wl؎XX|9ՊN &#ci _b\} 0?̀i Qr8tƌ :rDAZls d!|1%ј)̦b/{,0r2>G|/%BÝTZ 7^{n콣s^{r7DZ(yxp2^KSՋ@Q3>yghAMJez@] :rʀ!8K-6O`՘'3w}XӾ&Lȍ4%UbG]i(3?2ĕ2CFMw&'ďqHe6wpZܩћBk`8'sGS{??Gq>~4Qpl2pO +++C v ,ڳr'6{=NYVgb X\;?܅yo7ǽ,2v{ UjY`PnUV0[J + 6`J~ߕm}1VL6=#拹݌9 ,be&{_v~ {|91FM]N&m]晬1_8*.w.S +ךsW8 z}#ժ!h-м"-uh'( ]ze̜ ; ql429 gڻ/,;>t5o{(x/yݍA.@7/aa]bo̥26ES{ų>B[}8õM&OvÊ^v-hU`X5ՆQfv0?+wlS_y1h:Dp(PN N~G5uq<(Z  @ `IHH$DnU0Ukvz3K-΀Ze#V +EEYTZ-xz@%!%,Xעtft{;wF31L0ݤ?@6Ϥpiu8r(Qa1 } ӴבGk<ז@_֞ʼn'kF: dyKcG!ILȨc4lc~A "/c֡@_Q!ByEbO^J};C+vEmJBt\+ ʌ͵جcJijl۹XQyPO 72\ l{Jk!s ܘu0uy/fݽ]:zgkC2N`Kc6<p<<{%\p_~U+o=;>W;'{= ]>rߧ[s +GĄLV + 6xeHC >Z]D4(J{):FHa0yN(_G}>SPE9ԶX|ygtGёr.B$jEpzgdpyVGI9B¨W{Fok"L*P!8GFE+9k;ߖuTW3zWr#-W+gpky+/q/,AjxQ^"㯒] vV&~.ӳʭ \[vƁ4ޝoBq8ypn߹~_mO yrۙ(rV<9 l%@[K+R=H3~@Yxސz`:9S u92ͦRc;|fR,okWFj赟$< -YP9z5"{=8K +ŋY:#.ul☼/.3rY?g+˂0YW%I_{^|; 9 ՛;7yK{2RC&< pMȨEzD~MB.썎Uu +>bg.WN ARp@@[6Y 0HgC1[Ћ%ۯ'0 6V`p1%pdC^!G`Q( cD^ uv(s{C/c(U[޺am;؜ #J V3Kj`Q*%_3\&)Vˇ汃={+V61y{.W +5|!W +\l5u%^VpxqB5g ꍾ1Ql&y_Dca]ΗS7XM7 + +Y\s?81?"5=4nI{Y}?+QAD9DڷˑaK6)=xma :Na}/2Sz F,3^=Nzުk=[X뷶^-NL;z#x9w[][:0Xd]:@kWFUqL'Loj_#_*g./JUdL6R?>i#l⫓6Pe$'k[:3\%VAJ|6[7[==;^UÕ`^a"_Q|W㞮I22>rl9Sl[Y)J/SN=dPW?2w`SŻj t4o8ʄ.={թ(> aÃo;$% h!`Sy׊. .`@O(LܑGuIO^ݣm?m"%z*!V[oYCo{%7'F7H?ie}lLgn2q3U0FH.!oz|X/{E& +L7dx{7y"g{Vl;$.z}rz'DU&9bfa Z0rSǘ ^2rɑ+0O?AUғe,8tdqK=Id xif/yKV@FSrUǔ[6Dj:$7AŒP2Afw5Qĵ$v!qU1*HXG]. @$ȼ33(bat$~/{s?*"V'LfQXڴ$^!R/}⬚K' `Ej0ҋ 1otѯ,m7&% F,fU]#.~x7np_ǵa~wc'N`[| 9>αAW|9AY~r?s`_ݲ7 i)NS*b 1 56Snxo!+E^P ^Yzn?("e]@;z4;ei f k06 { 4Lf{o7 HֲQq9E +F5? r(qЪ:GRݱwXAڇ~0zZu =|||fb͟@-w>&;yASԅm[ZhaBϤGkTCw<S3oo[Crޛ??i7l P}ݕZ pGf„ɦT7zB~cgrW ?Sj5^5)C*B[[sVGdn[':iGz%0"o9z(j='I4;[4;Ktǘ`LpWDRrzS0$ bl}E3`V|bε} hF߲vS#1&tXQyw6t~|5'$.kjAƖ]{b<Ν0ˑ {`xAeYܦ7%92ܬ¿?#%^K*C+N~$3LB~@/Gz Qf\oȉoNvs"׹v&zB1h8L`@bMּ9'm0T:e91>.z 0k|`x9a_vEA?qmkZTFUxWΧu!_ޓ#oǓQAD4HcNYdBμ LaCÑȒpG0F`ǀK;'@~<"T*tJ3?SZ\QǢr?Dl?D{uhY.#i+5Շr)έ]穐9š<4I?U)ྜྷ :SKQ9S/xא)z4_qJjx\fMp'g[Q__ {Dt٦O*]ϬErf7by[x;lw3a.& R=ߦQsaTKzZL`܎q@ |%c[:1eH0hKw7 {t)D~(asg(mVgBCL{8ak8J #]̾,ZLۤ_8]:1fNhyb^gq;OP0DYjΠ[UЯ!د`DVaz/~xGj>4\aQq+VCqG|?Tkp+w \]#/6Dh=N=)f1-^lk3(]k0,;yTSgƃ*R@eT,a AEY a)*GOOSS{X۩"*0E+*j]*0PQY +*-EQ\X$ Dx{oE*i?ޓ|~<={'3Jx-~'z^ i8r.x@ Y; 0d鶫ܜMiD`=\Vp85<'G+xG7q ܐmBϲAoƆnYejy/{C^ȥt%Q%PM{'sITz~.g/άu.BN>Bz|Ek4]H*6x3Ls:g;| wn?Vlt)NfV~LNyRZ*¦:RqGL+\9c3)̌=_UP+ѓ0;o.xXrU`SɑF&߀7"!XM +gf|03$ФzG]'[H#|L` |\(Ƕ!&r(f Q rip*l䞜^XKEenG65W]ฐ+TkDhX]ȯK`X+[ +EV.hZ%ah'#Yu%: gd8.2B-g0;֯=fl0kwu16\s4#q5?E_/##A"[y`$A0CA)(v>C;b w 20M۷bcb[38B6ó{^博g B=B莜x90;s9Ƿ,##R|:5[{lqgS/l=Ӫ;MƒUW +R+.U}yV]btO% b9{4wϝHz!5Ez}f?z\|W= q׫~=jjW/3~ѐ܏f5?~\۶O-EMT|c4GpiC\jW=_F)7rMF' ָ.:}0 %0lUO><ᐷkRsq=ߞ? 2lEB+X7K~g<ǘanm@pt$pHҬ ѫ+>Yjodc/mGZ)JD|mH^'fX\ŋQOe4]dkUZZKk?TPmk|W!Yi!&? JoJ]ˁS A5@m^RsNԲymu6zHm+H\¨06ô +¬940 9̞':CrM˞0H_`.F!oq2sV5? +d_SRr28[`^듖C2ٜCYâa<B2 0-SfayP\E:ylH=.?y$#ho^Mᾼ b[n㣪 BRH">A2^PTZ}_KuʅT:ֹQ?wG댅?KT bOP6!{c8q 'τ&U$33ZNh,L:6ud}qf䥵Q +Q²䃘bG0C-32#E5+`.G95ƞ+3һ%v^!ٚ8hm42z;^I؂hZMA_A ,Bx:Q;4ח>P%DAȾ=(ˈTZ.zrڊ6žNP>9E#B9Ne]}L@(zLL֒a3gs&rѤ^Qr,pLhP_n0=F!c8CTaC'9S|ux9D:=(Ci??NO:׾2E7R$'!w$.Lt<$X3OT!>~ 3ňUL LoAl'f 4u󓑳^ 6_3 Rg͒)x1>_Y- ? ΝHSJ)s&pI^tO x)xRe +P X$/`t|-=KVoZ~u}$dD`"$&,l-{Exڄ{b+s:~5ugq +t}TȊH! +:*ikΎuvE+Xjjy)EP]ڵP** +\H@D9;7 ׮Zqv̽sq|?A9Arx `y{ Lv&ag1zf0Hm`|z.X#&0m#7Wߗ6;Nfasu`0CUI "A>D(NIuKdO.`Y}.|>Osgޚ8vO1`;G7 YS Nl}HyP3$)H=z<;V8l+|K4l<50I_a|9L;E{c^"$k ;Yz=EW(>ُO ~DKy0Ϫ,*8:3ڿKқ9DOXZ~([v~?O}+ R#'Sw~U^kqA;ӄ~Ig% &|Ӫcrq?2ZT3 |a"WgraT^|L'W0|(8| caU<(<4 Lp'E/?: "%b#21 N@ڥZKOI&)kSfI:}0D2kv.V /{ QpG4ooOm<=<& +l`Դva\7kWlCEJfC߮EM^t.LrX0zgh{WbN+"<رs3wfp<)Rc7wqCo}!_8C%T~="_dl0;aG8Xz6淔N!~GM +HX}164]uN&Z0dSdWօn +{cEx΀Ԕt}Dt*fFڛHs|du&+M0% buy)|{)=۱ +{ɁƓG&v,g<'F͵=9aQa*.3C] qp,ೢPQj>XI&3E%J8t=NЧ +Ww6xgz~_X?zJe +)_c\en`Q9`Y{0kY XMUmx73۝QPCNayqA+ ?O1zi"ۨ E?~.LmK;بWg2bi,Fzvr P‡i8 dSl*"yN,m/JݨZ/:𛂹hx69J=%џa-ڐȷs>:P[:׋`;RjhqZ7{">0ll[9jJ~/ARggWpu\JWkD`}0#OW7i17E<ǫ1ϯT͞Ҿ܇cao/qm{vmX 4=a&R*d1%4I"LDJd@Ś,Mé=0w\2H'Jc-?{11 pq4Hz,6?\cx?d}~H1ʙ$g#D+Qt'0F"%(g WF=?~ +F#AۛCs^WZ]s~]؋e}`n>qN7,b@|L;+)јJY2/0e?!<(~!!!>qpj7mo,Z%Ap  0rrj6A~'q~0e! D=zWM5rZ(Zmze;1% Xs$5 A\$tCԷ}xmܒ󤃟y,h*ɷ*3RK-?0N"Lx#NVVGF%tC=`΢Bn _긮#=SE~_ 2c 3z7D |<\$nT E^8s:\yĉ3#[>O|mܢf9d.ѫr?>`sE4, B_txZ<ԁVYTؖ@'y,dhzˏ^ދ޿H1y9$oЌ7 >173"$eN`z4w*uk zv,&=` ;F80/=f P~5}eq<Zm}! IxJ!/)nB GN-!ϐA`A\s`7#atX*1ދߔ [raw]^ö6;uЭgY`|,G 5vxbZq= ޽ C?hdx"fUDI֑9rk(])8(q@7\QΏmbF ?`b/%1.E&?&B?cLl7W]74 #)rȾd=^ξ`{Œd> bdor@%VcU91XU2wp8&Nē=-o-'sېp8*7-a)K<3SnMN+7sӸz{@S4t6XV@ߠ0 -5 }gnLΔ &Џ 2wvUqvKjUauPW?_-m1G#؞eshdM~Edz]`XJz +&0Cjg`l/xϿ&҅/]%a^D;8v}3Hd2KYcQ!u<ґ8!=)EG76lŒȃ"0@+jEyz=j<8GA/jWr <[Pd?hB~|Yk~ׄ3 ׍םs72{ + 0Vc ~q9'dIt-L7E[3@T2PhAR&% 6\al_gQɊ.OyEH$'=^pj;gAvhyW,e㽌ka e[:hn4Zo4d#ō5{Kk1wU໒y}ZU#Kv{v|ؚO|YCGS(8 umgR9ԖXz@ RrM 'kIlS=;cqMlk335@ǎ|xL4Y(D1歾)OG֯2gwG?9*Xq\A~QzfG GaVǑ/k\Jg:nKeZDAt(y|cMH_5f j8#7woĚi7Kx: 3!J6`Nb=G,̽a yaŅ#awjAbc{TGis~VY;TRq"(发?:\f8h`] `"8`xuR^40,cY+cΣ8"nGQT@XQYB@yB@Pvڎ==g(3:*KbpT7\pF,^&$踴3S k}Fũ/${}i(>ՖjH ,-S&>EG8G.AFy ^aα=j/u4A/'`\ZWPr`wmvٖr dfIp85G@9ɭ )m_zDԭIvrvS1pٽlݾ{|U+eV{Jl⏏L`)f"E=k1;ߏ?̵~N9u,N;&~x8oF[ڳh _qJ-Q_ +̐Vc4@Y%UF=dRz+ +3?~! #q-k |W9/UU»zm<f:] #{B.cTw87pR9|<7O{f؝pg?lzj$׼Fog%,B1+lZ T nHَ|4&ѓm$+ e `USpaHH8bq& +|.ۃUg]Ň^8_OXQn.gPp @#l~Ҕ5S^F⏟fܳAzIQqh\ށPGP.)^շB +z\&sfSΕs @A -Wbdr(:;Yn=.k=z=tlJ> S:N?f;ǍR$*u#}a MJr^{^w{dy:I6I1EQ<Z+LDov}%ck4@8D6{Үx?oXb\xb-} N җkym!Q= k.omUq6#2MBgbГe_a]fN>c q7IO@1+L<0(ґ;+Lg텷6?~ 9  &U-&tuoʚ>#O- ?>]" 17S# ̊Mﻄuӽ0`E7dxG8O.{̯@W*8pC珗2!G(2rF݆yo"q  `~{L? F2V xmGTs%M" +nƍ[ ֶZN Ys>R7wD6dx' .mr"1 -$՘&=?t]NXv oqz?T"v0C^Xr70Lr`x;'s _騡 mD rdf8tF.x~^T~}B|W?̘ظɴ 5Eȷ^gկqӾؘy'B'4A{?p,s1k5,J .B1pxsyupPr8s@5qK^\|{MyDӫHv_N).QʇReCY7n2]lrG 2ŒX7NҙguA?jck$kdwC>L%(#:?8s3 +9g2KTjbP6ޡjh>F +nW`vjg0\mTYzV)<:n49N¬nQC9K2%mi<&͑z[z_tXb&IHw)+Q۲@5} kIHz]?t؛ Fk a?;Ouq6P Aً[A 2Ku ƆB]ƌc;"[vGmaxك-Ry31n츨͹A6;w5: ؛ƒ?0q{=XkhA=X.:Ve$j)uf* uqN|߅uBK贵88]y$>ڮ9F0zBYO8Vzbl8?cH^3ĭ;ע/:߃'pWD5}ǯ9?Cb0с vĦtfWV3dEvG1io2S%ЫXR ³ȬvTrcoRKm?KϥCk .z D0z/9]Xva8~ Kmx"v&Qk?:>J ++l"5Yg^#X  qQ T0Yo=+ oPWqxwyH,F@ +TaP@%(TĺV$GP? R V3qf~̽sumHS>*q*;7Li,u_ýz~pݯKxp 18 A{g,!w@US݁ ֧x|m9?!{ ]h@jNp2r33@5_ 2DWVk侵NdiSwɷq 8:qC5簏<1W0G)1-\<s5{`=4E`*[P0AToaXKk{5܊o:Y,5^s=lվc揷?Vvu +'OR3__p2=>el0fR})tЭBQg >,5/M z o!vtc)&) C!D~?Γ1HhOPc<+oW4K=`Ϊ'Ё"[0І6h  + >ɸ\t1+nAxrifq̿/0"|O,i }h !?Y5ɞ/Y\>9YsnDnM ȯ),ԎTmSVͰB f0X]ײI"rGjr* ;+s f+nbCZ7f490{4}ڛT#cj! ͩ%渢o{l?x1dfSɗuJn;*<$뮚?#<;NueNXs]2 ?Tej.#rSl^QT*>ܴ'g }^tZafe' "04 Ru(LvָU"m5~9ؾEP} +)W)?0e>Td$ҐfD0N-?uhu+`pRkf6>/!4bN +hD\:tHYHhU@h~`QJE +-고>2d2![E~f+VxgqgL:8M6 +riU 4t-(8$f&&p =;#YʮN\EMąl$Hzf}MO\g|1?U_:U?v +(+g&}" lɳmƍGgbL&?-?Jֳ<d  cݘD|V6l 0ԊE#Y#;>rϼQ$lLPP͉T,3hV9Psq ` +Cpy[$Ki/1Ca?`.4;fEa (AomtC$ ^%}} P}\bw0io ,9?}-hmp8}InYPXK@gd)n֢,A+EDB/h\9 fa@GM<\>Am1቎|@U'> ݟz&trΫS5ģLaö__a+:Y=ؚRJ5 +MX5EtUz)c-OIeگ?q~-'~>ߒaD뇙jןٱm?Nmm +uyz_GiH~p4/ZnƪV:wuo ^U^3 ++[ukh߲>$qě5WVhKCErXZVYqʲy])D˖}iLk5#Pԇɺǥb:wT\sc +0B m5.6W|0.(l)_~BD揼Bsq#M^ iƖ7NֳGxzd\eEɊU%Ֆ} &]w#T+yq<{` `p;XX^ԞںoZWt} KwA!KPeeV:EC朡?ry?C<ߏTL sXÊc䑩Elɳll-ߝ +[PKT=w4{ +w/ ZW Fٝzi->1#6c5il>+k?Jyِ9} q)~o.;?VTjm_H`)zE’| vw\ j@_[aԑ!V\0_,.؆*n1 o}y,;|0HӺL ߻mHl,.*^+cdއ/NUJbW{9^1Ts(S}=<#A >l@% KJ۞ 0h5qt{4=VU$=ȯ#0C Q^yTW/["`UB*$VpGuQZU2xڢPQ֢@QE-JE @DH;&qv:933?}{>Ϗ0HMams{lr#wJߧ] j4_ɦ=iYٸ,MgJ?<`c׭f?|&͚r5H h^`F}ac=&b3oz~!]ę+մYD_Mv~B;;4<ޛ=@HsPkp'eBK~|Q_WPj1 G  *nD2C57jLXpUc@"p: _iX&ne?I.eL)/w2t͕agW )N|䌧4J~?zh>P>9nlJ8sŵ;;ğCT"+f`oxJ[v`ِ̓fJ "wLK*++^I/sF.AήA%GzUstm6F+""Be}hNR'f)*یӘ;J`Ocx pzZZlj@S1GYrt+.x!'~w4F:L*ݬs`FW:ݷ&ݹqd'EQ KNt\Ň[[@5 ߎ/!pEGue1vTOg't?}ҘС!ݕa`(@|;P˧G̈́:š" l] B9|CVPjh$uŸg*|e4𹳠k |]lNU*|%%v4?Z*[ +eJ6֟7j0D`3™hߵf&ss*a+x/& gt;bFKtod,BQ[R2_].Q̊1y'c\ݿq Yk 5{&l- +ȡx]31s=9nI"Y7A'AR9]~~3 +חWn g{æ)`hMN*gk6,l+`_R/6Nƻ=ٲL<\/lqgxiz ٓu'}\YhD A: fl]ul7Ips6b^I۫9:"gYɚoÆ#Rz2oZ}'?I2FKlԞ}?2™vLcyҎ4^6j?B݇WڤlE]<= >@  cZG8׋}DY۬aPo-).8-E'vǻ<bߏ}$f&rPPλqN<;F@Kd:+Y Rsm g!ݷo]Fp4*HG]J9( >rBAv3ٙ؇C_Q^ &(@]a޾ 1?c= p0R%~4mV.uFɲNIk`yL- 0絁uQɿ}R@ pNo?t檦-S ZjBʌnZS-p cm;ȱ!GIL9zy(K8[c}1:#t S.5A {9e~ +*gR9Qe*2Ky"~X0A|QMw ّݢ }Q1C zWd]r-l5:o0ee0]@tv7ɸ>h]uwuSe-eŊw۞ʍ~!.L~< =6Huه:HKy% z>S^dl9,q@eAHTq-rQ_ԓ_05NOE6LQU[j' +J]UWƂw@n$?U}-73.PRWvѷC w}@_d i3̤+;7v` Y7|jP,0T;54-PRdӾ豁w?K$}Hp 8d/te2@Y-|_q Xqܓ@}=ED{&AeV_GCAX]':zǁ}JR߯LQ`qt޽]>$: +k2~v\m& ?D$wA4i!jY^Rx3O6,2&<fk3,fpS<ȳaCɢ)@͏u?GqsN0ulOʼn`W'aNIL4r@ÿl;Ds.A\Po"Qo *j6ט֯_5C~G΁LJ7>H߲Vbfj5PJ}A #U@^}z*Z )veuܫ?.l$n@ +bGLCJi5(אwsx!.-:ošˮävVE8N4Be0fU Nrs B- G9IY3G,HX\+u۲¥+뽑GM +{t MxFȶ]@scam" Μߘ\A{s5F"KY1E9)w}TzL2.HWҙxh:i=N;Nes\f󓜴g!RXyxKag|A~ n3vAW\vG ?g'ܘ9U0P۾:򉣌@J@f54nLm ȾgXEODZ7%"Pc ensP*Z%a/6 *`R!i|btlj#v;fÜg'AՔ{e=<{.X>>sDɩ;xnMK]fvm hd _0[MCZl^-74* @٣Ƃ\yēS>=9>wPz'̙/Vhn{X\?қ8n//v3(!7KCIi43lUdY*@՞M ؟?s? %WI. {hc.IzsA&#-ͣ?uOmCe.ȸ _ZB9 3Df}A9JplQ\~ K ;E Y(`B8d]t{xdaԀ1oFV(&Oh9t cxG: 9A1d޿&\s$sy>Ɉj0Ald}'0Q_-\Nwyb޾'a7,Sρ?Aje g6:> +|m=N޽\ A.xWC,.0ǀa"tJI,Al K.;Tdݪ4^#TfHc%;2$wEG:05?* +(]1T9(ܤ޻l@/K@+'~SFkzع+F]_XJ;R;l~NgQ=ϛ`}PUkr s¨Ʊ(;(W8ID!ՓqWWg | +[YRPX<ğ0 3g&a{\#5! blJ&uPn1%*C3%uٗKpKA&w_{PUS[NY6)p(Ԗ^^NK|~}u&MYK6,5B.5zCTwԶǠ{UBk 1a2&u),mYK=ݟW`}{[ؠY3Tp5ݳ?Z)=Z)N,7p0EM9c2X\>dodC1Iֶ SL!,ȕ}Of:upu=,K-ѻ߈QQ9vz-͹"` Clׁ.PJ꿞US /Xs}F@qR9ǢnMh/پ& +?2( Fw?,P^)`9*~U3;%Ft-Ft8dgS@x~qj@i~> fe }2/J_>yjF%'62P@+5{f?Om&) +eBY;&u0My``'= kխ!$…"GϽmֆd;8o޼?eokU鄲#IŇJOFŗ@f:蕐`݅q/T;,S??& jqK4ؙIcP CU%Zx[$PVmɮ狢kr^kG6\% 0Hc?OgyBg#&bQ.Lr-r`\Rk,@F5q0w/{֧O wwpy{bcS\TPj ,AeTPMSnA Pgj’i/ݟ"XR rݳ%DVk?ܟ1u]znVޗ24t+/Ftz*>? "b_QM]y@ +BX@DdO vAm;n#SU;QDA)ZqCqĈQ[(N@ 71.p<:9yw//{~/B#)Oȍn\6٢E Qy{.FeF\Fz*gQPZXK |#Ls/LϓGS+͞x}=x*NEG<@ }0\6 +k@؛# Iœ6(^+`s(O~W5⎛(+b"g4~k6^eL#X `ӆfB(1JۢB$<3VA:Ħ=n(`gt6t怮8GOP |D1c33'tw3igl&,|7G +,#iNexSB G˟xox!o"Fx(+,vMh ]5!3`snM@mCKW\C lix>o2Q_/`/-csPRaLQ\a(mAd ÂsC~9~O4qdՕ b xH]}9\ R<:ѸOr:U9T~ɌIb2b"'ss{!Ԯ 2/] + 䴕22x~YIpYԐ)d }(xiΘޅ=Co;r#gV(-`xe`oA}Rqd*G(@=<7Ʃ>ܜpģSV-n􋿵?e_I0Xf(W+ɜ'3~.TkIcXŃYhClQl =l7&"7.Ѓ{Bאn"8p;X)-=r.hk~p4 BM0Dy er&#O$f E?+ t߱1xNAGSӨiOq;D=Tض +GLF*#J(77GB>YTW)(B~@ǵ"4v~R/akb$U5'ݏnO^tjl$Иy`%L&g<1\{86tG9( |-ػO>;!KJKFo0ɀj ƚb`GT$w\5͆0:'-Wxi4ָf֠/tUq.A߄AT3`kH.ߣ~A4fxw aolk:Oa&Qw;۾`!Os~|:+p-͓`ptR?B*H*ޗ8׻J2 .&2>=7WվbgK +ڎuc{ĊR\b(2~!pPG;8|(u_->8%3H#공4TS=~VTg!:cfAI;ZkשbIc*FJ7(o[c 7;C㷰;|>S? +byG^?E81,T703['hm.i>ת)˂jqMhOh{GpBҧJq`=I@"R!XDAHXQZWKVD@"]*BBB71Z|&̝;~5j:"Gf7&Iwr0KΒRĽipk F70-/T3ϕzލq+r:6ܾ `nIEe8F ގ#=B$PQX!c"n.m߄_ZWCAnW]CsS?=Ԅ}} 7t .]=y|G {I07#_' ֨x'3 gJ$k%~J+s5t"G@!qsKU1RGdhltP^V4?FDWq_,ʇv6$X'2ׂ4  + _: h> +I*5@ huDIvW5rUdZBBgx BBw6GʪCc݁X3Q[+AO[m-/'[4n+ՆL7WeuGn:9(n@AmTiL\n0CNb]΋B ?? M3&:^_kQ;fL!FHpհ+07MmZ":]~VoSΟ()=|EDq 5n= dy(Z[b`?aGcvK׺D֞*VpOԟW3Ic(}ikyu`ZB <;w[ He=ZMO!7of]xD1SFo S*7ݧ@l**C؋l${< 0ׁ(RyPn~0|7 OfR3>_[8G86P8:+(7g畕t`+(8XX"{O<]sY߿ox_s2K{QsSH+XF-,53{MwV:Ѳ]8zKNv `)q݁u,M>X;Q PQ(@y7^< +(8Y\_|=_ S齁V@BJrk61Q䠸H +}?K0'-09 )Ԑ{!.@Dd%xM:C?slhhHv<4?&)^o}7߳C2,@.@bh}& +g'Y7 9Gzkwl49v6{e)!|>? 8dкO?XY dsH5`~9j@C0k uDp8YFU ! D?m:; ]`*Dx?&WhÑ },=xy~fZz7;oS'0Q'Ke5s.(&sjFs0#{+z@;{|oY3QAzw@}Sx*NR-"e; aj,0bs|_;Uq<^V-)]ӟ!ڃktWwQ^sSØ V הPIu6֞wIjJ e7[٨^3o1[EtI`C ϸcS#9VES ǰO$^_Mp~A|r0+=.jI}$z {IJM+fAZEa$ +=Kz$ZYW0N}VXGy98^s[}w"{xs([-"ߐGs,Ӻ8v[*̕P#H_6d| {ؕd_}Gg,s_!<<k揢И<J]x &]7ԓLŭqIVgF:;XZ>, *G&QO{Xx7Ju}yonh#;PO=~--sF~|e5j -x6 NV< DA';M)%}m& &{ LтJ 9W4F9ߕA^jkf>y4'161iC졕a)x0 + b T/(w?M='`8:3+Ϥ6L6-@B5 Fd%rË]q?y( 1|<ڐq5n}24{ǰVFFQ2F|zdΞ, kT(*&}r|ꠗnlߟ:j>_zYۑ=:<kc{}ObS]0GjkqG @x<)LbSIG'):}blf?!yKFA+;#j)zf卜sn $&年kSbҌCUE{ +ݭvߨD)ϼ!VaF7C LMmQ*nm[ZO>!CMd謘85KHq{{Z`fR8y+=}āyo3n_NվʐyOeGhw{EA}nTaJ:.\ .ܿR\ eM&DYB׆**u<.2Ish iLMRQv1Ųd47{+XG)M q]7r ϠB0FEey I<Uj▉]u`FyuEPQQy($TATª(@q%:l0Dn6=ied1Oz9/=>KA=gx$NZ$IDSvPo{eE C#K׹l{ >JPi$^ Nhp~ѻX3 r||$r@xzg[Fojg0:} Ddׁ<&K}}%AtZ,KrPޭqòb:`*aG1+سjӭeR7T +{N`W87Tɠ11XNoq!<<][JA'WՑXm: Vs{ԘAl&9ZPooE0uQVFg2M㌆=8xcZyA^[9o9pcbdBVӧYΏ?_WBz;_?Dv)?ua"\/" JIֿ)t{RjK4](=|ȽaԄ.Ko:]g e}6^*h:smܦhZ6qwoKۿ~GY~}"خ$!3*?y5}A5'GcPc(j_䏑 X{(HRsOyԆƂK=*R"Oё xCi[~_~ٮ*/iuGZhώesP%9}gK F-F,Da,T־ +c{jⳅO[{@&2X8T[q(\ F.'QZM7 W8gQLhøtm0} ͠˜ckZ⾊`cӁUمMx"? ۵yރǫڜg XðND|ÛN tXaFhu#&& "MTms({8^p3 8I;86|;pNPK6{I嚍;'7{`l0X$?5?yTTEElD Md3 +K$sIh[D 2 +DEM +(@*(7a9IO{}~l\[ 1u=,N9s)zL([XE!s}yJH<wömǰ=`W`~@ /E'|=E'S(x&%s! 0j =Ib9_) ELZ3. x&SqOq|:#Z;P9/U]gWdX6x52Л}KHϻ4`F}QԙQTFz*!R$M/ϑI3]x Y];͇a/@% +dNY2}`|I #6#0TZ:0bjɿ1h< _v{tp_({VX0d5M)̱/.o 3A|Dr3A_ͻxyOo=j }!*Ua=UȎsɺt]Q!Y#,@Y\;iH7J +%-%G*ob~j\ FN`0h61wɇ_(=ѓDn0~.h/필VOBlA+h!he,CA0PΑ>1-?숟'<`K^eT-y2RgI#;E|/;mIMroyni3 vj')=#P u{u]_9ҜK|:&&C|!4챊s><#O@S)ү8iڢC(ouaT?A +8b:xeA02PFyz e~\?97N/XpèH>W@sU DI̩ݲ=!|wsm8*"rC j'+JE704aWTɕ̧E>[N79,GgB0 9 +j˱MwXq=aU}Ѫc)4͞`L8kX0Uq|9ݓ_00AGI:7>^0ۼy?叒AxAHznt_G+>9,cO+cXq,8C]EBZ9 ɪY:y0>Ls!<{(yT#wkCԞ9C+ӇhQ;Y0WqQLu՞aK 0UGއ*"jAfGUN=Ϳ!x;?޷\o,_Ηt_9mgh#k(c?QSt[IuGkJw_x%P~Wh"@H k +,ʛW-WR{K)N]_~vmߩ4%+ *=^|)!$Ď[n7n +{XGB<Ȼ}QMU 0d{ ۣ;^(n^E(l|p-K堟6/o{9텽ڭKN)>Ps~A\fK8/|x{9]xͯ}@tkA̞}+4XNK:}]5WC]oL)a恸 "a#/HR>A5lʆ-.02 +%T6g&5FbDZ& ߝk9g{ϚzԜ`9(5B1{rRW6h?̘wp/}{ecg~K5Asdfoo? v0aL̑2lIRWQcruyȌ/:n?ZqĵVu>^Onw! -V_(ztuPXёkO[Xw-88WmAۗ?^o-n4=Tez\.";ҝ>,'tЮcvċ֤8u_H68~Md*!RaG [-I5ɸEaJ@T#Bdq4$Yg +o 1ZHoO;{7NI3ͱ?G^p|lseÖpܾmB{[s/v<<쬥'n^*>kGэcyIE]{vS$E*-*;iM5 & Ul|g^k%V7򤅷.=tRx+:c!\Ld"5)ZG/Jy ̂ri5{X?,q_0.!]Nr*I (x0dH,qXnWg f‚>)eMx=zW0;|1'EI,JI$\Jְ2XMp<L&>cT>>G +QŌ[F;ⱨycU6h=nBA"pRx8\m=r2ݑ-)BzhJ)A^:cn5ܫ| kg)hE۰ꪆ]?R{phrрnCG'fhJK+(P\-9hLX-5 eڄҬB{2$ )f=4}xU0:e%:`fh!9F_&JEH]+L#Sr&i&EЛOL>LL >Z`մZ</8+zZq-{hKRcW/pT#X:x(GbSaofU /Yη&p7 J,U['Km 3Wk׼}u9#ofd?8 yG;3+b: /Yϳ*^7$-k`s ޑ?g14"&ZC ܑGk'o3H01`@ڄr-<Żm=+нtV ҧ/>}L-/pBWg}' _A1<]011m]~JwKPnG?7Np>L]:̐@Ir_F`Mu1-(aL{A1fDn&9%) a%6fL|Ě !,\_dz%b(b3 XN1مfb<տq !T2yd;W9}<_f1dc yv-yS0i=LD7GohM-3eА(qNF0Iօ<}o_ fnBpyυx3kLμY S?ȹsì?зt+СY2L: {Fkh:WUuz`\() |s0F5IԀ׾;d8>C@,G%2I'R"Y,$9X%*"Ee"Qc`6*Fi0 eQ&-''{~߽~~?a#̛o?~W)j Xg\1.j:w?MyJC؄pιFjrhdM,Ժ&%1Χ7쑳ig&+'h/[N1R[XPP%eRMfqdVWVt{/cCG~VM$pyxV,V5mNY{y?=ט7RFh.uTXi`!JwXJg^ek7#}/'OXz +vzЧ޺?k9#q ead.Z+/r<<S= h{{Qx' eքžtYf7_} {zqqn[ċ`̀F1a~-7.l,daEӬɟF5MMktwponT͸C݀&h + /֣N` Q+u5Q_PS&b!!F@$1H +vF 7sdtUP/I~('4_Q#0%Ct:1}9tс-AOtr8#g` +fMy|]]εkmj@0Cpo7Oh}0SW\:Ⱟ `O8}c劒6^wR6a):޾`_i gF9&T}  sUg]-iLl  +pDz[ ۩ۈuxO&e')ѕ<7=%˪%Ah8]yN=ξjnL1|Fx +m"sû]6i1M4UxU1h/ەvqNy\{`m|Q 1Xa? +R|e@t>W5H;b5{J*ޫW`ݯEw4TR s;025WfY"W9" k{X(=Z+{HN)qY.pohFϜ|+0UALG +6Sχ3ؼVK6D7e,K=lfmk`<KPN~BO_i_w,_9̪uv /=B:X1y~džh$Hr"iޗ᾽~" Si:D7Txx `2`毷Owmyd{x3=fF4sS; rr%0`,g#>=Eqzd>reD:xh7N-C} o;eJu^'7!VJ< Θ>m?q uM FL_Sb7U([d<2bWMDHDӝ`w>Lv}0#")|Ҿ9Zs_;"p}@Ͻ꜈NYST6NV`9=ƥE6B6,wWƌ @.Oə;!n?xL..{4f53AWbyj\axqOh伙+:Y;?~?^n=zK+E+YGda}<|rP|9lZbx=dy@nw__0VW+uSʻOw#'PT6OT~5uq<TP +PWBHHEUZX_nT@AVEG؂ UR||,<a@oϹu\wf;3oΙ=swJ"ԙxEqbXyHlJ#%MBIИX@*~%"dSk%TyPU.!Bz}||2Pȡh2NC@UiW4u=b&Gx]Oc/h431E-HnsӚ׌[Nm,|ZWLՆOhJ$*EWRM20[~Ajpd^f"fY0diӬLEq +5&p?Mzyrrw7t.香\gM0懎6lzN6-g}*d'f 7Uyɧ?bVѵ#Ble &=#jbDKý!gϕuyR&R H~Ԟ-{3@Ζ=cK`=0FQx3@3;5 #rc>n6s9J}[In +#Ś4U:? jG҈h pl?yvTƾ87իƠ`XaZa `:wm=6гgIo[o{o-y^5ZO)wgbm~ܦ6?0Kh?<$9Hg;ZX ;M/jj;ٔ7j? :sڲ(s'6I岥}G=cQ pqo)ؾ?j'#nFп;2XˑvE÷EH#4 nƳHB2ɤ@P!{vsXˋsZ=l3Lv !3ᮟ@}`-70 +"@b?MFZƳ597:b6 \By:Nb?=|A{DZ~⾕)}R+\WA98#f&0Z{Ny`沷x'I}*VUtקPpru. U7oS[ C`f+Z!i.⏊‚՘?jO~$61cQ; B3f[633d O>v9hc s_ۿuժ%9rzMQϢt~^_*Koa00 oɊvf`f,ssGǵF\)`}Nn3,@mmG#pQY~Ӻml5bu`? )>Ndɯ=;} w\(}oZۜ5=}i #vC\0: c6f:Xn݈jq3TGGLD16hоtDiAߌyZjk ;R}( F!c#qHg|vyui#Hʉ])n.?\n"H$mqbhƒ$j (XN%qi1d'y|/Uw =} @*P =šI>ʔL)ja@dyǑur6O0"$wι_bftT2g=YIU7Օ$<9^P3>T`Jx4%Ɵ'ڙdD T{j(‰_Ťs!GFN6Z5V81dQn:a"xssVG~9BΊ~d&FCg>(\ cˑw fOlyh=fIoZLil6=J yd){8?HL@Npt,ͽ+=$07MCELT, +ނ0n,HpB`iU'mGtYϊī`B urj-lon] n +]& pOt-Zw/uϗВKb`0hKegdsgOt_aX +X%킷m~H1J+!*f-wq }/8S3ُ^!LńǏo:LCoG(Y<IQu{1F[X2'M?pC.*s^v\O4eO7r KkG.ư0[uJϿ%:ӟG3&(\t1I\T (hߞ)G_$w}{Iŀ_ +׽ $ 4Ȅ{{hQY8$ Ə?2 |zgCF70ӴjEk/wp%msK_-jY&{[eqBe囚n^ewbqzSؾq } +h]xǑ07q@p /Y)ǃI۵X~hb>?z# Hcw챼mpZ1 +sEלD<<.GEuVCT +ƪCˈ)x6eڶf";VYr]&0LxXIXU +g`VծB|ךw/piZҘ,4jq0:>Gq>K CdڈD3M~SֶS +09 +ᆂA0gz S;xqI^tlǚE@t-6Av& }yW[oLaa%_8yl\Di= \ +%;\j"kwJ(#魄/l<>`|kK,@^O׊Ȝ+nǴZ 2ʀkX/LjM5<+H6l;fw2Ӥc"Pg_u3Ĝ y/ *Ao8XfUwp_cPt^C⨽0Lx矢6$1GpAY0NQ54<~@>f\<0drt8aut{V׏VXj8 +3]9.Rb +t٢sL; ; )1TžȺma2`9-;ZYːݲ>ٹx}dR٠MK{E6߼cGb8HG0{R G׮|Ř i+ߞW~Eqmq)@6q + "lȈEJ\R& +ʒDPQQDbb.4D{aJWso{Xhq94o۲[ǓSC*LMx;xXqO/=谀IVKC5+`6%o4@?Xbm):%I䢗L} +PzLG6ZSmbp x&x@ `'9}dboI䔵]d$a ikE]Pʯҷz `ixZa ţOu.J8eF{ KHidv7WTNb{^qj塴q hu!>c?ewKuqd +#j!.ɶ" +}h*]ֆO:(>$H-t=7RשIQr촞O'8ӛ `.&@F0EF7MD)<|;˗rn+O%WGOKY@!NTtXPs9}̖7u<Ag {JRsz|z@xhv +?`<~0X^)y(9Qvlim0cS=P ׯ>q4*9VdJC}Df!~@_Ø/u=~~);2~a leE=\_"yʛlgW4ԔhWx2Ceb]U [_ϗv<^~P]k-mӮ|sk+/{t>:C!qCmTKAi[ݒxk + iq7@ ip;N0ckᵍ5˺1yB&9bbU-K/z:1zu$x'/:Whii8dz8- +@F y˪ [;\tnUf.nh ?fԕn! 4fAc3jWZv1ph΄UыW..߾]q0 o|-{C>\˓c{w\Vvm()>QY{m>T+񦾲싳)x{:VeA֬ѫe*Ws+vuRlKqcU 8ؓXb\#TwtBzK<&u](<#=lU%#>B?/+d~PPXG,@yCj)ً6m\Z3\n݁%!\b +ŗ&? zc;BtWIc݂|~N縫كFu{_M5ydNb,'RXvα/ +9ż f# +KO͕Gao*12M3{9/:`@Fwc1ѯz|7]ˬX}mF/^0c,hأ3c^oT@4 ޅ=Ps%Y.^@[LOu>-[x HI3WLZNy_9'>#}ϜST\Us'k4'%= nW}W݃`6Ôrjr|Xt+8%񩣇u17]8t^T=lXE|&+:ˆgQe~c5g]<u5jm SkWъCM_:WQ\pX#vDpe*A8`@E)ھaPE8pY#-uES /Ө,Fv peDB7t l-4݀,qcKFr\b@шGIq::2'c"t^}MDD꽪zիs5E1 l[[a W]own_Ty{T˛lv{ +a[{x9ޅW{<˯0| h f=E57&#j(t-$3\"׷z ]8q*Wv5cK+^WJ6{d1P?mı{&A^U/.1[-9z_4:x1r0;-VR6] `X(,za0G| B\Ɣu2UJ /s=i`S}?nmߣ 2Ȃ1F:*A')KQlɤwG%w&i -L1#~c]>NP 뗤e(Jte(uw;y$^/˫ʦ!J>Iusp'aCOճCFuj b؃Xti8s\:k9t4rw} .Y@XL/p,׳wo(UE#%oޫa3x +^`e dY6ˉ ~c#Nd툋cl0J@l:4 L=x6Cu՛e9}Ҭztf_Siz_c8cIr)C3X{3{k'ꆲkYct,)3񶎶`db {llaMNjqU~i?i0KCitMH rwk:0ؼHB}]{b&/AʭЧ* WBZcy}ؾs%3fjY@&1…a`z`P3a@xuN0H { .k3(dů#o,sp&U`ŵA]qewS uIU S_0 ܿ)G@'WA2ò cC?G[b96+̵;|1Vw߯~[x,q xs-^rK㗋'}?כ/a|Z_t1^Ѳaŝʊ$Yc~b{K s| +:#(3"[?Xs[핍_ +QkNm5V`ŕ7x)A*$!2C =|ABRēwF{c`96㲄L\eY3OGo!vEN ?NL1~@g/b0pJ}/7Gj41Ea?'~?tϱ@x ơ>ț7uW{n&@=:c ` o/t;c#b6zGH)E 4ٌ{y,<7ۡ"˶QܝA +Y>d&yF3f컕hz,>_tI7o'ʯ\qZd3S^ň'7(΀82݃+u+9Wtr](<͙|u_*Tq=ؾT /Kt@E4g7~џ?O1Gm|8-L)vvM> FP󑍉yPu: +U3!.d,Nd(6E'#p(_i}K:K;&| fڜKZ]k=`qg0zGJ"L'B6F+d ̴ؙdvd.+C<+E%(-D\Uۃ=ȧ}& FȒDϤQ +ҙwX\@?H-SBbG,̎Rh4Ttϯ;Pә%ס zwQ٩wa[`')⸊ИAcnLbVxa+ Y* +:d{Ir;I 1 700I@;M@Nmmc#;-8O#h@{-q'u:]$TmP)ǐe.L&ҹ,x }x1d{^܆SSeG{yli_!괫g3T6 lcbkSB 9o9y΁paf})4S )@0kC,ݤe#ȝȶnbYWB:O5`lj|0{ HHH֠_u6_)K x5 ǒ#Qm^q`,|-&p)P@;=F}3MnwFcT`j#M$gbP1\8왋cAX )@}Zʳ :q{,` S`%m~O9bw7ߠ_?v\3_IlZ؁{\Wu9XG oݸxw[P#0΂O)X׾>@ix;KyXZcqhi"ZJwbc-]_fF䥘P;Vˢ6hCPb2BfLX[tEݎ|rd.;3A0]3|}@򩛎_"Ap =`m JvJ׺jVs4݌Ppgjn(<ij9Ì2-lt`Fsob<Id DZJO{=\7nɁ3My"ro}~ұA0zc%AiPp'"dk4AvЍ/_QM^iaU6u7Rš%NB!\X,j=#U\mbBJEqJ+QUSQg&!%AO\s]JJ-ER({s x"}[D~xz_QV-CHF>S,.ޣ7g'hӶ={i]p`Ve`+d"Xeo,2"acFVp;Ċݞ}ς&= h<M0qg=5Y) +4rU@(NQJ/KM>|DjG[T?,'Bc(F oAmNG^$Wj 6]<<%P|Β>flM%- ӢWC.PHSVx%J@eEEө,1(nf~? /G#bHFn`$#̅pN;'<>;M 6u/$h`nZ3 x6lHY$(7L +< pV+?qr[`TU7| d;!{l̨ =J?FF&vzw;E-Nh/}L[&jiJJ*/ D~S&az-ߦi}wZ, q ef^ q $~O3N`ZMrEDsV%@;a 2@FkCJN6y wq=HQH?b={ۋa/ǀkT^s7x`q9`󯜺*WuXSъ3 Dz?7UcnN^6Al7czRD1[וewƀ̹Y4ccd_nYl ~(dl3)x.o*U+cv|kdVyQm[ 2A(z+K"$re6ߞbAʿ;: > Tpl!ЗƩ~Z2 dhJ4.RBrH:##Xj4\F+D< ktABz.ppsuPC%'P$0}Ʈ]eEìd ] 2O!‰Ҧw*T^ө 5=Ľ5>xCJbm@rlEu`N(v;KF*NFZbexo[p{CDXē{O{Lk'nmJgǒ=eފ}$D:ư{QhC_捘H8`20>`P`~ DŽ]QgkW0 +A;x6&N7Pp]ǯr0a%oS'-m(R Xp4`H~M{~kFGOwgFc01ېgv_VBr#>> Bb{^˗[<;7_ +{KE{ +1Bu9 +Bx_@xTofû0! \g>=6^0 +#~o׷"o_n>uGx}wg9RI ʴ9fe2x/ͬcrC޼zthǶ8%I8/,=^-ups?~*:_ N=z* yI\7Wp@¹sO,0Mռ(>T]ZE7'wOd8N]Egh*5A`$ՔxQGS,.~opM83ΖDt>Fo8YQ춳#=$+bZfD!/J'=&]b tLCsQxOgE(69>T<){y_*x\ .lC:19 KI,JnTkoZuÅӚ|}lw҃U'o\(ɿ~ ʿ^EJ4Ծ_O]GlWԉ/7 ΞϹZ$-lnpu^7a +,ۉo`{QwLx|L0"Zc]ξ4h_U(p}8OHmuխɇc ]֦)>s5L&~m(i٪62Kɵ"U1Iy!t/51ٰ}"&Ml$ qZ&+cs3 2' Yܝ m~k'lv1 {K[u cv_SQXpi{G!K`'LuEn'8@ph:QTw><*s&ɻ Vc^3$aۯQ }c3N\vyMdr \z]zǻۭyU\ISVy$؜>!az +ޘ:Gq-kTCѭ7}pX8UKȄȆUW +}#u+&Pq@f 0,\.Bk ⅲqi+i'v/X,+oTub0lO;i Q$N"Myrqm랡><:U֦ro&L##nOĶsH:yGM^;}-'HZ71?c/31?mnP)v<BOhg [4)z +<Ps Jó.OfsKe .w;n7\co߰Na jcP"15~<"bLJʤU##_G+^x0qBl-o/#5.enE¸a~CLUЙZ`[E>pEY3,T$ޞeT=ȚdU#OO7M!  X"ikÜ`4XZQrwl4IW[:c|P f8 Z?w @aT8N3ߴY~I9[0*3K #g'NcÝ0K<= JyGqx?-S +hKvգGwM8‡~d0Kk"&A$nKI $@N$Ŧ: +2Vaw/`ݗ8#3sݿhrSO)tj"zeBj%K9qD^/a)V~?-,BN8oϓ0[`?̙nOM6׾CU,VPyȦS>8ܱ<Ŝ A: Iѵ.\ٲKֻIɽ*X׃a $7~=i3xy2Wi#=ѿBԴ3"_EREK Va`ɺ[TҎfY_Fe% ,.ᄐYfqE>(0 X -;)\ѽ.<=\;b`_=ǝ!D8h*+ WMbMC/~'.'sPWcqoLs$JEl* 74_na*9;G gG&&f=^.l25vSG̓G>k ?) CŰGc1X8[/?^WGp??.~# N1Rr1=ADžj-Hn/Pؿ߅ns'R%+ C76^Wz7D*tg=|,~k/mp tS)N^%Äoi. k#?e04*Od-S9nV2gF5և[Q0E優O*'t&3L7se+yg'#&eANyA.ztljin{j:7"S\Meho8Tm(~ഏx@QyJNuR!\ v=.G߯ip%^ +?,"ɵQWv\;\k؎1iFBeΡֺ=&J pA av ~ә deDu߱ +O5|~7ܥSg᏷5#wFdD⹬} LS2Xi;ʶ\9zr98( Up+V_S؞%k.=?`+&'ўUQ[9 Ǒtw?`ޱM!̣8I""UA eS -t@0ZDTǙOk: XAˌvܕe9Zk1a ks E\=Nw}?9wg_/{PSqoOhY?R s`{s"ȉ?46zz򇾟uryA\s!^lS3SB>P yv~S[ε G |8=;IoAbGk#1@mf+]HUnvUXA|?bbӖwn)2HF999\*tym07S9S؄*~09I>+ Z
H%kqm%[,z΁H"i^Gu{N?d}9ЭQȚa{%oEuS}84xWa+I֦V/Q(B?_P;͓2 YN G'ۖ/x!6:F Bkֺkx_-Lc7WV5 5 ?!k#5&Cر>9̑--V\hC[fɋCOb (ݽx +l͍1Ph!8XI|az0{_9+QI79u?+ЋgvGa)Soq=38- Ț5/Q/`ŧ6NN)y_9Wo~?ٮp\yQo5z\T6|= +];bze$/D9t(k4}zLw;X+ BQLBsvKڈ{z|?X]SzX dIz]IڏC^yep,D`` ÀSs2eoJ=VqX.2`H\[wq?6GCnxc!Oy611E=H';.",zUAk&U54 :G޻e[ K3+m;\~2udwhg9=gTEhvҚz=EA4A͙x?-GW[s{>~=URsnל?d}dzl{ ݴtxM`D?!gvJ ƫИR +#u1kXa ߹;4wR20,?E*5L:1{^,9 &5,xG~<䪍NEկR劲Azt<4Of$b)=bG) )URU~LQG ئzIPP/9 +bQۗgu-)weFɛyU+e2fqXG#mIk^Q`$N쁽~ͻ#}6D,賕eBFUhodS/ q"?`5b?\%^:R~;>wq@hccǙOH29ydoDi"cF9;c[ i9̞d}4ͱc&knšV>xBWMZK~u|#"{%'E~jIiRd$g0}b&Z;ՂۙV;5vKj<׎J&㼷yxSc¦j- P&6uR+ }^NyGkOF7)uަwajнug3J{ؗGg?dg^d_&z+%?\9EϏ +"ވ7xfT}|{"u{9τ?eMc,8&w@OC"GMԭ w@M7 =D_9{.(_0;3GE@!=9 t/45 Gpsؕ_^M䵈G!GhMO׳dVb<],f#x4 Hl%p1[~VMuX ;$DO X,to`(k`T8$7+Fi٭Rr\{2k~eeZ b$7qȾk}x 8(sɽ#I9gUch ?-Wb) +=?G !]GE0؜0&6 ׀s5BOe7Wj' -$~.VY v0o|ۏ ܙSިo<\62Pgɘ^Os Z16ZjvpMӛ~4W3|˞ht쨮aE?0M@MUfélg/3g0Eƙڰ`W gKo]j u E_4>_n[Km^޺8@f(C' ҶҘ-s}SZV%7J 7cb "8X'y\#9-SToEO )M—oݷ#A+CU$'az0_ky 65<4XuwءܱdAl,OvfE6= ӥشps%^S延/F菉$3gUYŋ3SR>6ʹG;gXK<+%s&ygc a:Nֿ7)9j"FܙaFԝO (x9 -ND +A,rk2S3H&B0% 1xqJz4P$eޯH.Ab7Tna=D< z+M (GQ-(.Jَ(.4Qh&A#Qg8NbYqKjNu\%WR{…M$QcFf_6~r?_,!~g٦ ɭ1!􌧣wb#WW<<揉q$sj/tˈ=pȌA^%IZ[#4fzU:ts+ 叩"-䮂;g[tƆT>|W+;l6Kٿ<.cS2ût~M;:pjtOvLh-w6q<{hR\qߛױG?q$xBkʡ,sl]t4yoL_L 4vJ9k{ P9@aˀ8Q@e"<ŒFA/dU%Sݡc2<'J7rL?&EKI݋Y/OXynZoA橕6caB!nm->Aq16*64{0)9E{!.'dGHc?YmsU}KUȌw-3Q1?mPF:Tڷ׉m//qɆ6 y[ +{26Y~)gL f97-YܻY͝]Udm"04O>;0ua29.=XD쵍Ue.uOXfo=ܟ >k#U|")9(?S?,+%Z@v䤩WJaЏ ܬُ TUu_Yt\Aw%O}kat_`)P~ QLC>wtoA[9 ѳ7{nDG`'Sx=+6//섾.bWWD߁H6oZxz8+P2zڬ&z!g`&H ϖtү:m-xخ\y\l!#!s0<1;^덗[|gx6Bo;~/<9U+=7\D$p7+25`gq1ұO t~%{PKc0`~200Oe$9 +(,P<"$#ՆT.EM :fEӎ['g3gcu$q gI@=FtrX? }nh4VtI{O2|luduEx]dFϏ_B\އh%QnEs#Ǚc?;HjXvnY΁FVaZKf%5%mu`j؈댕J)\v + g T0A[N)oiSmͿrzLuj.\*Ar,iwyh|oCL~̡o|?ɟ>,4A%G YE칫 qҡ= `  Q<^~NocȻWFR|c!-·M]mm+X&/ s߄٦2!K /#o2:5N4Sh5FKK!6FcHxF$>L tX|-$Fs\ݜU;zҚ̊7T_9z zF>n`}{?Rq_ xܝћqo2S{NMEn08p Y;O 4=BK4ݓ#Vqhd0cx88=q$g/&GP7^NcjEEK;v]+)M X`Mc.2vCVw79MN7듛$ܲߒY}㣺]S!{tb'c h L x-`Ǥa>o౧)9|>  kzF] I/rFOj<(ћQtb"u6+ U퉞=rbe ++ Atx7( 2}"H"71@N %=MdJkJ{I⮽=r 큱ީ7vͯcM#rhx%0 ؐ%r^D=%VWbGI c=޲^}qZk%p.7ȤfBWAk|w\g$ʭ(C4\;Tè2o?r~ɦV!ߝ9}f<[PY|_a> +{wKp6; /*kI%v4FQO]U5u\Ź#g\BМ{ ̹*EQ]e'm-qߗEfĝ׌i r,7d/nX,[{W9=]D 7'G~Mĩk]@E2c [Q+iG) L|wg(\ @oRRx\t'k}+#[l=!'&+*r-?u #b}Lҁh _6d7Jkg\vD4(0Ws? a/Cnf)`^CDgA댼+L0K9{,y04f k{;1c:.LnEVxG(e. 0\)1}cK_ k + yAM]v/VdufsCuFzHRChJ:59W}1puLJ,}ƍlY7TÒ?PdG38<)#yTpoxѯ:⣢gȂ`90Yg@_'@mk ƨm@;Κb~c{ a(cC(dtxv0H +d섶md.,<1&fVU V~HzȦgH;y0'@knP&Uw8@6y㚱%7Gdk]gDjSA#CIXk=Lg(D#v\E,qhao$7tg +;Hr3qpsirƳR'2:>nCЍ B{Ϧq@;+]KdQySܾWd;?cfbF&5ۗx/J>:/ho2|r +:r}VV?d`qcP5 zMW-GvVx MSl@69u\ݮXrU]`5G䤁4^W?8BMw>o}sR~u-iNr֬BXcBCJ[VkKÔXm5l`OX%došS߸A뎠dM1:X +k57ETP2`P'xZ?b]=Nh CI v WQ;\Av$H]s= E#$r畊Z ;TK\9S[ܢtX!\.6c5;0K3M+_R('=;+yӾMHt|mѧU0 :`?Zv^{6DU}ƶ"_{# +y"B qQIV!qf><k26_i Bgϙ6lz&j/)}8u;F4Sq`nrr9oKAy);ka;x߇b\AVNB6/cUȜVba1v3dP~џw;rҎ0>1X#Y㌮=g%CCo3M Nr4HU=fdLB^dc;A'2#/<'""UnDY ouMuš`p&XTh22G˓6g9i//^{&_ Yio?8㸹{RXYؿ/]~%`)?aPr2w*dPuSړי,c ;`~AOxc~\#i[_ly9T!ǒLt31.vAgN估 v5`l=a"̲PSܣ=qt"|n%_MxLѩ #Dt +j*nRcC&@USxc/ot34K70҆6cF3Āa|suw(@~af8J{2f3"ײlHEw`}[lڽ=m}gG瘁?% +]1b1t8M7ǹlY()szqR*I8c0n'^5Rr-Ct TeY,sg '=6"m:.YW PY>Biή_ћk!v=[ IKZQqnPyup|oX%s<+;t)N3´]b~X6Jwpf=ɓ2RS-(r}Zc~ڳ\S'T.9ۙ {nms+"Es!&O!R8NN'Z&rejeXꄿye-W +nD^?U,̽zG ׳G:zDsyw|_F /}֊6E|v&ٗF *>!~&?&\Sq~%G#r?G7Dt+D( Kb(8Q[]MMɠ*Z{/VxXC2{"!'f8,kluvxnJ`|L;8y=eT{b_Bǧu;7fq3};%i6~jE`{,"kZ5uM;#N9ITTH[D!V7Myc 2;!AWOEBη^ ~'c}џ}n'2 +h,sZz 9T>W}U7x-ĊPy=`ؾ56&Ar4 ,GX5Vt:A$A)˾荡1,{ BeF.ژ2,en 9tX 3u>֟Lki/(,u8q}mM@YYDE1q$ju\v:A@ɘsx2.92KUwrjq;TU?^W{Ⱦ( ?X4 WsHKM.tvIt:ǽL &9@1p`[;EmW]Vuu6$3+#d'}՝ 9zxfTffk@O!Le2\oU$-^~C8cBl0ܧ@Q>9$D۲GO<Ŀ]I~qrħ0p{63_V&P=Ȫ&MWЙ3]K1r^,縷 МQ7dž;fPv@æ*2<{xAe{L7pcr6?n Ҫ]`=?uޙ.70GpƆJ .c%3}drxXE-1NH2O@CT%'{3]g +\AaF <5Njgi{.{F9>l5N@`f+DlsOnvH/:#a`3Vq_0 +*<#~i;z%_DԸtI3FG +c%xLHtKK9_yA,==!n? SR<^WCH)=H2Y"|[)ɯ.l޳]!]3yӦPy1)a|sy /y,7ay({-jCDQalSPa@wr %ǹ~cz:ӂqD& 1~l,2qtAE P+8 0({+NV}k^{qvqK^Obxe{kx50CfmLS;!{dpim+|'P oGbaCyXi:(`X07VZM9{V&#wp0މl:uH+< {]s,C$Y-v1A 4f~yGj: Bav XWceOHQF~Ժ(}m>>o<:)!`nFuPc,eE8sN ׬Ra^[loY;+? No]X?%^:E?βϸ[|s +ϛ€:[Я0DZGۂ =+Fùڏ{y]$jц¼6f/A‹ Dָ '}cC?͞c}`_bnf@X6#ml/-6/'Z13ò{_C}HE|4ey|2S4/w-~3 {C0~ܷ}K{s#t\C?cڵf}yk J *WJDoҭ;E5\'^,{ Dw_*M%%)oWsAZz)ޢj'{!;!3|iGf7);vV\G0F`?卷p̥?zZOgbwo,M?r(L-@ &}UN`P3j aOLd2=wWhqN`O +oT8sQ$qIhFum '{`O*J%RĆ䁩՞ſ$T) T)'F"Tr@_<;a E CB!VAB++8VABF)(U)!tcap_;:Z^@ rh s{~y/߽Oj˙c_7lKY&y“vӽ+=?Fu^۵JtmuAp* XVRظUpnJcK1&eak`PMMύt0[?TR0{2QuyNJɊMa;X*.y6&P:鷩gڀ#[s`6 fj n$l>`Fa?w绵{ p#87HiI0:zN,F fZK )ʿw`2{:46zߥX6[O5J &q<߄@CM'Sq[  T45L<FyQtMsC C8Zs)?EK\lx<[@o=1Ѣ,OӪ}=RVS%j>) +%#>3QyCۉ2mEϬ?TqL0@^\l@ #'<"=<ن ߏc_Y,ڊ~NN6B8QQZ8ҟׄ?h:Us{ Udf,KPmG0hut YMсD8_OPV#b,jV~N?_יW)A}Uېl*c@`Y܅u1$څeJ}8`b,j$rWYvK^͐vxRM Y :pw/xDH1_`|tΥE0 ]ss PY~`/ -=,@Ug1gi[A~}7Pm!dFO(Nbc?8U~lG~',Ǟ#.&r  tȕ8:{)P >Kl^Zl(lyLV:f=GGd&d uo骧4H_ xh%n;}{>kpW-e<}WvH)tm>Obg˥r7 dԼd|/K*D3^ SdC?|?=׸~2Eƌy!ڿ,{w9F?Y^wsn?8_ Xer uPcnۓwRCj`t\O;zp=PZP $:Z DzLiKqI 2-AxGs(͉+.{Ֆyc?Te}j bj+*l4| Eo{LOe1S BiTq|KƑ[t6X#T<`D@?w2#6?eX#+a#sFuve +Zσ\oqE +O\-jc !SLWc;фu<|s%X1HS8Hv |Xh~ LAi>hT.+]!i^4f9_-vB,z`b&<}ͭ.L`ɼ ǨdcfS\rT • >^X "ߋ7?T㓄=ԧKK3>89&V].rW3[*ʊ4㽑s9yy#˛ovm4}ӪNua:ðp5de%9k8}Sw^!{_7N˽ϸn1MCsý{ חZωGeXb|f}{7  >w9q݂J9 ǻc9y& +9W榲2Wy.5OV#VZgqx" += ->eKn>za.իni+uLJ}n'P +ߚYk oDlJE249?Zzf0Fhbph +%0%f=V}Al[dh5 )y{,"EtS=x$Nr!+5XE0/20{KA~z-)-g+{9 [N2'1SO'㚏K4QO초 B}l%{\f?ig3_ ["\}U;?^ȫK'K8dhD8/9X?eԕE\Z!a A-wEW\jUZTb-*k"jʴ֥St$oA`(Nsy}~OX,_<xє<de!qT5^dks (ܻy2Ky0m'8H[E q"(=Pٰ7x/?,&A{OmD_M^)e"Ō47ČYiV ^E&@;?'rCs#j,2z%I=+d?*W)r_D˔ TlYd/SL c/f˗IV)ӹ[߮M7Zs3dbH\ zM/3;d@;nG?#QmVC{eܓGՔXsPp)[gPjp{6h)1+㜠$6Z}),Aہ.36BHF6 O=> ~Ji `C].¹<' L51gE0X%5nfjTz|\~Lz%=`UN*܌i~!wt=o+/W_MAďY``#c=[T)??V0g)z ]*o1]8\N؞=:(B`tQ0Zw%og`eqqc|cO2,`3|o_vɔK}#7N؂ʂp2ePQ!x'_K7?(Ґdi%{m=}JY 7`$Gdogj-5=eƫbf~7dl@`XOG%#  B/WN͐OИ&G&^v;WKLoDTcM<'X,̶#vʅQN\%ď2- v7{˫&yzK,/uR}sf)4ZypCGv:0uq1slo9k=>'CK_9=~=h߁?P}COO_xCo|KCz44Fɝ ((EDQfBcqRKE$/-Fb_梠t>~2zPOa@ "LY }oKlJ|TG9;g{\[ԄkSag rվ6w:c1q2a]tS h`kmn8Gl{‘1:X0Ad A|署MуtY'^htKT(ܱ5{wM9;#<9^ =wZ٤{antׇ/V<<gu'ჷ}58ƃ6GG+fyIiճf!E|險/*֯_ǯZ?z39iZ+w{bnyeѸh( On}MQLrKjh%N7vPʍ|]:K<&Y;)@{k5Vَ`p,$l0E6i{(͗(kc tyk%80yT&ԨBsУ6cMEԇ+8 E I^%ryiV:sX ;6H| /f<Ȧ;P]vLZ\{0VP0t蓅R0LĿe,'ucWG(ymqfqڹc~ y"Y-.t`X'6=H77kPs֞džXPe3H^k(ʽUX/L=%P2j n2Z7DAHB "[t:UGzF0ڎ] +hSk;Zţ^Ps/ ,j9՞{;w8<*:_ǿ +f/7)Czu +&O8^HbЉo`;qL,\[?0Gy^b2ɤ;rdfv?g Za0f/˴pHAodC S,4L?%MMdr&T+lMk U5xӑD.o"O>}J{ 1V}Zկil13N*msP1&Lu/1oZĹ3kmgRzLGdHNi`8/»0|PxE^6s|bj2Pfj (IbuY,m5f2wcG!9yhҲMYIϪY3qR8pHSX# č*h 8Žuu>ԐW 2HadfC~I8u?P+.4֍7mDb`N𳃗S,9zts9ٵ9Q\ʹf?>7cfGA +g口{wgc{GZg;^k +/IO;jK龽,֋ftA!c|m߱yO?ֳ- }_c<YOG=a*Nչya*{ NoP6|z̭QvA1G<۸Jy!c(6|AL)+ ~n`rW'9bE{d၏ہDk U;|mt"aEw졦329/0^!Vֻca8)`)a7}Qsޑ-'[FmHv50^ubED=aw4"aH]Ѹ_ L:]X%c[G}>o5Ƶ5EǀW_dkl 0Ǟ'vDIm&}z^T@.Lq τ3ټ8f05莇*<6/f1?QnL!^GEWGx1Ko- af}K暎qНLGỜ)e#A +fc=gnA ]%e,QqܽgǓT^4#$ȃ`9~P JܠDIM 8gT5پ`77Ndf8Q'qp(إQ#a;7Y`%+H7Y`7e$iӃÃۮQ@/!R{3aeHͱp) 1o 0uGeAPXTn϶oǚ;So*oVJT$_.?\t%ѣv{q^{dul#G:yp+ݴǎt=/X*UoXj-~M(y}Os|JEoNZ|lᏄrNG ;..OPtjjs(DYAPS)ߐQr|ڝB]OekQ ~x3/.;;έh Im1]V A(Dz :.>XV9 Dec&O]c hozWO2\?$ƻʵ=lЙBd]0O?C2F;_x"M$ {/}KK^OFb҆:y z>~r69ر^ꩀ:igU;0iGr7o ~un_kg6`̹{թ`;ٟ)=%p ;3[=GLp摆F`\ٿn]_S 0e~+[u;]=-OLjsgl |cctѧ>Xu0?>9w qnթ9%F tl;[7z/mw$P3LHgJ2 +d?$ +3}/"| +I_'nr>CVw-VCXR_yoFXFhzK^vq)©`N"E dLTsqU^$ף0W_QMY ZnVeBZPѐ#D`bgQתjTpt\R:gQ;kO( d!pw##:VQ|[s0â3?U{Q+Fev!d&pH4P/%LM<'MY{i(7gw?~2zQUQcnX9pIg5Χ. 1^ӟK>a{]B5hBC +[*R}Aa\>\7aciլC?j={XPGsY+3DD1Wm?+<'bRiQP䎎CWY2-l5z-궽5&ܨ Nr܂++OdW J_=VnFFICT|m(N!'8OFtH (p-6x3<>a:l.s&uZ9 lWq_d%/6c=is漁e%7O|i:kHϲi(<<;ܳ/Y{2Fs5hwz}\6cb'voݯ% `坌ă*J:fyY^ѺB{t&_|1Bn"ǚF<-mx| zJn 1n49^\XBDWY,[>tO5E}AI*sC +w2&BefK&RLY2LeJ(f{̤E;S9RVQ"T<^"P֕"MJ1}5cl?VALΫ8L$Z9\ec= 2htı枓Ӛr\ˊ\8kߵ + Y'p-C>Jca^T*+f3%7)JWW %]Wz}I&$WϬw n9s<} Ijj>?BSMQrhy}e8_u$5}s-SG GK|?I7L}aHF8 x?ܝgR$C{xΈ +@ W.2zw\m )/:aOCdg͛߿ 魡mAƅo\7V.Ӗ?B61'/qtQ!A])m.NЧ ,12)fÛvpcWˇ ~u->eY=,f%yGBlK}/Y%3[}? OR?7b{^#;?D'27O[x!o tۖqޞ yWΞu[忨]sʸlMȕRy7KONj[(cyV~ߗ?O>w9b04:Eh~.%z!ue;gr]Eu"牂[Y>PS&jW$2˂&,0rKM[0D1SoGnP|X۩cF]]'c S}`+2He]:=/ ҇'Bz3H|Ie4R>Y?M>X%ĿhjzP_4(+րLԀFP@@fi84ҍGQc2JTa dP,.A@QXf*L&Xe˔Qxx-tc5`/:U~W{?:u*}Go&t:!7`_l_%ҟTEJ%*^$fb)FƁ!Fq540^9 R]?qN|8֥@|7Ai\ЇX#`e~_lNw/? +Zoz:r]30Jޜ͋Qr7S3:1J:3)ٛЫaqf?hC׉7E9Hme# a$S DZգ"S6Zёz`÷'EfyT"o\mX.Ⱥ8W1/^+ gJ7l>QArm a$%S_ldz>FU%҅b7hAUݸt[~x =3PR߳b*^ĀP_2e1DBT72)>C֑Qy\ +MȤ}WuTQCxf"X Uvvo,Qd/>ts)?}PlIW` U7̶/1lҔ{:!T{*lTY"3 i wyš^"abu]qW|V`6f3 V=(^"2iM^j \U=Lqm?7RsuAzW[gC{N4\ȭQ~WJN]"*o)`=|#rVik'XO6~`Tc49;--EX+},DsHF'TSS7;Q=rVH(j2?]=q4< FAb_h=01[qc?N %{;H=x;'ˏnJx: KJ8+Oe? Ö5zy.zmaEy)Xy[w`ѵ2y%-Gx:< ݫLEq6.^liɒ!6CsGqpЛ'ڠ?=خߵ[x5zuuS!{v DBHű՞*O|烒9x7͛|OcsK> ,&PhrSg:x\l[` 8pq HmRz_Bq&+xwe-8>[WVRi齺ⲻ&]>Y~[",ni1\)DvrU:[EJmU$C~Uw|WlssX"i*oLzY*.JhBLyhXLNe$ӛXZ>:iXS PjUQA!H @ULdhJuQ( +`-(N8s)ETZ:bm9I$º{hէ?gae}{.>&^HR/<"Ӕx9> ~o]a.OUtRzC&D-k#±B_N>Tˏ%T\gIPGu@g QOr5hO0D8ngg F֔d3a#v= +fwH*{t(RqvMO"ܻ cЗ=T:&\Z$> U$SU!:IG?&ꁟjXũ&*?&N9N0=8N5>1f"q΀B"{fJF}`#X3 WT%7GcS5VǯVߦF镗s/p%2KT\0"JOchc`Ad>m.珖#%5Lgƈ AYnѯIl<+08yᏦ^8~Zi~[!گt<২%?< Kؑ8$=aٺ(lf=-,qȝ'̀Y(Uorbuz:V[Ly/d N1:DzѦ:`;n@ M5)-hٛZg;h2*c +2ŔȨvcGb; {${j}1O0mZ9-bO4plϋC?­ےf6_)c".~!Ţx>֙{Gǟ珋䬭^x&~WGVF`^=nb9e?~gҢ 6-2Ŭx3xAHUy&iH]EZ@|T%}@(z93 979^ׁsǣ.O;Xuv\Y#N'B Q4(Bw6.; +^-ٿ7s=/fՇ[T@:Ӯ>0,UT.9RB2j6W]'d_)&{y +1""Ma|yM$TV8ʡe[>2W:o\#M01Tc7l;fEx&X[2ep$Db}‘Z' B\$fmi9>tPr^Z0_gmV 146Z߀~:sq\W^**+ +ֺE`v#Êl̄kxƩ|}Ay u`eFFы\ph!{Fd|M\DL|}=$pHv^=IV +VUJ +Ӳ$6}%<٘r+C0_b? + zX}&cΊl$ DXК Q6A,%9[dŴ-}+x/Ρqpb#g:}~׸q (oȃE[ZLwюdeƤZf.]7sbAsNXEmyOY)}Qnyaǂcbػ~g72?6"9W&USJSENkb {Tc-9}s//\`}y\ߒyW)NT#Be} `)d:ɹMt +xhOTs<< r:]Bձ_>Dţ)KN\[tae Y3*3z%P8Ca +0HPknzzQDcAWQ qcc9%FN ZU<s{rO5B=үҹ s]b.[IYŜIP,hX~RZ Z$iA2jHn[h01: u +\J}t0XuMK2yCCRJWYX/Rtˋ@lD)[EE{H8f!xI5ٌ?Ƀo8^(^8tmڡh.Tq| +Ub~8Ai{3 vtzcZ Ln-0s)eˏ\p2mzmbx]篋Ԓukf֘3E_I^֗ xM@s0$K`_\-ʽ XK|sgPx>7oJuJVf+F#at^iXu &?в;.fWj۪<-;W5Y?g}XR=gc%4QJ58zwy~EsƌscڏZI~Da `zu|O~䀁N@D\~mZc=%Ͻ8ǴAj߇T @C[GF7>`Y2^Nz*9570?ş`C+\9vc\_oGISYAp^;2A+ jJL!(Ls ACLߪp[?l*~ޝOLJ okZB$Ow (w >PTNo͍}+sbXj T,9G|KW`0lPt6_:#Qًj7F|Ï~AJl!p"x0#FT0>vĚ t qzd%Yg`(XI~ԍ}Ǩ?@09O]?vbޯ^GDrZr)S,;ctnwǕq7Q>អXHE]U#|3H~}Y=!&m!5GtdZ:c9vu v)Z6?ĵ&)Esår +=@go8~˻lFWb.ڈѱWzM0k}N$x|w'l_A@=5+[ƏTdH*pNSSl!(WlKa_N=hmMAkm/S4&{Sc?"Y];5%9Z7$ Dy(8FJжDP,A=q +mllf"XE2 s f#+^/$F#QL=5PmїK +)t0 Yi:Ό?`?{nYn=;J'g4xf,MѽTULݡلR,`vgK(V0KOHՁ9h\|1ΏmD_{ A"m1uWTx+* `BAr˜( + +==+3}p0Ʃ{{/C;Itk9R]GB\&܏Tp(&%SVYi<$55fφQܠ‡~ :k:Lιបg+7`azeY$f2<>XA)+j!f%ז2>p.vr'[_\-m ( R}H +=P=}rJ#=y=^AsUBh1wAR˅xWkJô| No 6ẍ́Tf]Ő=ohhsK{Z) +,lh؁Q Eҹޣ̬͑9̽V?K@\FtbiJYB1ەM| ~|`6:bcI]y@-Ksg1?iE?ǎ3>}08Ypy8jP 'V Ur^ẹ8@n#E0%!  A ɋ$stNuţ"U@@EwE*VqܺZA8v{y07c-9sww3LI}1R9HM:㵒NT#v7<4nFKm#\"t^ձ1GHzÇΔܱl9aKnj`fA{NpeOrChywb7a7<p70HME> O?MY.Jj#iw DjW{7Q)q <M}ogd$35ߕQ>w:oSsy a4^ ǃ%Fb*lta3xɃ)׿ rcM^nQ^R=crbTUH:ISһ>t6☐(ޝ&-@d݁Ul' : GQkiR_\GiT7`հZSա/-c# L ޼= /D$ۛ3OzKzOIoڞ)ˊj : Pr}/hN$40,qJ!$(&;")`d\C$-*i>{/^'yS!oƳ`hׯ1Hl.揚`f!⏘ygaXi8~lg3_,gE\4N!5$q:N,H5WLdZs*wf\1YX Z KP}zfG 9%~ G23Amd+5޼x0Ɯ&B`WFx/ymj['13 #=#,~'}Y-Zk.;?Nj{q/ٓ9!{a}PͿP0`/Yg7G |)w{^V>q+U[_χ_dGE睡0hx]~^PqawzU`=h2S>))8%v6]*Jz||ӲG۲=ڕ:-hڽ1Rq CAe%;wve͇J4_q=s'r=ܹyÝ[J=r8?oV| ,.:1y賻.VI[6: 81"$!eBR5=ay֯^\}w:Ȯ _I^R}{+JϠeN\񓫏ș!LM sKR洐T2M$UW+R1SZ~z]`7>!F5'MopmςYbNP&EK#$̙ Gd*$^J'!&CM|P~[;d6*;;ah?@T ^;RZd=$uRr?)6z_`r%k">n. (>`]kn\kj\fR`GO1gM){89WdVn?Y}B{qۏ!c +c)P-[oBpu{SFr4gװ_36SPxGgs r,/؁1V5MOAߔgn*;|쩂 +~ t.5J{:n/؀Q l4=.E2Y{eS: `4BprBl,0{ CXaLvǒөu\ Us6h)(gTK {56$ \ ٽJ.~:/t1`5m )+eaL[ hO6TyК `ƄB[֔ܔ3*4'RW\sS|'?|9EE aivg8{|E{<Мp=$ b\OwC@پ@T/ @͂? +lhq[b?- >`z6ʆIAL\^fј1 K2;YC}'6yLlDs?J(cC}PȔMN:2짾!!iJEܷZ- |L[gYqH{k.{/KMM+J72:>vTU,[L:V|'y]DN3s\y?h8L2/=gt8';R3쑲bŰCݼ@*"uP۝xMtLz)5W0KKn9oiWN_f, /*RZT;2ʃho>LSNjqQj}&ʨXß4jF>r9\郰G}=9)'f~xSS͂U`^CkDs/7I}TQ@E#15ȁȻv]?m Ѝ1\KG 3XcLlhE:VQ&}ﺂOe68)=viZeA}g`6E=O|"X5Ƀ0 1o1.l7p=ƌ6%֗L@[FCcɈ?xV0",grdk;\bmdz1da KsFSA,Az$Cq%-Y$MwѼ_&Zxsӣs^?D;C\I=0V/R=mDzny?.+kIG{ID|ܜ묏JE'őFH:-}MƋZutt 5ci7Ѕ6}]hMLk&S4H Aa4C=P Cث g CwK#}1$U%d R}cgHVaWh} r Cf;Hأ$Pe֑f/* f$Cs^L7G60h#9u3b͍xнFX0*HEk-}J!TL#A:jx q>sTWq@3%<`f4+z4c +F@s'4Һ ?9^&D$?[FI17aԋxwL%p'~SXGĆ.yhז79',9\ ea@ NȄA֠[CY #i;)\!}9_QM]y[?QV3a@PxMV uv1kI0.ͅ׺ kXf,$,I7HX1kul\!J}x36rv& +i=CokQ лEa +oD-%k|p({}խDc|ozduJlຓѥ- ɾY1S}”@)HEtJC{Lot7TvB&;&mOo\+Rt뉋%i'qq'E/|%r 'LK"CGDLfNЋGQvO `O6:bK} +TnN}ЖPaNEz>?i!frS Q5~"@H2@`|@8bC<0\^j#*<O=Z84m*1FF};>@QXvB-YQJ wInJ?_ܖDrP7Nб2]k"k8(\Zw0';f0w*4ST$5&NC?]44ֈҬ ֕|e/毐|KAJO]rvP2c=Gϱ͍)APYT׭+g{I`#%7vYO-=H\ɝwûW-1UV~y۷qxrI>a[-D):#1A~e~s"Ir؝ e->&.yԷv>.*}BJ ,H{ |@R Oҝ5dI^㔖?*~};ԷJ%M9-jWTwϫWVҞZ{ؗU՞<,Od3xw$[MTΛ^5 >eII_eIV5{ZG0$'*W!MpGcc;j'[b3W0eju<+D/Cs[T^lS/yOޤ4a..V0f&$s¥jɝ) l޹ +,$zZG%oGn6ǥZ4ʬ?p +m-1a0]PBB2 +١?fIB 6c$[^ >e<qT{\2 ƓE\l5B?yGfԦ0x<'Rd/p +TZ仜p1R9UTƶiD5`,:U[M%qnNtk[c4yӗN!wr_F/ 6 {8 + w6yGrc{&Y8?̸q͈Pu@`y'Pq|OP*~sDA^ϐ +O2mrp85JB%#L-Q+6jMp-P^GEds'ʨtI ^ +/A``c{2 _<+" +PA\Up@ K@ P\Z:*,Gq1*ՊjT> +y/ G:rN{ܗ{߻v )e(*Ő t}[T^~̋:tYw,ʻ.D:]o3hpU3qa4jAl,rnHunh?v ĶDBn4|1vtzsi>"DgMjZde338dm+rϩeW kأksUgy Úx0ޠAԥB37iC[E :|?%INl|}TAky,097⯳f}\VmE?Nv5oĖ@e\9@'7ɘm'n/֬B) ۗҪʮOYA`0ډ# +K`tcI|DWN&[yDH=Iԅ)i~.XO 0uݼM15Pe0~`:|$>{iX=0:L)@ +S?8v\ȸAa $ߕa~$E0Ƴr+xd)銼u\-GWFW{nJ mJ1#@/#̑Q Nȉ&2ݳg8=6Xt1`ћ,=_ Up>߉oFηH߲K#}N=IX|/?e5Wuzā[*μ{>N9dLS=kQcu|:W~uϾgrN:Kqjn**Ng,{߄ {&E4 YA.q V@%@ßҼLS,RoKrR %aOzss|s[[7vʦP6(00 +YYȦ7Pv#'>?y6x9,{ʏ׸tD$M'ed~ÍP9Ss{NQT1It +}5A׏ #{o?n6 "etx!O[By p{ùBep2KVoUNj_ɍrB\ +=cux&{MJ`B3 SZUTĝ*{&2F‘z@&eqo0Uq?͓1MWlPsyXj?raNt"Tz!uOc +`vqUt/K@;1x8eD͕C!m[Hp:?yTWƫiYHT NC .l"["I9jFwQ#.1qG 8FQD0q1C\Anw :NuW{Uu}~9ap{% Ró`=^`rօˈ.[޲`lM $fMjv>dZօJ8c~Zsӏ؃Nsۛv]LVkݨ*rzvlB.q9텅C4? v8~7Ջwס^zَ' ez(I "mcbhPhb!W0~;5{^6i|j !Y7ٹ0w'D9v`bk"{W:eE8nh7p~4 J{o=}h#cMq@L;t|c\ZR:}O&v/*>G5)OuϔVhduCh܋Q7yF(6Oj12y,;Ozo{0btpRȴj݊::t쾱>!>Ƅyܿ#"/|u?ذ-9g׭{g @DlZuy{͂y"!mMy@|oXMBz=8_7p?,?B=K`x^A>!(U^̒q&G"t:3{~Ze_p+P? Ie@*ߠrTCD𪢁}9|TyIbp~!U^ajTc#n1YK*6\3G?gG+ ypb %_G6:>OROg8PS]$ꁾv+*<5tcFOs^J-U݆+Ƨ|^?Bc ss6nV)wٍYSϤ +ֺvژq>-Jp1!z <8z7V4G=KI);)g&`~~.]!yn\Kq%Be..iW9<ˠ'P:w?;5vF,i[dAUBfQz"\ʵNmܙ\a\ h¢,Z̵giQo?._k:Y.mP^bMcjuDct^c4- +{1C'%sd9M)sMf{طo_>FDxY(+. +e PmvgtE\ :ё{OmV,Nx<&/M J?91t1P(KQob=4r" 4p hq=ZX2Pijb-*l&~an^yŸ 3d,EUqR\.9.I*Jq$ir_Ö0bNqu^|.'J:K+;G4^QO3p}4`OLMJajl@i>ҁtg57 ; uwqy[%g7oV9Ѽvg(ojZi`U w. n@N5p5i-$f-U )rn߅}n@s((Ly RQ8{;9:ow0.ްvΉ)dq å_ +D gɦx gx5{RlůLgț|UJu9wPO+9TΗ"!#:wىSOڇP*bzy;P|ZE!ԯiw<{A`y?I{CL􌼽(Ak.kK#~=)T,ƚw hc&1N޾ֈysCv| Ah:hmI˸JuT\ Whji&A}]V!,FzOROOPM&|ƫxwz|?S&/ /Rר+#(.֟g5YC3m5j=a~=b/]C PjUWCEdb+)}[A:fAz}A"> xA(Zky#+=ƾ-KW~Kpᶬ/ZsoMSSQ*EJw#Qb2~r"Ǒ߻eݞB~pB9? +;bǶ&[65!S>f;Gwx=>~~߯t)Ck(q_ gXQCQ>b?^z]n)؟5޶fo…K0hٲ 8~oc1Q퓳Q{ ssa|G|x> JЬ3yc>=0251 ,/rF bǵ.qJvyGJsQڝ2mٸε-~`}y`{Z6>2.X2(S}zGCpY!Q軏Nc"y$&34h}H6L|vfuZTyܶ>ȿ:XHѳH"9T:3C %S9+4;eqw.`:]eB)yQ|ܞڃ㎂|?}"![n@ F(f`\[/!ϫz*mW:kuŀv墼x6VW +2X_"Mr;E^MVjNWjKiO.&Ʃ-者MOƐف_#eʘ/0[f⧽xrJMڒg^gaXֈ;PGי_3gT.WYړ ÍЁo `9J q,/X+~ >lH#!|8a'PY\%e-5hrg#%le]Н]<"ݧuu.~Z&}p<=wX=|Cr6%`˨Ux&miJ4LyKA"mrPa{jS{x8o'Qs]}s},x?/v{?!D䇂jDMB*=|6&VoK':,Bgv:o" hQ5*ƫ5&:q7{{[_/G׊`_`)gbFHdꦋfczTP~Q!_??=_K4&c2yW҇ S{bx/*1nચyX?3t:3=3GW\v[yUN/磗?ޅ.($GoM2mϸ9if$3?5z̳G~/-\y- 2̻4?Šf'R9vF3ǎV7}Sߨ 츚tZgBKy1X:;̓O ϠYb)˳HCe8JXMc#@ 9GF$9Ǔ}* =񽇋5-&YUGKNp yIͩBE^Ch*K(o7+!)cj]Z]˔Gk59|UvAu;M;&QiɍL⢉`6u0^Ί9S8Rc>kܟ t8<ɵ'0<PA +s_ʝ c`bU  `;`9_D =9 (;S]waB8:,{D{3L cO;0W0eTH~lC)'VTks(3þTM AHnس +8!N5[ +mKN^M N칞Ц؎Q5iNZ6;%<^G參wgԎukr,l{ _9؇l ɪ(upFR/L~â?=Ba"O3S2)LJNsIvS-'ae'έҮ N~(QkK4yZyH; +kg$55] Gaj_3!5nɴWܾ|%-LprzaZ{8=Z%߇oy/ v7w9( P~!@Mj'P'eZ(N`~q?ܹj/ㅄ7z%6[0WU[J,O4X2w|iDw +w6 p;pHp ^"Jl$b5efAy(pj9#g)}C-zׄcj^OXcP7Hnm̃Nne{/VH2A)mdz]Fy*]BMcV!7%MOXܞ- ߩ4k~eYgGU, ᤆHbdh8;θ`cveh IfBXpkI92l3MgVS"]iJ)EXg0%Xbhxzp(Q1ȓj)VtӕZ0. ͤLTLgJdh"ЭXib~P 8>OaE`&,Ho278|c^ o~qnՆqD`Cf-e~*]l O }Gi0{W^UvҥwB#uEEMQlM[s.69^y?󇪭T7~+:Eqs8 gyY*XuWL5#k?}v<;Oj0p$,+pE}.Ñ!05`tc{ /E^aĿKﭸb}+uk88hүjS=8s+䉦{$'Z"9\̿{#ƭyUmn?p~Ùw,Poo";WWwOD6)h+F'چ7Y#rQr a}(@Tᗌ` "LAF #8ff'>>Y7A{{O> Cf'۸d5=˜''/r>||tSޮ`[;r0bH 5D.LjH0 %d#5 Ut)_AjNIIlASZFMLdcN#ʹb{FV +9XC9܁L seD{h6i*~;JԷ 1's!@p>{n9Ɂ_|4-]up^cǾuod10?XMk_F;a%.f{;"[o=k4S,q%c9=n$ٞ@ 8^L ʋ\N/>ou(0rt@d{{crPZs:?\ +yAFjaO;xq_`$c &ַn +߳`^6`C!uܓ_QM]yH]pţ%!HQ ga9tlq-US@*X [eeQ @~s/9{yD8m珠-v ssJixxQXf<66.){0ƓƧnguJG=Q@sܷ+o?z1]GV,n{E]qcE6IױyV!5~+s6=o!ψg"lqƔX gt^0FsxLuޯۏ!hUMCkT\N5mU40k/Pj4.zw_n wUJ0TIxb8hD@H"#Lx]7* 0Pfn.4dbyٝ  B%~ u[c{3+>%Z>j|0p VLo>SE/Vp?=>Lށ{n֋Mx?t$EZLsVIv3 eQ=agow>tgճ. zE0O`g5 +Xϰ VmQi ̥fkGtalj_?OS c |揌 ߑ};Js刺_3Aup7VIhvgzK p2H9 dv`UNZ#+QkӶn{vjs*NLzu4Ӭ;3`BS"0 yh1>_$ pU!"/̜dJ`ḶHCb:@8Q{q&!MwV-dS{CL4ӊ*ug0'?}^42Gpm.Y_lQ1BH R]ؑhf> +}qLNA?U0 ̊Coc8y$,T` wr{}jrHFi(eiל?Ώ'ī }4;]֥?>- ˭[e&6V<6-ã8 D +ez^?Ft0(v8?Ba_AkõSHGQЩ`:Y?䏃?:eޢ*aQtXA$L&B`LA&πzv #zk\#YwY q1D|h" +{9$%d?L\(~ 7NPnf~K E_, lT[n0y +e=Ƕ͉]1* ?zӊ1sf;qZa G ^䏁?kwwXf)]$;*Rtїb)ted]DQCR*A{KED 3ܙف,jUnC3"jljR]f~xm\$2ӫ{N +ÖZeD Ei٣?zrWg?yfN8([D[A$ _ʔ3`x>0Ö칡T'![]l՟k(\h 1Ag/װ4G \""(ȅH "^Wuuk]ڮk+)Qw;^P]EQXwW` {"]iW]>9sf&Lޙts$k+F#%`%XKa x" ` f>X.nQ\`E]) ; +clhOrtG0e'z̖<`"cA\hA2S-kb<g{A_vlZ,B&Ij\hP H00I/Ll +V$kV>?=\q^{[w2H_)z.5 +hoS;F`@gL)w%,듵9|U[if 2!!/pHE`x=m撃e{јP??I4cB =8~ tb l:mhÁ?{UgCNMTqډ-0\7m1[? ]9{E\*H#.eZֵ͟PiZDLr}>dOǕ0pKcgxi^r^O4g +MPB˥- +9VX  ^CN{V"s 2NmK+0*\-ڜQ',I 90{ 0;{xt%m`cB;t`6X?ls +CgZ$!=( &/l:p&:SݤVgefrCM&fORm(aYS 5#K~ 2N.$^X d1@B Kdm+ZW0v$Y~ίn~d{季 ?_ 9E9"Ц&f?ڤ0j5x]%cQ1z_]\&}Qp>Or@Қ/#9t> j\M11B5l+ P>1bGXy!Ӗ>j<uaL7}s͓'=5W]ad!_ 0a?߷#{7C̬-&s3p,Fb`5H`Kpό9ݸ"`x#gt-$ "kKb!梙p___[SVΣ/*]v7'a +q3菄T[E Sߙ@;clw]Cb<Ҟ 2TwϞHqCTgy_9$nX3_Hrkn0a8` Jc ާ^=Yr$~ %wky~,w'37lm:VkA`QNxEƠ^G0߂&jQM^?Gl3 +_<qCUM'*Yrsbe20fT'D֝w}E; ;uL0e7FG8}Qxy|118wUJU`< 9Q{sy G|}ǡef`cQGmqPiW +)MEh$tJfTO{]`œ#iu5eZ.`p\Pă2j2;VDʾdFA  nqeqt + +Q2qgpKժzZTt k>qlOmG~nyKM\ +ϫ6N~o|Saz6=|2V$`FY%ۭ,5XAɚƌ Xƈ{wY@nys{9K)Kd׮"Gb('Yo5v,fV(N:*>)g |8 x0 +]nU=UݣOG#ᘰӔjp oԝJxxeOhq>#|ύiVaW +s&Y:_v뻓:p.hÝg] ]"/SHo^QZU? ٦J ީ̗^`cXCbSm.;3!!g*tpOC] ޏ\Odqk\ϖ-]3s:ps$U+~H .ub}[f?c=lq >>_̚HΜ~H eG 9cS2$b 7ufL(^r r;҃<]: +ɒPclY R1Z +XWNTx^$:Ik=I~;GއA߆t]s97݁v{"8&߬[`W&~?VԷ֦I&kӸr Dkj%de +܁˜bdfRP)' +T$Np0a4ЎQ|O8O/R esAan4mۼAKXNl! -UBGx5 9|TA%ɶdEL,@~b8]1&1/n/IqG]M>UF-ߵ<&U| +/=_׃7\kyhP|̀rk0r'[K?[&B0N~J/!= +跍Enqnᑪ@W7&^^X(._YYXYWqJxvió|zo~>,9NyqR&em|/IVʶ&I +S KYƂE^ a0XfU ;1 Y7r-YsSm[h +Sʤ@@һ(@O1w2%̨~煀̴Ul?Uz3A\o"OX|u'TG{Yun+Qr4a_-8A GěٱeABfܳ] QW+Y/H|,cT6YW,5;Ou"{*\Nx!`7HP2q2 +kmyOmA8x6X4{>zO +y3L msy?'LBfϵ}_=^wƹn:= __/z@ȞYhH77#i/߁5 Ln8U~ iIܐSu«u77T(R +# {{~詡xT_v/_Jj2n 4=kؙ1HYq"gxsM`dw=UOc`}+m8n>:660ݷ钊~ JxztuJ^5s26)B<"S_im0.ZW-Xɋ{m{< }1GzeM_vu GA /^`߄?R맆_2YSX]o:njJw&䭕Qo 7@ +j0>ɍ߾ 3\B7ѧE*2b)/h_;zwjgzkf*M`l-L~`o}c4}dkNuf6kfmlGrtLAh$: Ql d^?"箚y +`^9#;9L ydSLI4 :G kb<@ʯ-F<2]D`1ܷ3B'6^ϲX;jb i6qZL|Iĩ\d:xn;z@zr3N.Up:th6UodgigeU0j}0I!;Qmo /.O&i BFѹ൳_P11x̌k^hSx@ US1C) J,}s寳Opފ4K>G'0Η>=&k< \ +yMogsASJI~Ak,gT7 n`iǚE*I# sc> 7n4߷Oe/N;h֋k wJ}xMFw9BV Rng%Ad׵ ik){h6l5&@Gd#qdb9-N+:.eC!Ue/y)}˃)9Ev}65Z6Fۥ~I4$|ϬQB\іϯcυɔ_>%/n +Y(01}i7͡^,_V x?~z;,' ~%a :<_beL ̖+7qz8/G`_m,R?Lkk IdKЧV vخ$Ӟq}vb_{5Sk = ,l& ,pq_GzUҲU՞=<Vg_\%,}%Է~?194 lpR_=ymUb +S_2"3]7ɌK!=%ƄTahn㭽,͌sY&2ڵml ׃ռKs "!945~=Oœ;<6~Om{9ԞC: +f93 +wq.lAIdH09A~ xGivd%wtQ G{sdx5CW53-k~mӅ z._^t̰d Y1a7gLK1p8c2?c_AIi_O`qR+_YP1Wp8Xh$]*e  +$"{0fFή0.ۇx=M6oH%^hE3Qk(xr@ D~/Pb#3?a@;EXwCY!Dk\b€#>+?Pˊ}ia\|*p_?o?{^isA9yק;sCH@LRKtm7E"'NcӯݓƼEv :5mw9YyZou(Q>>Q ON(vJtp\6!׬VqF3aۛӇ:~"~7u-A3" y0wL +*Kse,)׆0R;:Ue7a:4V^*TFVk͋Q0p9k;+JmkB7{Tn#,19H^ZDAMpsƧwzD˝zxd6gqm&}+շ +TcơI'CXhSm߉UGCEQ^6;jSs?8ɲ.FH\=U0Z@roQ?L] #b/jn\dUmdU 7aaxP`1b՟#]PSMc\.)+^^3si>oz8٣'x?V̼ ڿEYzΑԍ >6q "[oK$Eft>{SIZ"L:zn n24uL:a^+o/| +|ϴ}yREK]ϹɫsRWfoet#%OMk{XVG(]СC0s Ln +$_Ib=\b +=h]XlVjkW"R+`mV+`U:=``!ij?RwGy~ +s4myC-=Hqd;KA;((` ;dl~nbۦb]r/^3Wr1ڴ9lx^+hA@@GSX?0Un_ߦKŒݨ|;^KC,g^5Nϩi0Ki86 hj>JWܺ+A*r QT<\bYhW u*68p@_oD\u̝%ЫKWyŰQIjA%adr╍ zu: [ S]s {tڬ6wCOTDG [e. d`?ˍ^u~i$x.Y>H I^m #~xD:-|7z0RuƳwû1Y| $Bq(Ѹd>-z {YnBf@ +kC$Mj> +-O.PxQk}(Y)ލq\*i1uMoH6ʟ*nOksuiaݫ#xEqdi<^捗5{67|E5a?>u~,c4\&p4o~G5ya<.U)5,aIP(-a!Zml;k{tqU.PA 2E)[$$$! Xdjzs]`)1UK6 09W? {|˽ k;ژm$\Q` +Z,A/E렜a;֧GV>QfXmշpY1 k ]ɿ+8OLkKYBWA7Y? tuF {DJ +':`Ҙ:`]u٧T-F|_ `5,-q2#`c*&ak' +wyŵ`&7>f?DZ`%Etcd+VЍcaK9$^Ny'i"5?%mFoP6I󥋗݂w>:97ЙIyW-(_ꃫ.KC_ՙLZDTe5H3u1/{"o-)|%2чn=murv9M0CeZϾh-0Ff.^ί,:ނTlh=9NF 7J`O`ZژHE) +93;Z)x>aSŇ2ERЫ5]w(xdXYӜwЧar~-/<?[6 :cd )Fyx3œc%EK^ b>O$̴݄eq_ڒ:o\t +dVC/G~1(Br)T YMG 1YeWz9#-%Y09c}X0f72.[V͖{d^AR0I6aq{ْN!,z$lg ċ:ӂphc[IU_E}pW[@w\EG=-`D~71a/>rmW<{E{jx|䇋`PzsD 0zgGGgDy,M}^x7E5 k'8M2wd{A1/0p`VdVIse_^bg\\g8Mj!@'̗@j{Zf*Md;< evYhkaj4Σ/zQm+)%Π=1q1 8y=Ǻ#_ws3ȺV$0(%}yC3jRt:PqԶ=4Zۍy g=-ssGh4ЬodohdA/ӯ`oJEځ #H$%#JxF`ߟ3ǝcLs x>4J>m+^wpp*(FcWʛ$U럫Tv%. Qëm#=fIJC$'q018ͱgKޑ ~ +7@o2B|8}NKr˛KNTl| +*,,4'ik\ĕ Qa5g5qa쎒W71a]׭+`|5 PDYR"ԳG8$Rݙ-ɥGwy =OgK~G5u]A*# !$$,B( aHQ{R(V@XqG@S.*hzNGʒY |so Z[;N}wi iGU=|{x"Ywջ VItM|%}E؁"Db<`xx(}0g` 3,<0'pv;l!>G_Cڹjdoߟ#^q T+~{Й~q"hlZkέ\X#%:KLֿF0K3Ⱦ)㈡vOٟ7.P+yB "5jF2E.1(5E~Hj:ގhUWXZ.1%.5uWJ č@BrXH4usk9șUw$j*7BAei :w*An tc? X/\v?6 #=lgT%M;<} ti=_ڀ4Qw8rcWk9-V"wXddb=#%٫3xO~tk#~~K=38y4 `H3<˸']]=}D1!{im cG$E0%Đ{?.o̓Wyhm.1h[WvWzZlaҘY܃?Y ݴ* n~)H30>$7j) = h.:[ }fLOPlHDmN{?^K.d`ql{$ +tC.`mtRFHطcUOqL ҐDM1# G&ѐ_t>A4D\ {| x^ qr!YJsMES}! VVqO7ƆC0Fw +N%6M$/ ¥Xuji\w"~Vu +cm/%bUwXu/A&-U)bD\+utW`V:5ѻl*煶E+$| +q`qoVT7:(ĬC.B<{ K_A冬r8xέ^.T% in7XsLC܇ߣIsĊ^QU:Q<”_dka`ȉ^W,Qx\(UHGxPf2W*"*R&|SqtI F@ջo81K޷:hv;4ڠ*A(kЙ# m;G#+[9+K';UI.MQ.N&>~o-ĜO.徫E-.rslq[ZgNƽt?#>i;ԡW^?UX5ڗxT]CuUduqi +s 0}Gm{Tํsc]ADtS5UͿVx{8#ѱuܯōF/﨨, Uu=҆"ܘuQr<ѵe-11$X7fA#rPX%S 0wۚr\4~O@JS ZK%p7fm](0󇪞kꕏ}"MB3IZkjړ+:AkHز̢jZ?0NB7?=(z//9Ư85TtF%,0X|OlXȋ,}x?`YLn2xܕv3߹h?A3JDqA#X2Sh"m~w<GNn 4 ֏KhmaGvl`|iMjYB{„EeOޫȯɕ\wxg %ÂfqϹCL~WK`W VJy&NRrq#Pjտk`L|*:iEEGjMAQG$br@na0j+VƄ(r/drﳱ>xqԣnlf2dx!T31d/[,k>">@~Γ6xΧÒK,w_*4W9CdZ"K:<335k#i п;7zyf<^W)VR&*7dTcr:aޏ&U{|褂4NCl|z{0ݰt~P>X:`0Jɍm\#'{)A&L]J;"LFaUcAZ|~ +COB sSlh;xۙ܊DOH%P*b%b?.|鼞@ > 6WtnoZxp]ӕ=V.QatMMJd9F5eH{{?ބu7;̛br!TMh|̙܌tq!Ꮹ71jtl=XSb]~#}qaeܣ8> +TTIxDEAyf[muViVjGEEIxպժ\۳ZObP@& d!7!V\sgܹo~r2gs5 l96XcF#P!fv,kUkN z/38R0OͼuUn0뉵[ĠP55d)X9NՔ#*\ 巰-)P<\tj߯ӹ*'I^s|>4nbTfьG2*EʴHdZd&,^Ӝڜݹz5GED8(W>P.@kN Nknp.9E/Iwu_{hclt#'c_<) R%aC5i?MF1H&I :{oE"quL1q?d7SH<%z)s:ALƬBo6+7%n) +RR(ZID0QQ }EE0F7&iNkաS۟+]8=/=v+aZUpV5bzj +LWjTe:"8yj)U~N`q4GB6:'x3+:KLExz~OO +}-ǼMbmc(F$s0id /yxo:_c^3L?᠖N1_7?irzc!|z^TNTv 5c7X]g܃! k5GE$xO()6KT胡k&"EGsO՟8u͝[fPcL{'isﹺ9ض/UD|~`Rc&_ъև18I ~~~E`1?POy$6 +pbvrW["Sá?tauVۘ$Ms~=#^+{` Oyɍ5,U.mz2$ 2GҠ3%U +o_XYz}E8>?ejbďUR +_g=*'Dm zirs +pr<{V?zʽz.t8[ϡWo@ꛤ >rFszWFfE{cN}Q7zK.qz{~Nv®1})/Ěa|K(a@8%%^?Fş&c<"q# +*=+Q5ED B85lj|W,<[$j+UD4 De^`ç$^Y!?|;so/G3ÚS+)5~-xX瑤Fk_C\ Agn+!ygw$% 1Q=krⴙޯ#(-x(2?YN%{()N4I"&C#}M >[xr?ǛΗ {vnLlmBØo[ȯ6e]ߋobAj>i2{5 V^}@ԫp5q~%_w|{+ߎ9br9YW)na 2L 7pKv}I|hO@MU +w}!$iwp] `aMrYec@~RYCNu"n@ִ[|=51zE(8/?x~`~ pp* iqO_1c} ؚkQS4ߌvksCD k;ݱV~kC/|G珤wx-VZKjن5*2}e9&5dxKUeͺhY^d⸿?p QUx2:wi In,Vq9CEQ:N#{gM  +y-wϰta_aM_W@* - +@H_ Q[lSKj/ `[QDA}XnX=]V,j +~${~!Pkܦ{8Oý{ss-9#ʊJwHk[oqa'Qn?[f]5G0T`ёNܗ + !X\zDMH~b_aljB~ +Mp_3a^kmFokZ&+@!7AB#(drxGv!_Cbp-fC}靂ax6k5tQ$`8(A/FSLBv?m,.58YKy {Wu?,yf .['sh]x9F;sXUZd ܌I>,ٹan~LAzB| s?l^M̓Dcuzp=%ҁQKt(LWwc5;q@Ks:uK7bpA}Onm:T pWX0{՝"pI:KK Po{~gl"hb>$>$~8^8,A>< pS!%q#1*s +5emF˳!\UDU%mழS󲷦#znt+NȰ]h,=bеXbPgir12F͊T٣E%W ؓuQ:݇7!;Kt#jBy -#1?  N>䧾Z\QPkr^]?P%H\WetJ&u p +׵JH,.h(w=ǟ;~sx; M*/uUg 6 +g|U7S~s>jY?46 t W#4ں9t! Y=r"rYyRNLOBpqFϯ9&C=bTrEO2RcjMzD8?FC7m.%5b]4Z%WQ&Dp:\m\J zB-f3dz u@dݍ޲WwPC"|cGxM'W_oѳ{'K^ ,uyO*Ӏ9"RQٷe{D387%|]cXKdz_>|K4M#)WJdXN`rSs=%B]!|ۑ4 \iO#D}ZGһۯėDtCDIyi pB(&ڢ ZtMssUB;qP` 8ŀ)0 R༘U;"˛,47:ޔCcI'22miX\\HnԊ{ɲZh*^m.͋yA!o$/οj +F >5 3޽׎weW21-ˌ"W*SrшAcƗE#3F~0 g6x͞qK̝otCz_xGiiG٢NKj`m94]$cӪRD2,ۗ~%{B^L7ŮZKn=swH卷 #m~R|7+O P|5 vV0odr`t+%^%a<^`"<epY"<ۋOp# )TIkm|m!.ܔ+0ʢGyurm< 1 k M6)- Ӝ󜉭Px0r1^p8|C~qg8ȿ"2&o^`<1!Ū,Bi\ +M"vf~{}Yyxu?ޜDžӼF8ř\<I$w%5x>Ly?]>PGTdgƻYijqĺ[RV$&\CE B$DxjBU; +6Aʝbv!ӈ󚡼 [zb^0#Zx0>ť!H֚k,pDsGQ܃~P|c/}8*o7Oܣ F1QWM HU`贳=sKT\<|2!Zװ\ h +vYj\p]hXͣ /_ ![9QD4.>ysa܃0(o1>PQ1 b nF&Q@@΢"|"(qE+ +<{z`fPNV*Qתnǩ}=H2SS/ <[';'DU=}7*Cw[RCEW:~.9l׺๼ nVRbU=L8+kC$̰&E)ٽ#m~lbY-wd "|',9{\s}$>ƵU5 vwoNz+a.WZK{3\}`rzHoSZ׉.7yt2Ⴡ *zHCry +l3'ueb~$F=nVvᗸ'-9L?NuO|`F^(UOeLa$GMtц6TM2P. AE@6o~v5E>Lu8~Q1ZUmtr/Wǃ='j8ʤ97$Zܖ^uxe0約cB|OaAmIAaox='Xݹ\}Kb + +\};am +} >`?wW^#u)ߣ|ys +/Msv;suD@[.>L.'^#xٹ~uoN.g9[c=J+FOI5Vkv'gw郶ճ=.V&sdB;r~,,taî{3p:](iQJW!T{ҋ Ŷ^Wx +Il`kK-(oJxz#%Φ݊+Jr|elS$?<1mg=Xnt+(^Mҟ?SpWBl`);OZn1KtG),\e2U{w +ŧdyDJTzb&秾DovcpfU3g7\p|cyY:uh_McGЩ0Dcu: ,=[Qv/;=x矎`c'8`t`tn!d’Ht!KƎ\WsdD(ű.!_ZbC7 Ou+G5\q!E蚷7fS uOI^J[. +"d;D0JVMwi>&>߈@o=h$>/V3&r.|{XEh11o 4z1#Y9`K`P@9guʁrnҫ| 4ѻ,}?oq@ 3j @Y;o{ov@xiP&/7q1 ҁ bcG7[,i1{LS(R;Bu<'+3XuR׹)Du! % :8vu`V; +{w +D,.B\kaE>Wޜ순ڕI%)R{@z2Y$Ks_QM]ydl ";B!$@Xd 9c[QTztT(H:VĊ"źDAK^H{餎3G}ؒ;>gu.FPȞi$@|FY8ȓqP/-}ЦƧMȧN9ʘ`Huh6iXzU>Њ{&4Fyszʉ]Mx޹hvKt08ΠiBjM@ f &aJv}8izyg].%N̕:+撏6GQ-e];vLVպ>S5Rɂ]1Uq Ax2a?P %$10̀%:`;Oޝ%i`,ytmNW9+1=\՛ٍGpab>GF1M.<X V2ߒ'̙!d$Mo O3K|n8ξ6rz㑜&+?`P;k4cݥZHW{Je+d?^(|^AV,LHY*)0q kj/-|c}@x +j323B,-s`co.2Pu)ݏv`!i->G~Rz +,cz.4ݨ޳zԳB 9T'g@x}>z>m:L۲Y~g?pp/qݘ_c! Xm8{8L}WJo.hmpOL0K?s&ڧi@?TbW޸)RO>z%yڏ3-:``<W#p?x{@s3,g?&3 ߷&ZC?Zg g\Av2h`xAP<<3va6`ic7:OXJ2ꏵBns#||ɯ-F`PGyZʺAWR~QfsD}hct# hހ! +žT ZRDs^{3c[:fW7 ^"Q+Ơ҇U1–tLs;G]ew+gInp% 3bS$G4yKax\/.ٓY%B]$M A{FYB,&}?:+ml/X*P{kmiw,K*n1 \+J:3]9pXh0 +&v7k@42ɘ(ġ-2<< E%9Wٛ66cЀ6'2$HGQd t|MY%1"m T2-9Vsuo﾿eIWan<=Eh"iR,ectM!'ЫrT[2uubيJqNƧtzf[꼄{A'|^ؘ7zM j^;"^IڏǻUd6@]ґAA*-.>8n/?kQkʥ(||FYV +#rǽr^ݼhOw)3@Gdn &[NbKe'`&$\ܺtX3ȳQsc 90FȞ;=ax &~j֥-*68VvUW`xGz#G2E }"Жb:<^hh(o@삺KkkdZJ&v)]Xij5.7\eLq@-3RՁ2+\3M,5Z2zqp+Adڠ]yN"bM`QĈbX}0ZԜv7?t'玔>Iϵ~Z|]CcF6Q՛Ùyg) _rc|ݼ=A_r nJ4lc*^/D*+9=6; r#v7{0v^{[UsG>jׄeݡ%f67ԾkUYm'[I462ْcΖ[Y\l}{{ܒ#:_tpla#s `U +{!R l;=Oڿ^po s~󏷏g;c`P{5}j:ا^c7Ix38Oэgʷ +F^+2@اɮ=c/oMɕW*=߅Ϡ +#ViNgh"$ښLI'\΅a@N#K/~CbƃmS-$`\75yYy;ﭹ}r?ږ|\_ǙcGW SJ[9MJ;L vc":}ߤĞ-\"jKXrkR,rl*#"`>z.3:vs0kmV]>!: OqoB[#Wc{ +dKz~ +Bnjp5t.Z?Zg @,z>c[ } 4JLmeucF9P߽ 0Z3^Vq#PۓGԝ:>2aà/BǞc-i Wzig 4^`׺l G;/!9VIkru@|{TW֧`~t0 AfI[ϳͬG_iXSW/*a+HآEvBB l! vF*V젂"Beyp#b-{ڂTȽ KPsN i?{onRz wRw sG/Vx60R۫BHɵJd-04 Q1: O=r˾9VM|$41܈pXw?ʗ#++=#c{HHBN,׀>bA8 ,Fڊ'lͲqm4A~]ɼyR7w ^K֏8X^Ud{qPT9(Y!ekoVA 2'/7WN]Q;^W܀vhf?az޷[w(8`^ `TrX ܈xi 4r޲dvLVo1Ux⏗`>J.eEG93/0y2>hKҝCΣ8XiWtMy{jU@cL3Zpjx:sA"`sF4z "4 讛`Tn s?xp-^F7e7BcXR:p4MxjuSL.Ŷ 4Jo(Y j:Lc} Go5;T8!k:ܭߧ_Ց|S~>? +.zѺ?[SQvvT2#|_;4#?1`oм'ǚ {%S;$S lc ed4i4;+jG`s;I2~*Xh\@-;Q_NwvO)++h 53 $|_i՞jϕcZv`V0_p-y!@#?2s/lJȩ BT vboVvt=R{Zj +LF+kjC)s;o_Ә;uMuPj'z?K&OM*UZ3}`}t# =}QRPI36`ph `R3 q4fp{,zAo *fj$rANH&imO1'?̍J]¹˒ `.,;>whYЦidG,)k!,/r0kJ2UpM%'aK<1*xϯ<@;_L庇kβE^Tidd%Tp,o";nBʙ$kߙxQՖj@Lu*/<+QY*0e',! kB@ ,A+ZgNbuڪ "NYTFAQ .m8GH Zts|}{/@ i?{ENaEsñ9-AքJx5'B; *.1 +d눞ʋ5uvOW%n͗{Aibؤ8UVS;/7}̇W@}a;qu {LqH3P84K/EKx揟Q狌Iܦ#;L<3~@K_Nx|OM5l$Z~ެ1O?e%L_9Ka~ZH_:-ޡIΙ*o#;\і(--im'S=)a<ۿ5 +vPݓ1=SK Y.Kv.̧¹tFS1Z*yU%Km/<.t<2jʦE+Lq»)1eU9%`{}^ 0 yW'"O^va$uQ6e۔J'J_;OGnntOV9Gޒ_)BT˒(JYZVVs)Md|Ii0[xZ%HZ68K6DaoIkվK5f*K$gC$w:>@ MѰ?pk)9XSSfFWXOm6lrý(` OΑÒSyZl'd@6Ozų;3o| R~ȳ1 |X9$P%ovYڑrAZ6-vaUA]ZM~gg2jxEATp9 (B, `E* +hűKV"[TpQ(3ZBPDZv N@HJ72cΩΩO޻ru,lYj' !sc4dc!% +6t;z9G}~*SnS2ÿ8>A +m@O59@uɷ>ӿ8nJ\=x/ޏ~s{*}],RG}>P{zTJg ?ܢS&d;FfƍuS'61hF +?#JERz7?T(zAn%nm;{8fWχ>A~kyD^\^?\siֽ^Ra_a;o˛sΔl_Rpnjvf@A>sw4+wהI qU|󓆙` 86gaC]ZVނLjZ 8_{\|e hBB?럯c|?(^@p `a Nb' 슓ڷǽL(鼶#t|~V0K2&0`<J$XΘx[8g?iIeL *`.tCa\G(?ʮA\ElVu0wELxF'H]Bd cFc3 ="ͅU5DXcB9 +n5\ +ap٣w`$1uORwOpVInjÊSnv5]|a.{nV&*M/)kjztGaj'-D,d!xv?/,!upAQ } +^p<}bA,0e4c$M@yJd# 1Gj2@3j\a=z8ڌ= 欇1q`[sa-ѽV+ M`@LfvD_^dvL#=__՟`a;'ʀf+ />+iʿPV_()  \tj8<>5@_\}zX54F}lYӁK% 5kaՉ`xͮW"󼫅Wo][҈֪_C-#gpU(xcI=N9=IPqb->Fs>fWa'Ne((o])ͳyCbȎ z@r c +&Gb官CN8oJxv8gb) w 8~=iBV)iZ1ʭ(pWbN`ߜ" Ǘ.Y,:'ҫWYewR<-}aWabxF½!~[WQV}/wpm=HM(۶rp,!|]^EwT+)}?fExxz\o<1L poVzwLu 'u_'3_&ɻyFy;Knf;WC+ X!8&Vݬ(y>bB*TyVɐʊ@`Hg ;RqŮ! :#%@tA F,]+O k܅`J_F%@\ Չ`/fi [HT0;&ZX1v@al?tae@f~?ccu_Dfk%†vKB7viu)wu|^G+,<#R+f&Kǁ I-`MNUm"%cb&_+eԙ%#:KB ! ! }"ԭj(Ū8R7F2RQP"%7}y{99g3=3Η{oS~ablV4WۦRkIqDu">A|0lQtXѼdг%).GT ^McrQ}e}̮&ԶbcSY^wlR K܏uUbsrK@My:KN".cM{#_M,#xk8`DWTb?,<8ǃG\8~D$G+|;ԑ5|zXQj$P[ލPRPʂC7crˋ6\ع]/5L@u)ϝ Wn .+.{? IHx@{X4`+u+_QߌpzG0hZݙC|ze==RQ1^1+`wtm wdzE3ȓ V:˜L0u9/$r0-g9jDdA^cC + }q*3K3/UQ,rY7_5Ð44T -^dgzfvקgib>$vDl~HX}bs )0O)Kev0zӭXvDTwbg2ce"~3' `ӊU^v]~bc.:4X zӹsvқxMԔTAj U9J\ +lq?'<߹YOgP (V?WUeS6@GfTC"> _N"AڛfX]IeYS$g z+?sw`Lz@]$Z+|Z~u &88sxr$yRR`rBUgnRއ =?o4Yӏ=q[. 7Nj&IPlѲ* 3э2Vko)b*Nrɼ&h>s O'7=€nnF:,lmeA|>o:Ĕvgas5h=؉ʍ,A&GJ-2wM쁖ҏZc +\h<SMd9yo:<LJAxo9n0XcVw ~bWUC+cզC0D .&3gF %##e=<8s?;s̓=9r\!fŽlXM8@;t?@g`{kTmTo@er^N@spꒆ2^=@< ލ FE4%_XRՇщFw Q*z`Ș7eЌ0$LxUÙӥ]mRQ q`noG7l&ZΊdX/?%ygo.ڙ`x`I]烞G8Egk]E_8r'tii/o<))y:GtĄ(d$y@eHN'=3!`U b@pm%0&f8Fڱyj6_6"eS@"Eunu]o JTd x[_(oi[sDEI} `ccvdCg$]!@sPYRe+n?0+ʫݿ׉4{spـNT=#T_"]g|k; +VF3TEr_tlD]^S@o"YRcv`tF\ܔQ 7u?rrt{BsvfzEff6gMZ`oq^9ڹsGXOgPsEy +}*pd6``F)v{S8*Wd +{y|O\esEm&}AUdh@s+e}aFT( 0(, FuCϱj5K4kE#GIjLlpEԪaޛy>$QhH9yo{?hطǃ`sٝxk7T￰e LuX2s#?gl9)?fj72kCSUtU;a/BT{n:eWY=W7ݴ8=_f +WTō@oƳ!¿i3}5nl:zV.gȍM 4QBNQkxL) \c|s}ICW>;r:]sH=}2Y p6k/9VaO?nj:O +\8N[V7QW)TY3c!Z uFǹc{oA*,Vdn!PM}eDLR5aT '0%X٬QIƦKO~^UvfC>WM v_Z&kf0PAsHR>KEQ)>n8y@rW +/% Z>D5` bVFb-ߩ{|.?t4^&|?[W̛;g+k&ْ,(d7+;džqjPJusvSAzly*]mCXGHJ=y!:ͻVɚX ̙Z,VC3{Pvkˮ0TtzX:M>X3߰y60QR:;vQ$;p:K)f{y y NJnu>v'tg-7冥p]Ds+ۓ;z7Ai]02JJ;_Q=jY``L+_{?g!'H EuPvUaili ?SSBoP@MN|1xz$} 2φi~7L.!R;ĭ4토LnYHcC];>]Y*hFDٰWP9zE|HӇ4WQWQǎ5L4c'1u{ր+ a՗`J +3| +v$ @V5u;`h>=58K308,fls)j2;)?؊5%LѲ}D, =ɴb3%m81aa |0߁kJ])BF_0a}_:HFLszؒLkK4Ʈ˒'R[e?Fja]*#`熪t{"Tt|ύ eߋ+soN)`׌NFuP?7RnSM`ׇiHQIL}Inh%gNUզ֌_QMY +*2*H !l `YѺL錥uA (Vxu<:NTTǭ:@D; Z+3%w~ݫJ/.[}_gvpE!ӹ +ɣ˟0?:7o!vw闊CUnjkg#!0ֱ7A ױAx8 +ОH$b (1ݴ_(zEM[eGӸ#8r .?ɱiS&^0M7S;#.=R9;~~*vw2/M +$g؍*@h݁1Jjo` >(`$`Lwc?PxC?^n{/Wk'A@8ò&eq+_z+ mԣ;l T>/6Yo'?.o:2f(?Mf,PߴMb\#|T<`3b"ס +<[E<{ٽUs/0%`ġ6 @<$Z[T$OnU|%I50~`?ѓ32*EV_$*Eȕhg+lﴇ S9⸦oE`.G? `]47K[DCl39UG}ͦ/5rj bwhCsEcrE(5EE~=Ek!ޏ?aSD!t5>:T[#w̉sUp{ +@ĞZaR]mxtClƮKb^T-|.nk2ї/#m;k@c'?Ly"@4c.Oފ_Q\|0!/CAyp[ ~i=vZW9~w£lfT Ga?yQC_n*:=tΐՕKSyuB>Tp%"5~h%xZQ#ǔ0Ia\J6Jy4 +iT}h |dv.'B{PTm9tF*}^QK }L +,yU7qTǞmK|BΜs׎LU0GR_vDX;G#_%"{=(G,G,rA.\9!rbe.u):1sp|$ +S; +jysB澣p0·xC Kа,AX!:9VsT#_+:? dJ4<ј]3,8ʙ[֝f\l,t[|SZ +h>w XLRUi3uxlΤtLu0A3FN@N,{KXXQ߯,=b`$Bk/qE;Kc/Gt=ƶ(b 3ȫIP"Ibi_ogu zca(|i_}\q2-1Qvt`! _aƬ3e]ׅ/~yy/f3/y/ 5wvmE~z.j䎙5{RZ~rG/lbIx<lC0ȑA&+Ŝv, 3=Ɵzr5y=yyA#;$$hnAgͭ>fb1\=(p 4J?mӖT3}inGWR--X/޸>:e-'AKf}pNddUq(\Zn 8z +`V]WuL'nR'$Mj]H|dCSW=_\# HoO^oL7kX4.1ȴ"{'rn0`%?ŗk> +'Kлڑ]J0ύ7=]5⍾ퟳE< Bּ[([%麂mz :7t#Ka]^[wmlP YANŵx !D6X;ڦ7AmZ0gg89X8ƴHW "a/="L 7f T,c,5]R`sj茬jMkcVS]+ Bh1iQ5GK{m, 7ǺHKUuy8%?,%#>W̫%ѵZ1w1eIMg , SL RBȜ/(T&>CFI"U6^tA~ a샙_]s;npj j1A9}D֕s01|` :7K9jUBJ* cfڻ+i۱zFfҷpOn6~6}2ޟ}أ Ɯ7 =DɱB'c|3ǀD%A͌L6}?b%ٳ-%ݰl(U;u+_JeM;Ri3|0`T +K)%mx!"̉ɘ]`Čc9mC0lbd~ʹ4ͦ5KCӒaK` g-$Ie(hUHn0[ 6+t;X' +Am_f?wfX_QZ.MXR$KS<f?lCj%[R?TVSdğ x@Ԥx5ܢ&xT(֞ s{X'X̦G}Mq=|Qẹ8@XEqds\@lȚ@bRT+T:⸴:26UqN+ZW`p{"/Zϱ?{w{A}ET-d0s̅-ϡ@rCeώDceUev*xB F{m *G8qp _U](EќC~xAʹuՅG-B +z]_uxxΜ 6=J4l25d#FHG#P(X S4>ԋmr5an@0X[`'VI@Wt}6byZ[ks R'`9{/@ +IL ,D16es5 Şz&WbHc̸ uKߑD$^}$fS=ArGh1AA9.5mmFNlw2HY4ܑ{jC/k &{y4@(?Db´̣!a8'MotA.4 kLeG&G710~@*kc]#IcNF\b;Bnt^|uGyS(L! s?yşP˷GMz{3jO1YYC޸#Zvv1,$P1ݾs:_#Unh>beknotW\QnN+˿_ȃVwL>جuBe-='8IK*%ob垑k3h;h~#o",ly ȁʝ +I + ƍerpYKk V@\ e-ty{TwO +>i~>/iYWUYy Fu^wN Gx8ľDeeu쩓OS8 + W}| 8 zBjuRS#]7;sr {L ${/C߬q l}5쏠bkLp-Ю?_!@}QEJR{D2݌?$pJD !8-Td6R٘?PP ^K]>91žA# eQl@BxPp[$U\ѬƏm׸>|oxˀ(Xx 7RJA'wU/M^7?]3! Oic L2s1}'RVY䨁A \+4&'cX]u{Zc;|8bz: N@%-U @#& ?/E V`{K!|Ib8<غb9ǒll {T}w[}bM 滯=I +j5=fdPnZa|o<-,u$jvd#䊸cPDb)C.vgO8%Y8gq/a©}7Omaḿ`%F(1HN@=+/:D߇YRZ&CԫԹGbur&PZ4 (7<+h89!Cd +Ml"ڔP 7~8*k\o?Hu'l֋ KU{,Fwk$Pbω` FgBCc B@E$xZkcқI?B؟ ?newO7*^,~.w_:wpQ՗*:UQt" WTVs.>_6o"'A])cx&Xx8|&(?}Xǟ;EvUb^QɄ8a9U"$# +mw-V-Z+Y$/sPL{ez~y~o + +z&{Dn] 89Gt)ꪧyS: :v(x΍Ic)cZ>#e~Y<&LIk0wEu}e .9+>N,ݗlAfC3e;87Ɍ›(FA7諸Фo69pXl"OAGJEUg(}2ΖAȡ1-q8wgCGQ_°7;w{~rGm-(;l'~/ςk0O1&jcBE"Zg`r.A%{nr +mL Kd~!SXR2DFxIŠ8xuzxGDEmB W8 v{Q/S8ƑP|S SwS;ߺfia]ɩ|Qf#c 71S6 8hKgT/zt~&: +<Ej"!J"'*wQ_s_XS]:ylaToΉbڭ찦$B.wYϘ}b-AEW<t1ྥ['}!! W*X"UX۹PՕmh ̸H=:W<=/_y{ϔ-4Йjt7 ++ 2PN:[bpΗϱGo?5[;ѯTؾ-zVX&s.sVܜ`9uBX7zD?KZg$z.Ѯy{ `A59a"<OK:g0) cY}'MA"-%|W| FH8R'c\>/q2M cPfcXEޘ 2j+U+=',h"n,GՒ ia~ .SZsVkK +n`=Zt !OUѬYᚅ#Dӂj%=~9ih@@Z-}_56J$m5pP{3~ߖ|j{:Aأ~^:buC$Q)(ȭ+q&%?"葍=+@Bbx/$N78A;8Ž—_zȓa$>]p+V>ra2í]X_o``G E6#6Ik'a )Vp \QQQ=\LE<6Z ?RbeAGd|:d3Ch10XkB0J +0X ơw-ʹAu= 7+K[`kƫ+?\xAit$7{Fف5NhM:uh36mK4}U(!6CIl,"n1hn]7(r[$=`dZ/v3O^ɎWưd"k "%w V9A& 0*\13\2H|A 22)L{DM[(K$tH6A㷕6D9%H[|2ߟ[sw_Oi?՝Q!vP,mh:uIزϥ{n +m<&խ$H_bMjA8O9n /zb3 ,٣1)9fٓ^NbꬠoP;~←zIJ1ZAnrEcеCpY?>4KTíQ"t1tAvM_.pP SLc"A +5(ܴK(7PpKswǟ?=r/3i|mf~sFҥ#0wt??w.N<<}3LzRl#!ə zۍA*JrKoTq;axt{.pS!WŠKI%wmY~@D`e DpGhZ21EG3 4jӘ1 -&JZFkyuk׸@z\awOͬ\F-BZ!x_ycLjTUyT gOrPIpe~_m|_Q#o>B"kyX~ {Dʂ__.H&6\gUլΞJP/󸦮,H*Hk -a -([ @  +Ԣ +-H TQ]AEg~Z*֥q~u/eaepP~9}{߽{%Gl;I%`3f/QVLNrCrMBZ,$` ZD` P a٧9W* Qw0ʻ'+˚p T)WI* f=sC4uzrya> +%]R^_=ؙBd$O蟑Z7)9G Z}ҲQ槀`ɌKN3T(\_S{4#aI F䏧WYlKow.fܪ_l]_~ 8okV+O9Obbjx\ymt9}j4Ȩ|ޓ۽UGfwLCǂT "Q{u[Dz_ήG:Ql%j |RޚȔ;Nj|էs:?(jN)7K6 Md/z 5T'ÕMi-[J/y~TݜT0[xNuFc{?;Ywe#)R 7lQ>'F%| 4 ;ZyiRO֚#ް6=D5;vG ~u|%R XA>o BcMeOq|Ge 9tF݁a D&?V~ sG]i;+=f b2of>(()Z eܦ;Ur=OЍGP?x{_A\9t^ e04YK#yAiIl:s#LhL%cDA` F#Y xnykc)@?%^#dZo<k7#a-z Y5ա{]( ߖ[8?A^-ߎk3 e}촘s9c'VEd5ǞT=^ L5%C/I^BnVONL;nI=#ƻ%;۫"XXjF[Yd,Mln6죵) ɼAd\`mL=0Ї~gk ư -G Mc֦7p5JSͻhf++nշ}QG.B؇szt; Se;FiWGM=XAzBݐX98fV*Wdp +I+،`ph_,4W~HFYf[מǗSNKJ: +n=a,D^8a'Qwe@qg1uܗ:drr P^(Ep±gQ?ghz'y&^Զԇ2 +F xMRv*a :A9A&e +; -~?P9Ȣ*`1\xn~.Kw7&ßi,"،ñFfpOJtµP9Wf|~PFZK`6 }+ucoF xP*nZ8 Hhv;?y/Bȍbn.g9T3r3_>)m|Y[>}FjYTP90@2zdsA Pz懣;/ٲEL"4p  LT Ҥ0t˛*[Tuص.dǛ:__ޣ{w?pq%[7mqA.+q7ޯ2`P85]CcҖ캏w^Iv1/\{<+\axc.`v޼&]I_QMgYdSpAք""BgGmOTttdl쀒۸/UAZ@͍x>H;3nn}{<_ͬ 6BAĎ`t1/AO2KG B4-qe23\S$*O5.. +ۣ ;G7[J4aO0=1]Jx3nPfZ &5V!:7@_ks⓲"Ղ8(i7ďT";.: s!iŦ ;YV͍?bG# }/bJ=5j f战JOqG9+NkoOCԡo(f gTwF{'ɑ|iۏSa%,~vB&z/ aW{PM WӀ.5_F6[caP% Zԗ7PPJ"X8n S,Nj#Ybe9;K.ځv ֋8j_4}^? m@co t_J!uhV'bg k|w2/a8YjyD.8&<ыgKΔl.^6g]?lbٞIrIO? v`\4eAQԙXk@y$wL!kհr2"-QIET-[LU)~ȗ ݘ8S!h"-d9d)DO0NuI0YwNW}&пsκgXi5?>s=˖>L{w)::1oy\?|&d*d_B=bVR:OõNl}~lww dZɽVso5;'l#+=1lMzFr+Q<%U+ŝdܒvl)%#vcoA0!愔Gmr๹f}~}Lg;'€PY̷+0#HI|*[y?&=1lw?9}\, 1s1h)Ӎq[=_#f.Pji[J-&Eltŀ@(ONfS VTh0ȕ5zfLNQmEU|. +b;DDDz3҇yP^mz;v :Wmp씡ܝ{x.|f$ysׁݧ0(I/87fCք0anUju[0 NT[Ee{]b w;Ӈ;Y'"ZjK1J{׀ 1{Q}7Y#ln +WZA1br&] +]r,nI(wBIʢV*7KI\8P`<㹛y x I_AcDjʕ<9I֘0]zia,A%39 c-u཈fb7yZqߨ͜0ʰ `[W-nF +38;"=qW;q+k]Rw6XXvt_Ğ0xoH=ޔM:CIEL'^r:RKqylIHO7Bl[ʻҁ_a['nRߝ J*sfJ*G:yHkA +倯k{;֕ +~Ioi ā_ES5Ky4 +vqH 8տ=ag*~wՎ,y[OnS/d>Ľ EߴƠRi"3e Xw2mV̆GnI%k?oEB{ əKX-ji]ywo#r""m8Qz1);#t#f:nܼ#MU;#u;0#8w>gk?@f}k}B}VOްh"-B2?k\<޳RjԳ&?yWڟjFE_2枼мqAG0]a̺͑MQq'Z֖p;s,[aelڈU6Q0b2H rx59߃PEƔQZsPm|h^rlU))èFW{uiaXgzx6_hSnzi{[BE%© A+C&d:sw +HTX8 ו_CqNxqIN'MA'i`:A +&:p/r:"/&0sKho muđ ]9).Bv3;=s-oY"i,*@I +-^Q8˷p01/ 1)AݛjQ%q*e;T:Ϋ!ӹ5d7wqz,ٻhjt*3*%ԋ\qwH-v_ڲ"{))SsYj-T+ݙz;XDs}/c@RUM`Z7"]"ԏbuR3Lټ|w(VMqXx@'ao.o~nzZB9qog)*mPwPM-ި*[CjWK3Z2PD~ |A0c>p\P`EVSڿV>FӞ0.F_}%$s_%Uw/εlGWh 6Gy]̺#'狵kÍ2?IrHJ%9ȗ`O;)5{s÷r*W{pLIwjAZ[KG{ ->4 :lasɈEr5_* +fuXw94? RS#{Gj+>m0q`9_FOqHZkfW'!控'o~Yq]c q:XAP=H FMNfOkXa&AlWOY߮8+6z_¯MAKsqst|ـf6Msf޷>"[#.[=# +<<>x=% +<މ+9qes +Cj4och%Z$cZݺ›&6Z'Sn1>Irs=-$te "/Dc3 =#j7ϓvqC;@ue|[gH`WjX~7,A?$P=_voPiz01^q`ckulEpwG ~Mѩ|Ń. +.l-akD-%/ d n#0yRK 3 `:x1lw>|o, + +iQJOj!p; L@yx* "'nﭼN1ۼzsz_"ŕx5`_^oi o sVتntCqn[ͻ5yq|%t^FB,sz>L:ef`p,]"ݐvU"?sIeFKXݾ'a!/AQhk`<9 +`p^:ڲ1>I\#dlSO|9#X9}a@"} `n #Vns$b|@ca +6 z[{:~m`;JP47xӄma`i:{aYڭcq&y+]As_6Vj?tv] :d9,q~`.)kֻ-⾘l}2W~IV` ˵1n.m#d*R]:K]RE2mb w#cK,PDBB!;XTs+e9Mg +Eube[D-(csXsbQm<3I-RI%;+o<7U|}O @B!S)#of{+l([6I +HB̦|PǕ.`Q"D;=uHMHsuw-hx "ތ|IȫoPHi=I@$̀1duU`Q5 x=j`ᎫS,kzgAŲ} +KߧE0X7Av$;^C m0x<o|;b5SUk " ["ka*hPZX]rUdž熣ahGB8/oE@˸XfƏ#P,ϡy -\^{cQ@}z.a(?^* ! F?g [r%϶S@F4h{I/z$bf(+aXEآ<d1\7 ϛĂR^S.N ;[ {*(~ hLF"dO c`-;|XD4ƭZLi_Xw/F|2_i{.oIh]H&2VtC9;a ۊ}E~u0ͥ>[`P1l_Q)ᷕ!f-]q@Ɯc$,!_G +PtGpf0AWD3싐* 4 R+<~ hadg$0": ϣ]"E*eKPg{5`e_-ѹ-φiziSR($U*͓Dp\6Ugb8CSC*AuB T=]s>:Y1 )ܚOa~k +Np/SDp;*:y[ݳ^ӖWQ%(4pc:RWcv 8ޅ*^ + ܊y ^u׆XT_)Ӕ˽T-F+Ƌ|DB8nU.(c=>T^#|_bly3;V'&ZJ?YU}?g5xOaٲt]ƺdƞ]N^o&~CR-nﭲ%b2Hvc2hYS^AV劔k@H4LP30{T-Mˊ :sEs1Tf'nZs*R\ n@TfyVNW/j|g z9 ;:QtЦ"ݴ#aZݫc[/}hnؗ8΀yN>bϫ ?/XI oJ,[ .Y=cP1.j y͞|I+cՀ7;F _DKY].vvqP֘zqE\Ty#@B^y\sVKk{r"t^g`<~5}qE \1hp 1!!$$&@%GPPv ڭvV`ۭk*(KUHM4Xyc{ +G粢NT(w K{|a^ d +A0q0pu/ &#(gZ?w53J ^\V}~WP5@iڬzй_Ւjb @ό\]+c7MHU@99=棶͜ +;&w] :O=S?>Ye"=j|(k㙊9˷L}w<%˜zzD#cEDxAs{|X yFKރR&spPj4@W~󬹿wtZ5ɮ}!uPP7ܛNC7gPI#Rw4ʠNPOd ;'=gDFxQjZŦ9C;t d{%wHė;aP#K9sYxض^{Xe'ϸ{XĪ;axKDs@+0Tw4z>PxIWjV>(Ġ|Mqqڠ:i"QϢ_q<7vukk]cI IsaTٟ[a]$ҤJWDC.|pXj< ^}=wV`ɥesm1G>Ě*ﮬ!Eeixɀ-ϥ}wڵ4w%ddhKD +g"ˋaBh<עBN\)^#٩6C~&ǝ1&Brp1ָ'j`h/_"RkCC,qF=JG-90ƭKLź7]('g;j Gu\o=,Yh+u0Ȱ'ݶ3Zawx!0f|.qcm$ #/c4F<80o C֝OTḎ,GuQe}O0r"ilnξ?<"cτlC}x~_0|?,;^& SjpLsY/% +q'a8vOp\Sϑo'-1җ{eC4^ pjq7KCŸX>S1T[N^zP^w{Nu [X̳X(Uh4u vi{a#6:8ϋ0 +W@MOG3·-LJmXأH*EbQg*~;30 +C\>x%0{{; P.;ZDҾl7Fw}OBa3^i@';1whk3;<7 _S%@X4(VH*٨>FGg4GyxE[M iiP%s12Bl}=NaA(z`U3ߌLݣKa־}kٻ +S0h6)3SCf& +ܵ<bϖGF7@LBV6ȷo|gvO,I1ۥ1Lia.3'֜{МL,Gzg:&5tXO{B~{oe`V'+sdЭ@J-ny !l{h2c@I "wv/LAzapk"`Abhlc^*fz%荣T1V?14B^\F݇Co2u!/iO_؄BAB>9 8mdi1+D"n] >a6}Gk#BUΠB tDLjN^ѹ#>fFXA?ԣG>;W | K78jWόeՐi% *2(Š ǏK +8Jɒ7l ~x9&Ѥ5g8\IdRވ_GD NYk7*bKpȸG,/棨5SsRo+|W%s/m3L 4tq&-aJyOGռ Kkzwl@,sәJ84П X +TSZXw:`ΕsaXOO_;\3_׏-3t3װO\bۨZѩg~a:( 6@v+q*/IB>u9tx S蠯kjA&؂}"`PzlIN"#-/WG{ItR#@D X +DkG?&DpCC,KA dS͹GT:zw1f!m o0D l 4zI(< e*.@!Ad%$%,EPXpZ7VESp:S[+uc:ôZZG!%ܹ/猳~{lncn+p6KR]+Kk{cLWL[[$ t5%zz*jKbp6 r/+? ! .րҀ%? ,=1e`򦭇zq"2 +pTdpk\ v聽= ܑGFD뺤/ģ.i=Fzn4,1o=D-DʡIƽzѝףbt]QG7F({rO}^}??h):Vq3sؔ9 XDƂeLg Qרv`ts$:+xv͏0NO+^`"XuxE㥓WOTXYל7 ٸ&ϑgA%4eL>lNMXST䦬o"Ԁ+on'CY^aPOFh,#u0Z!Z R gG= +;"DVH@ =:KRnN6s + vA *L ďkdKAxF ]= P xg4X [?/17$ j+XνO#N[ʉdS!MHF^r6^n3r9S+;'1q5ČF9ʄ1ʙ>2]e~DMU;|e?ЎOnMvry>X<ݮЯbuN0ڊpؽsCp8kniwh|u6Xba!0P{FGuA\m`x- 58su r+dvwhychlicDu9Gbɓt3m(n+% WQ,N7ujq<\Yy`Po\Rղn !%*|vE&Wid(n5;yazaco,*j,I/WÝѳGbrťc?m̅\uJKF".n t% B!/4'5|xT[G+b)rC J>Aox1cϦD¨Rco|'s|+74+^I(I1{1#*6h6,5}{|wwsUY4ꪏ?X.×J9~Y&y4+fGëcGH֥K]Ξcbnou#?[?E?<<.4~;, o{ߝem- F%-cϾWS?ؚjE۫ Cc/b0gW +2䶝􎚀n)zPIx9}'yc~xC==hА.= An`H=kBs! *Q7p L}/D0C5<&%@xN8ܻO6,Wp>a>/zɄPDw,9ԃEg&-o䏰3E+U%"A.X9'?ɟ\Wd|3}z|(Iz?o'vTbƣ2 )|Mr0T{ɺ"U!ɉFrTߒ 몦;׆{  ;?huƹ_wx/3b$2:e?/8Y^#<㻯.aZ3e_iZF % }=0I~VCw /' Uq%o+g)55\~)8p&ᾐᘤ/kt%?0hm]s2/bK szƐ]$:u\M&zkG,AH-HHۘ{D[k{h }8PVF~:xs`K hx`2(3&^WvO5QIy*.Č=:&Q3gcHMZC%!Su\٭/x  VYK883 E6WKxՔl~GEu_qaUąEd= 0 J%MM\pQҸAM1FIOUcQaykikNOoDߞ`o*eb▆+:ymkht(+Wg&p^ó{Ib% AjS{ׄf&Z79C=< ./G\G4QJsz.]E IlMðLPė>QXa>mh3DFMwSM z4Ϟk!sDLj;i(ЍJ"WQNA>,y98~Åώj$Y +,V~Ju|f=2!X-j1 oOb~lKp(GqִCػVte QseHr[!k;#ĝ(J)^*UTd؈z*N_ѿ&~?Zsvq\Q8{3zjL؂>Tn9]~oOl+[)+?Skq&Onu.?n`t7 wL3 +cgq 1 <L9ӧ!i0='tt~E݃-I;tL}*YuuHioށnd GS#-hK~o EA{ V" *fqj3ycOxc KA-Y"ɑŵe <#ї{=P7{dk=QS> R6#N3%V6y)a]Q"UDֲ7V#䎽}Z6_Q{$~Gp>5.v.TЧ>h1'粇yGd+,{FrJ~7mY 7[6{> +r֍z~1//=%}EL7s#W1˛l*D@L5nl@QVO~~1w5_֜ϓ<;9<ţM<ѝR6R~)T{J+LV<,{tM^#6ƵmIEԖ^glp\#l&Yn\(0 @]׭r6g&NhMi|@wi/ _U5\iW>qc}3wwb4)~66ηAcn?)cG9F`oHZtel3iBVэpd<|RafJWKg XNAqSZx#P }溘x6h8p̕پzh#3筞P>#t=EL$^*d҆mz hn7ʧB҈974e٪}kQ[2e }#m_vǸeip'-`urIhZ+ǠNLu >йA sKvXa4|#sfW@aHɓmv*0ĝ;䏮bzvUC.w%w'_pT !s|nQ˨ϟhk(^C# +&EߋlgL5K|u !z{7e~Z~Q0N6Gѭr1G\)a$D|70ъ숻կI4 D]'nWF$D봵$?uL}ݏ}k<. +΄4 Hrwn?jŷ+B`j"O( %(?]H# OeY7$I}kT;yo)ƨ^?4$}X#;*"/+7r?[6Ū+"qW=ض~ֹK\SΥP 40y<=&SFt;;A*\@Z*M#K#%rW8֬9fY[EC dsϚC1e}av0eaXDe aqh4IF#) ڠ5"X,ж1uiEQ" n}oڜד?~͛{w߽O$a4 k[( Mݲl&/!l&i#bx4C5N}6ǃJ ׬ٹ0UЕ_2P+꛾.*;M96#!鎨b=, U4fCo6c"0v5d\%mG8у3Snd#!;O-J|?h3h|MJ}<.??+W)Ȣ/oS6l_s3Ag%tA>7P/N7'6__7Zpo~q *K6z|.p^c)\: LX{/)H3[#{XmtNM%۸* 4:F xS/"4ȱZ[VߓoZ;r;s>oط6X}0z'h@f"SԶGBJņ|QU +c}X FEr8}y[i97MI#_B32v o&]|͓atB֥WyG˺Oc`5=dqVcK,5'9! 6$ 1]mޞ3ci?_Łh>֘Ɇ 'l¡_o ReO(sN3 0EZ_(1Ȗkݓ@#4iiacFX*,3}(k\7e W[h[jﴜBu)K%\dKK>u^Imq+YyVcx[{rɂ .1r;XXa R3E/>n$_GCSbT k\<|UӐA?I?p$p@o[Q(fdfM> /z],T +1mˣ3` 5F6 '!Ͷ<^.~թf +9oC ؓf_7'=3r۝\4ZwR򏲭^?RۏGsKsETɌ,)ƘKսH4Uwqך3uuIQAH5ڹ_sCA,5aC=;C+QG~gt֬?T- Lڗ)^&{_;;ӟo+}r,%v֧L15ZY.˪{ckg^Iн0CT-Wďq+ ZYf@$Qk)`t.ǃivDqGvx+ZN_[e +s\al):6D-)zICZqk7a U<#M+>[ +'e$ & +5!;EIH)*+np(H)TAQzܰZ:RZfNVQx$) ˝{!-N=Nǎ&ù?[ċcO병v&jj0E#Wa~pNebս-PCݐ'!Hu=TuI8# ĈR5\%|7M[7CVHA-wr}ba5∆ yxl?`0dX Qݣ&TAAg_#4ۖC99M?ŰVc1f?\k95SG߱yl,/wI!ԣ5gӲ)B4Si)-2StgeiXz߿$zslx1h2gͯژ?PEbz_43O{>~Qݳ{󦘒U/Q_ŜΎyΗۤ~8=31MGF[:ge99Pnƿ*)v&GoiqqUvgǿہ-;BBA>`?]+Koi d7#-I')ߑ:P#t&WxKwGB'*}hM }Ut,Zqb$ja%@k=}h(jhw7s<uq*;6ątL֡%VYhn4vgYXHc*}g<^ğl.=LegA'BM> +u&p^,kGw!3DRIPҖ &v8{36n9Xg,QF6o412z?hzw4Fct.L|7] nA!GM?5шM aq/PVtռ)m2B,F(*.i?̞TR1ë>/.#ЇZ`oV +}9?NE o vvøym4jt.5uJ|kxdeqT2ڦ5!$/X}xg╩.>oR5jxsmi wVZ0pfj-MV8b1j^w !׵# xd!w7Z"݈GSMb?O*s*XYrOM[~[k8|}?j XФsl!O,o3RW!4_XUQ69 Xt1_6|BQu +)]E. Mhw:[

?u/y;am +0&Wd;|10bؙ^scsXo0'31҂S򩆂cLnϵUH;G 1Nk[pi/\/dj.XubO筤dvmՎ ƹ5?L>*6).c-W(,\ԍy7inx[/!~b܋!?ڛid32l.anA9sSSض憃O*8Ye'_8USvvw} xŒc?//hSb_?k db:mH+ߺ&o9oBɈW]wh.ꆣKp`_`qCZ0Hqu)V||>E=* +l| [!̺-Z9 [CD}{8lAo)͛ӧ:̩;dNc,h+ZҤ " cwF;Y ד烽7DtqnrZYҕv?~'P{ h> 2Dᕺ$'f>^,2_2Ǐ{ҟɧ_8jE+=\,VH'䛾Aӻ׉z,G ̓7Ҭ +V*}G5y_qnVKM+b9](5~Z{=s[zu+A>.r!>주/`y4ڛ=nvJo25󿘠CV4Kޏ\g|vmRF?='Pp2l[<0F`ݢ:+ !y8˲lykyMk*C1^ꍵZ4ײ5,vbg>aTuϕT>ż8dUJvM9"%>u%xs)[sHpѺ㙌As#v$S#96ϳLw@!< EVXpÛk+`T&^]5*皆qR/1 ݻn{UND80S0rDsXF?#fЁaL Q&"K0kG1c-$5C3^S zY-v@ c - jڴ<<^o dilrT~g(wW>/)ox㾼94;9dĕsAȐ8V4|]-Y&}p~/Q $Ǥ~(c xDljpyc]?n^'NdU"bY0ҟdQ~ZD:(l\(7%UDO?7ŠXHQi|hu[UĠ}t0m,)chƆtH;43O?~(|mp}M->5ròX8\Sbgt,`@P +cIg*qA}cXQs© F{aЗ~^S_GP٭"͋i|By)x9xp| JN57zr¼5\{Ym`+{`Pzsi%=!v g61f b +6kogc6iO/z3q%5r^Ř3jWr4sVLsl NuOK5]ˎgs +CG|1Ȩ>εUn K:ib +w{S fP;\3[S[c1TT/+Ʋ+l mU^.6)3U2L rygƼSF?os +jSc4%SލWm5Jmjg`5O'>;jo[; ( q|=*M c:ޓ1o!.S+1X]rm *o[gi|X͏[z{:,1Kf`O %šysvi,aBaޭGzkEVxl#ܻ!XB~A?̫c.HedE2Uia[]$ۋ鬃=yp pu>'2G~~}/U  Fw1\n^6&!Y8 $y6LΝ򐤜{۪嚋E>Gޝy9"#QӎEI|0*4w6RqlTLi|Zu_Ivg_$훼\b~Lzd2ak(k\Kr>Lm=z\YŰIi SzXŸ..Vk@8+ɐ~Zޖg١2Խ LSB>Qhn4==ys1RG .y=,O (@8TT +5gYh&.Ή"DGupza9PMBQ䳸>Gar.^P~CA (̔SA̿Rak)9V,D +-zE3S %ʬ; EG `ʍ{)婄ayNeNCL0|<;/2l{q岂 ;vYk±ROَf^[ai,#gDO?zNb{ȭe׭.}Ֆ2jC"5PHDv4(bŊk{A_QGЦ*Z*Om]Vg +If`_=̝3߽{{J7Ȳq$ag{(\53*Yk}ޔE.v-t-Cwjvs}ST2Yǻ[Лt`tcfGd{{+}]!44Y/MZfο?Z2|]zF-@=; O#yE Ό#g5 /;\ӶaEZp3p7+|dޓ |AwuRW8&g:T:TrW*SkԳճVghfӕnɮW)˔+J~^9^ b`wgu4b-9& +(?K/@y4 . 5Ot[&ߓ5]3388[3%Ҩ8!.7Ek[DsTO&sR0 ǣYe<l{أ xQV2ox(ݦ(IT{+h2zXse|`"U]e?b͕3B[څ٬-vu/3ӧ?{߰1=ϔ؝7/E8\xδ:y i}?`1-w+JK\5:◽>t?W+q c[1507I&-1jG{#\v=_臎i Еt{<Oe'%J|C]sr +tY3㓀a葥ZeƏx WőV<)-J.0Ƚ zb q#%iՕp<@ fUҵ\a}l?6q#;NwTgyyB 'a@OT + +v6c~G§]C`u8!eQGlY1M> 9\x ؎32qQp\9C^7cKx'%ۧAΡ?\XKIeщ5y`߆6EŠ!$tQك5g~DJ%a+#acFc1}kxL_Uw?\iM +g%?B!o`Bz>C| "&%Ն=hG^2lk~0aU7n}p+\G-Mv]`לrO&VŪ] pClс⎣S9nqCqI8]M{7g:Zp0׻ -4O jx\ 3wu ʀP\ ˔*%o_pBڃDM͢X0 X?xb-x3H|{$(>c|N(7/A^܁D>"<'/\CU+?v1N6U`'sg 0@zsE\䍧ltD<@y6^ҝ[v cФ4A Z'(6{tϼO\ש}[V4I`utH4< zAO4czdޖ@ +ѵF}<۶!܏Z-]F N|MGԳ%ƇdfS @CuL$8Z#okciCb U#t35k>ჿVuEwgw\G}?NS`f@u7 8$oz lE `|!,#`R2FW'l$.#pZ6`6=*&%(MfF&JҀ : +DA E1Aqw͌=:*uG1!;MЄƾ{{utwϙ3ޫ޺U i8G(W0B& |%Z7WټaI\C5wItGl̶%0*lu4@}FX)9) +(13ԥΒ8^!*-Ku .Pvos@c9{=_0^Sj9J(Ծ0$G擆,+smM S.j`/sI2Q#WS|0a ga9 ach`СǏuR 'y@9t ~ +LMT~ʵ ht+[ʘPᯯ -#;Ji;v1=1&Z_sqL!M gqKlFWT4?֛_l9k9ywb5TH(LCUYDC}v!WimΒHr,}[{@ko?]_{lCQاGf;t_~ӚE?ǯ#W u6uԦo$\:V~ϴAmg *-ě!!~]uA iT"6q"!-Ux'X[!{Mv'LI/ ԭ!^Ofa>֗”7>&Lm=3ɂWnz dK9ovFć9ge?׎[X5 |ۆR +_D'ydߤDޢo +(_ѹQ<V½խ>keTYU 3"az^ݘ;A| zV弰+:Tǂ]6McxbP 馬с?/l\ǂd] .|`_A^7+dֈр"~ nsZ=c[78g%O1DgN=]9X5Ս[8~pIUQ]uks| yu٧ل&ݽ>{ѿ6,epa~>} +R&Qimuiܝ.]k U|`G#)Zx\<*mx8i7 U3tEFt3zmƛD~"Z^KkSQQw猀ӑX$SQ쫏OsM{ 5q8dyfMvm_grzNgZͱ4>^iLΩa?>骧)Oݻ0Q.rXOqMX^EaE V;H/HnJ((؞x '3y+k$wǹ [щ {b^${Wv"Fbg 7'ÐnO}+qNw/d1w0SI؂ć^vd|I^kf?ot͐W[L=QG MV3 59dӝʐK;u])Ȗ[$/񷊰9 +e14EMvT9@z3@Ž:J$1ڿ/ +]z`l'*ЉJx3_~2!#j"X͢Z`:7%UrKG^'14uWy^i} w4OkA:ڎe)yC_^!\68sV71S%| 'ZO1L$_MVV<Voc-ŵ}Cy~G9:=];+JP̡d"SK]ϩHkP% n*B/WFՔFW+xf|GrZywX[ F&5)^.XEm/ 81 +R[R[5>[C^SSUS̏**䜧0jFoÂi[&R?mg,2*X̃~-:Q/l1puK)s&փ?I?IKCHsDmIݢ̫ӛ~@aOɷR_8i?SIon^md7We׾x~7e0:ruP@ױ qVM@ >" + Hrk[O=P+11:936˩_hj5vk;xQ,˞^J浔s_mU dz$וyR CV@m1s Sbc6׮l%dhZSIc1yrSuo?d|%)~g[kM[_ܟݗ+EW2♮" 1SW`FiNC\Pi^:oǧ4HFA7ߏGa%MW?zec?^4`z6VUa9{0F}&|=P/j:-ްf( ZZCA0&Zrlam5ZT ˎh^c  ^4^Kb*|?Y.R+7%\!ѓֻ~ԔqL4{,l+kd +6AzWX[x4 A(6&>rE߸:9 5mxm5l*ą(]%WN">[Ӟ_ZZ Dž.`v{0>* hxg +(>ϓlDsq*CYoa! +{# GK갆@UQG'&(7fBp1_s=Kky=OwK-Yb/L?j=P[6ݣDS`xd|TO] hnE_wȞa}`3mA1g}^TC~D^v-s@ yUn^&#hIƲg,ׇ>U#11WF?s6cF7jqm_<בzo=HYc%fGm2v13Cݖ&}yo6:=%$cLa¸A5|}9lqQ!Gx٩\|7lz?sO=üuOݘ! 6&X4rBmu`Is VDjy4&깴kC?-]H)N k.G߅G+Kjw_ t6ӀeC#_^j3 +{~ /vl.#[jxNdyP6x0=+KD}L0z.Q:z=}NInWrOo +Jk-a^Y906qT9|j*Dux5_s4J"o>)]]:Yh"68Xmts'B chj>L*IU_8E9qtj[k;a:\?rNT nQxڪDlSБCޕ/qB6ߟ($w5أ @DbSg|WM?!b||w3:rP3v%vCL0v|=לut^ z)>$DpW̃A_9# h? +31 >9K +MÅ91Fk{2zhKԇ{x]WqQ>jRQI]CәTnTĈz6Ugrϋ=mbN"M`Ur}-zQ {ol/ |8n˰M3箯 Z;ɷB^J}pɸx.d )⋸CfC4oGCCB\̎ܫ)3=9~`?'=2&cjBM(ۼYonl{_/ğ#s|sdVOܴ|)lg g=?uQ?k//H}\5RnTV+ 5fbco zO={#q#bcaUf ,;,N9UXބ, `\U3TI:2{`[z@mUGr~#:._ClV 2wH~lݑZ΁>8]`cġ7.9p=?d٘Ej M`@MCuP !X[4Pw,Sʇ r 0Ta'jbNZ#d}[0M̪j>;ұ i~RPtC{CTGt~14@-d\V̰! 鳄W:l3l".tq=#ID1!g^%D uA +4=-Ыk2.)+#$wJ]$~1vL`YqKdM'BÄ=0v|}f> +Z eՕ'{ジkԨAdM!bD3(ƥb=K%MtԲ@AU0v *h# 6(- +18D[#gιV㇩L%Nׯ{{_~<P-KnDCD!&4e!vQBvr d;)W`Ќq_Mhi*O AJ?Nw$Woo+;Sy+}<'?Xh z0V;bΔLyAgX??hZ~KN  NZw= d:A^֔^=<{}OJYQr1)1Y _X [~Ǽ#.^'MK*_UɹyfcͬcfCu9>rr+% {-5o J]I5ܵ.efƩ A,{lGt|p)t=Rv`t8鹄{ߺ}ܠ_˘ftM=?eCjJ^Bz{zi0Q| ڧFO69[qD8G~Ԧ P8s!{c/GHB#5z=inǠYu}$`{v5''|ȼpTNX \wf u%0t!q\o,0b"Tb}f=.}k֯Q(;R%7p@6Aكl㮧|`돼6ıޛ <22ī1͵&`RD)Q""XAz$YOv7rvF%bF#4( (Qif`0%a04{={6'=ޛ==WQ; j*qJ9 C BmgХغϥ6K?Zk+rG|;!؋g[w./~e#.K8f^)c|Zj2:F5 +KzTQ0? {&>lW{8'Zkd ^Rr 1R|k1ӕ1HOqa cwYr|?fOYM~ckŹ!*îtvӑ5Qib7"ا˱>JP/&ٵ+5Yu##nicE^md]D%t.#*⾺l[;uw]wԏ/ȹHl>G>B-ɝN[WCƏ +e}_OLA `jUԍ=4iʸYrmv.8[!X)ҭ٣@Wz) ήSCGqON"."A67F6e?ҢE˙X.XIdž8KLψ=l"_YS[OɴkߢvRc1v?̜x%h!d:V@&̽=l>MĪcZ%`ȕJp_sn?fùC=]4YȽ/*T]iDE4f90{.i^~{<΍|%J+J-$=`o.6D=~UzZ5u;wT\חO 'hO^4P0TDVB6M<8qǡc0y,B홁G 24>̀9S1Zaf%j]l$s'g1GK#-$ֲi.0mvc=~X8T*Pϗ:rXvvD p7_.yXnL1U3er}$ 2D~;8K%a7_L|3p-4VBsw~qa(c%r5TΊ5y4$~"p_ҡb̎Z?w2͇qEX#2GWҽzI-q+ ;wNUm(L;^_VJR @rJ+rrt]k(56Ҿ@qD +'/O2wn!01?t~ @]EJ;VU]S 摒\MP[ce Rg̘z0hѺ5Yg[WCPI}%;}#b5CRn=i\vy3d=w +x0{f6+RBM*Ph2>YIh3>wfObH/=[9^҈_aQWxDJ\@c@PEEYg Fc\q6Q+%(D8 QaF"2" 3ܙI}|0~lwu^ܞWTĭ<\~(zՠOw\L@C1zL2]aDoF- o~D1U˱=Aiˇ~3=K[y]qnR% +/#@w֟u]jgj9,O]9숏V>IU`,޺ s}za~mwE.`Lgx2Z^]?'L~`UN8|0%ѵ u;\-lkoT +m@;k׵q0yLZg + /Dw#Kו!vtq6؍Dq>M']rϟ=G\~DnJ-0YJ}uU!fn]x~uK룻>wbF)?}~8ꓥ^;֙PW f2gonoOtL{hig9QKWAdMA܄ò;D$uq> }T0s;%N/;sk悮dl2F{xcPkk5K߬惵訰\˭hZ!Pmw6D BMkC{`P gU+])c9`r rgtȀ/'[ #h?0cWs<*UçB7j|,u?Wm~ +PPw,`XqLﰷBj-; e":}SrzMh~͡-kaB+`# n;%jwPwnexna"\5奧04vkq#ncNWޘ +BW0wb7_:ٷ1 +mI?$J~8)L+j / |̳q0xė^m@?`i&K1AMUKǃ=O΅q n7A<x+.T e7~=v3fbjAO:P-Z>ʦv^T8){SPxMqA*g[P; g7,K-AS 2Fj,͘u`1lKdTHPoεd4!~o/M\ThM,cN]J=`Փ]΃eJW4h?2[iTDsI>nOhh~_׶-aO3*3 3 103XcߵPi eE%'h Pa(׮Q +rԬ+1^f7l~<~S}D%L UL +{dFMqjD=MLV7TS9y +ѕ{S3Nx|}_5y>0+<^x۔;8)-i+Mk,x5I(H(j-ρ V+#;r^KU;I&j n@V9o(ż;Js|8Ulpv;M94󠥯^[8i/'؛> oj0ʷi[= ^3 Zp, 69r>#Վ`xk)ZKk% @h +, p;¡6ϕߨ҂:Y{uIcβP=yRQZԑov|ݵ2+|F)SPjzjOqؐpA@_̌ 嬌 Cr笾sd_0v6UӤKe΂@I.,Z9jTH~Wh#jy|kYvp9$7K1恫6(Րz7ރT| 5`ԟu!A0 +yqxgdhCKu.>O3Vկ9!ՖLѰ"}3Dz0_D@fд>W]:4gOC;ms+`oPSZu9ۏE|у5_x`ۧ!zO*oxvw2z3W9x>ʓO`gg<4 `@cTA}VH0ІaL<{Y/οY-֩Pó\X3 ׻w1#]Ƿ4aJr&e1UrvN/'A= A>Rtu{>YX{n j9r:Z{C3Ʊ/( R!d8rN)/'@&m(Rkj匸ą>a KXLX!'.}6s cn/TK$Bn~K%IP3 )ѿGS# +c +qvQh ==מqk;17ʷU*96dԖBvIܿ~݇ɳ}z4oMThQR3p^HR",̒HDjS֔1lZknzLX+ٵ2hK1*ȈETjo<_4V8?awrAb$#k*ẘ޽_A~ A~Zڙkݩv$+*7#EiG8D/[389kv[[,6S#ZA#W%ާR?ї hGP?+ћLaDXvڴ`uH_F+qVKʅ̑DX =iuY.h*zBf:7dn6gX*̔E 9UpADLlI˞Ow7Ԉm3䂌pk S)Pn@/I̙?ۚQ|>zMyiDcpnqnz8j+rc<&݃VkK2K i!I:MW%[H}{9EbM\ky* Lvodthw2j`xgC)Տ=uio/=߀1 jE(ο^.cph׫;/MUù{LFE%*(`bU,"2 kQc<ĈF#mi#ZAfH0 j=V\pA"âIksr[ᄍ_K%uXkFH$|_ז' _2wM?L]7ᦝglE??8Ypz!G'$>Uts,v7-iܿ~[w7K{ ++F{ V1"%_KD+5 .@{s +ڹ:^DH#VY@jd^d|y$ +2x;P +*{XdP mnrq0$ZLsP̂$Y#g'd+zZcpSJ@7˰@?fL%ʃU .}@luV؃\y(p9L\gqjycCNBȡ!O4A,@Ĩحۄuȇ^Hw]8v{~ݩd3 Tjŏ pg'l6ʘ`O2S'L_L;cƂ٤7G{Q Wӳ>p =m: 59{okC"10ШE?ǿ1U g {̯Pyo[o)G̫sWdS -!0w:ֆ4]jIL817y~?A2ÙQnw7s{|`A#]Ja\.w'W{~:G:7]εI6!v? +,ѩCwHB?%kf|!Zh{pw9Uk'Rw_sCkId{ tS#񆽤gO㞅+[,Q)vYGaȡãs`6VgpCE_Ey М̺ûaGyv)(lNf\É]_ i͉E,qՎ֊/&coc@.9Ow{{:k3o.w,H +5ZҶ"}"}Ug%T6dtfbnĪnx!1^խJ #kT/)T;.ZGjg +'N]wUV^sRFsJ9)8]0昁q5#=վ/<׸0ov㍂0T%ֆV{nnFW|RWէX!)6ɦkjE# C3,ڿ#Urz0KMV5BJw7> Ʃ+D]w5V.7};؉2U>0wOή9t%lGp [=X? >_Xjm>ߣ*+)(C\ow;Rh=k0gN>rܣhL|*.: K hhވ]i-i;˩aO}<=1I+ O0~1ܥ{Ejͽuv9EpaFZ͑2h5Ypοٯ+>QQQV*D@9VI6SRMAKV.ΰ"`Q@EL߼7jz=qϼyw{?ل(hʰwS\B݅VK>'Z$>_|@XB]a9s%Wjm88Yu7dέNn 6#<$+uq+ rK EMI +0o1Qjv{nk{|8 G5gs_ar%|=|u6 FZ:9}|,GHhȢSp2?>OV!K> ?W}Cc> @Nv% ? 1yX n#t%+D.N4&JxN4ǡŶз4>.cBLs.FPrhh$F9P[^u.v_yJὤ k|OΞ3Ok+#a8{K~қd%7j-^[݂\[#FLh d(Sn-|/4#3ó +U{־L>/1NYr6jy$:+9%^EHA>&N1aDd + Zj߃mƭP_Mgv5O/iI9gNu=k~7<l.^Or]~t4zOXz|2{%Կݶ}'Y3EIqXaZ ߇/{x9ҕFٜ̎.TYwbXgKIߨV*+8-'Envqz3܃WD*;}&q=X{6 `luD0b/^ v7Y,'7ՇD8ȏ̸Z§sƈd].ŝv8Hv C 'bRjN>NG;3?T0>J[8iN+`'Ntı"K;{vKJ//ClM=`~N쩚# Q12O[zvMDykamϵʏV/z>'a)//t"c,Nz\A>+̿&6.C>0 Y0*GZl\_ϱ*B*V; +_#+xm >#D%>H:[!L9b7i/X蠴ڙA4ELR]xr>Ω?2d8%0Ԭ;ln +mS-zt0 ٭eهsY56f]qY+|X\yJ߯ͯl?f iӯGHhow|0?apLz.fNp'~pxi3blN>/l? ł!O<[.Mjd=g1wu=5K`w5$>G(m8A`^6tbaB+m%~7Q\ I=#]hO*}^S:pQzbSDzck>usc8;ITr(lkBL:{~ xWNrĢ~gyED-ɰB.ag8G lhN*"d)o*?p>rZe4&er3n1G7LJ zH__p F_}};Phf+.D|_l}(>Q`RvX.xl0$trzg }a,}="1: +c35CfVBUO.@8A9yThԳV &'#9W2*Ty=@y忇ZVûE#}BCci`\{9?vay[ja.pYDf xaGAg?{#fiͥZjS Qk]|&-ǿٯҨ$Y 9Dq!* ͪ"NT5Nz&6EQY 5lFEeDPDtĕ׸MͭK1ssQ-[ݪ}ǨwUnC8sH pbtגпzڄdwض4S?RD5#/? &E5{@s1AIG>X`a=W>{ۣ#sQ{\cDR5vA EzN'{.~~KNc|4Fy$x1P٭c HGX)oAKſ1=Y׍x{?Q|t 8G [OO/3S2 Z/?_2̯DbFb]PNUP=H KL  ዚ( 16>TVڙ:mK3`fXN\jouA~g#\ +9=c.๝q$Y ن|K93'+EqxKWrWGF0\|h~x} _>;lG>{NPF܋7z]ᴃ #;%uK|K /}ovb|5¯ɿ7t_+Yc9*繤^ "-.l[vh@[zOqƀ군눿Ou*x>FX;oEdE5B b~^=rm W;bG>yb$t0W.+wG3ʫ[yb|R R'ljUҁth¯9$OQ_XgZ6HED|!f{f(W2- ț&Ԕ;Qx ?OBc@6yij;O]UugHЦMȏUfvɶ{eM#~4GgJ{.--~ko?3M4Y*ȜcW`ALbb=֚Ȥ*>9v/NBr#~mskao} MrA.TW8?e`QkY?)zS`% 2phIs+"Eq}ڣg}sVYڗe߽sNoXL>q/S;42,'{/82b(bId`vq#LA|=EW3yvc!X6,Y׭AdxBrDQ':)&R fbm7}bC3Յ>j2s=i|@f O -<PUOE $qN>r+27VF?VczZg/1VnDwNQbB5qvY{Sg1gŔߐ).t pْݟ8|$F;g5d~@Q:|_+bBܪh͘P­f!9Ytxۓ#$jl3-xsZ# z@!Fhv0wq&<|@k W94ʯ>8/M:U R=Mż\ziGRqjiصvgzt䛴vzR 7^+EAbSϘ.g1'k֚0l|E(I|{qҭ2m}k&{'wsua=kԾޛz5٣籇 bd}%{n܄v5Fu z z%^<|¦0^ltg/FgZk"PZlUS~?qȰ$NG&_cUqw)}{QwW?ޗN>  _>g(fzeå2F[S`fh2$* +pB dvmq > +.M fL zh^0_yk7K'0CvΒZ[䕆xPQ>Cp{{q0:7)GK|l؜ĠD' :ֵ{ͤ$&Bd&g{M[|^r`h^/%~61IUԯo/C9 +UwhR;/^Łsك +|9J|&Kl + +jVj\[`.wފJ!Jh{^F,;J3OzoU;neAR?|_;*Jjx &A\r)(}3ѿŁrVM4'#~UeZ!P9kW^a~ +cMny9LG]8v5p՞{sG,QV;-6HQG#C6cv=Ɏ)qQ7uy!Toyvx\3Iv-Zgq!i)[2_=o|b(E12UVsʥbL FøGBJQ!_3LUy{ ]N>h}~^z= 4v9)Obw (>ud,ATӴgBXt+T:m3ڪ2| +Q.+"r\Z7գ\e˄?>~?OOz<ڭ)#K{UE]bR<oڣrb8zgcЫS#\Ԍ- ѝE:P#0j cƸ`tcp#]CDscEY}BGo`u^dYE& +5P|p?;t囄H&0G: +] QzCACƹ#grR]sMk4qf얚]I:xMs 9p*8V3LB\S=yİ|'lqOc>m8928⧰\; S7|8qM 0{#$ɒ|R# NJ* ȱk06k֗3Nz\#6H³\LK䛁6~9iS\Hl@c'@ҼPj 3C$yk1|oyIcKZr̟xfC*̳AF0~ i_wC!Ś Sjr.h' +xY[[5EjAW9ɩrnGaG6/YkMQcl3YT4兏Xٵ@[4I?륊}Kw?84/3 /tço]ޝu8q*!qfG@,gw}ãi`rdB|ѹHN`[0hނ[X"Z4lrcC#Ɲo:covjtRmSm:Y-m]}3PC$ǔ]ΔL+|sf%r?ϘbVV\aK>pFzDe7.O DH'd-!1vu?Q|Ku ;xfuF퐸1X[q1]M訢|~VajYo?yk؈|r*G=qrFr19|\z-uӢ:Ru23ʳ3ve^l >*;&G+ja +2q~ bժNQ*FeDkH|vFo1vZu]Iϔ"!t +u=ԡVŏ'ܧO !4>J/Y + ﱇg\d?<9%I uT:?y^]yG>%GW|7s1+Nk}en5cF}ijvP'AFv; "y'{#9H:qq8g/)]zBs<. N?宨ch9Wؠ7)G5? HuVǩ)X'ƝLIփU ([;Աvҕ1c$V3`)IwpMclG_ UOC &"g@Y|V:Bk jm)q4-W{v]k!)Wl\1b1 +FH~;ﯜIB96OpPP8<+# e#3}Ng 'qq-ذak#1Ȼa0w`sM{Iɥ]PSLoUe`4o1xmYm&ut=}5߸2zkFsO8eϊ^TmJ Kא[-WE~xdy_Cp>S'o12d`-z,<(+ï: Q45qfRN%fUXBbeZPhf4" 2 8n:hYTY&: lcoyݭހ{sϯI}NFi{~=~ tHhMG>/X{)GQ}*?`Ez.  5L&]!>5LX#m%ѴٌGgЄ ۽`rIkXެi,j!i Q/i: FU튵Nz+q6`̆C]9N0œ6?tH0'$t=oם\M;w;0j?5 +{&4dmwn7G?¹sR޼M@#*L{ W0׵ +C<rmj}FUNC5_Ecmו38t'xqv8G]~0N|۱Mbӏl{eO>~mBk;h-]ɿJ\\63LQU#ӽ企Ֆޥi?kWU\ Ф%wgc_:^rh[~^èI0g $9])GQ8Ot$)P]Qw4@ƟWUafzI&5 2+T47pnyvSFLGo:0yy_wqz`Aa`7+˝nw8fMQR$ܘCU5aDפȴVW\GՊϔ)/- Nm弿 .uN,c;URr\VW%:M8ɴ$w& JOZ&;@-кR[YO+2Ҷ# 2P6mu?P W6ȥ +vNzAIx=亲MJj+)4dqt@s3  #3'ZA.kWV1kE=XD&i UYnnيr 4W /*iM߭ܘCi:njya;}f=M L-w~?3@ ZP錯`Su"HڋO9ncƬ՜G̜Rυ  Ȕ?74A1GjM1޶  2^@(es(  Ab:Z+3]"9  Ț~x1\\%VJDr-A1tz庲M`a;}=``__#*sѰ5׃  S 4c>A&|7wӳU:@:BwΌUz#k̝n7 Ȕ F1]R_H[%ٻ]L@ԊJ5}$Ϸuxy=ܠ="|9A{×bNAhP bAk|yaA'g^3%φrDpPk=ZF5 2A QL'q(Ɩ:Y\eOsfBpByDaķM3[}\WIjeF̉ &Km͚iR%JZժk, p1BH0ׄ0%!69܌o%iD0S5E7U(B9q9c?{\'0vaQ[wMҐ?@H'> ss|tdfqh^/cDs?ݪcuuz=IyHvL;'dy]!{&?@HiJ]nޮc];o\>'Os +۾s:zξw{rZ-C0DRMy'vw>͛(x-Y;ګ*螶Y\ͥg'0IQc[)PGg(SC:8xsJC;|6KEiʤG[oWj +?@+xp?f/j~ +tQ{2Rgi_t@ MY^[ Lij8Ul6:#|)Am NΌPEyGJQܚ`<\-5S+s+ZJKW1̄ѐzl $,V~e] 5N;tft`7k(,#i5~KK_:o @KY/=t>`aDΌ^{=&EE/֞zsykX LqN$FYMUѽĤ(Vw U0g!ir49edӎmiyqQַ~*^?o>!eԴgWV, Ґ?„s,>q1nRQ.4t:1jl!ٶsc{tKǼ}(7|G9"K^+C0EP9~b#WM?7z?P?jق8?/RXG6vB8O'eszo仲 S<5_?5_Cܠ3~ K8=fty,]Fsr^B٣`H 2t%=œJKW:{ʼc/nw]  b]1}uھo?]@#rz9XtFa.~uX (P+"A?@Dg{U*wg(Z!z>;L IG/K7F¤( ֞, $!,;xsښqǘ/ oh^U;Ό.]OPbt>@NMP +l ;ĿUr_ݱYf-]Fr7h~01Ӡrq]JTFsM Dʊ7~6yȸ*uR&Mݴi@ պC 9$&W&0\$vb;Ph(D!I]ij*L$;|?ك q'᥯~;tξoM?o0 \ig߽v,nƩ}E'JԔys A?1TtQ7罉vVho-c6s;!QWVz A?2<_ ;QZ;9>J\P?r<^. r$5=N_Z}*,|8ewl?ڄC{ 62ܳw~ۧ~>;?>d_#A?O\;vcޮxќeK+hgd.~0A?CXĚ8VPE)7{/@x q3?Us~KftQy˳?Y +͖#.!A +Y /Sr<鮫RC;!w~-Q;ivRCJҖzQ)`Ͱ?|>w-Myoۺ?|^ ҒUӸs@O}m?c<?e,OH4Osn~gC*WߝwsZ.@{(QSz9A:߯w[,+oIvҲ⭹al\Qi'} <020|>ev% W9O3c + _9 1j9F}OsZgUnkI[MS?h{(LJ6,(<+|Fqe֏|qg]̝֫J (6e+- |jO$-=C+oۺowNZzSq3y=kuY(h#^W%ȉ3%mɣ{\_!]W?7o1odߴٽ PdNUV ֭7 9lǽ(=TCmhՖR]a:Vw's{S#rc:02ql=6t9Rײ7]L5'˪=.U'{\s(M {s=7:T{׶֫ͽ uq͗|]Ҕ7yn0m?DЋ^1!AggTwӲt0ko|ٹ[˔&sgg9Y>1cݱ \gp@/}n,Pe]w]km¹u5QIO=@:e,:wJufZ{a{_v.m}q|8{ٛF\ۧ/D?v9sO#kmiɪOH4%vś>U%\'ڛ.,IKTv~ƙNYy]-o;[/ln{]=:ꏴ'c emޤ-dOR쎨ZR1 y]ǰ<`^yTn.SevRunj秬|l_"ǏN;YJW+sʬ?N߿⭋RVk3If_d?G}?&-//yӱ Uԅ7sJ#}w9xg^dk#,ꏂ$wC8{YeO +@s^c1Fi`gk>>H* |/'{/썲'${ak>!+H_jojس?>$s27ekBL/6^>^{o}U1éi ٻ[0{cEV֒]!ya,'6$s2ZkVG:j'|߿0ilXq?q9iGR}|=Nn3Ͻ{VŽZY?32殴GAb_y}Q?ޢGgֹ81&ͮDsUnk&,+LJ6‘LY]m͇enʜMQԅ\2ǧym?``B^@ z?@/?B^@ z?@/??ʌ1ZNŠ@evR=L6~@옆 +aD`*W/SqH]x*9K5/ [`{@m3j +endstream endobj 10 0 obj [/ICCBased 16 0 R] endobj 16 0 obj <>stream +HuTKtKKJI,t(݋4K%ҹH4J#Ғ(H +wqyy~3̙g<3Y9El +@ ]!O-@\+BVKK :OX~WCaiHKL0qY `5ck +X]x= 8 XĿ׽>.f#aPn D^{y8  dp H st:Y׬cxc IV?S!:_9[YbQP~+rA +ShHht^ '0߅™kYXY9Yqqpl'WzEE$%D>,^|t*K)%/`\ҫ:&D [7dplDa5|mb4,yy{e5 3⚅,t+whlA   m k +xYUH&%Ȥ +qO'Mz3KT@v[NUnn^\o]abTrtlmE]e~U+jאZ:zaqi5};CS[\_ۆwCaQ1;>L$Lz}4:%8M7l̎Χ/}XT^]X>\Ym[n!ycskkƶʷ;v{pIs0Xݯ3s󝋒&$WWW*)!$$%!e$cHNOAKIMEq ƕ;KLw@YX;ؚ8^+DspfKOTCPpJ%D=++O%$*8IZ\Z^UK_wL"dx]}>9=;s_G8/̹N!Gz[<=2|B}PQzlH0Wc(Een|Pds::5&89yFT"od䳔i/ZK^&gd:fgQl kJХeJ*+篍kj5U[ZUh0|em6]B@`PpH?QM1Msψ*iϛ.Z [JYZ)X-]R޸Ѻپw?@?5 ǖ'vNg +W3gLC#u!MMMEvAms˔FVNA̝GLwA̬,llؿsݛnͽ+!B²" 'R&k?3?4+:6oT\ұڿ6VʝoF?LT;:>::>:;eqvx^sawݥʕ'_EFO\DKLtAnFF)F|ԭ6\`@z?m+F;LwiAhy͖)Mgw~_ @ZH_XA,"F)%/*9aZ:Q,\B^_AU񡒀2 +*'[j o5[uR1uh`fm$1xJgBdrltlyyEe$feg-g#`dGbwj0TOC9; ܨݿxz6zx8IP=A!.aAxۑϊ}bG-ޒēx`G/Ԝq_O?0"۬խЮ˯ǰı²µŶȷ͸ӹۺ 0@RfzƏǦȾ *GcЀџҿ'LsٛFsM6+1MZ:{T?~ò~i~L}~cbA~Dad~ty~W~O>~\/~|~`Cx}%H}1X}%z}K} {N}׋<_~7A~-ψ||Dz|+E|[s|z} ^}wO@}-~ċ {Gu{Dz{]Ĭ{f{Zx|[]|ϕM?}R<}Ǝz]YzHħz|z={LNw{\|=>|v|ېI8z/r z;bz'sMzd6zɬqv{D[{0> |;|yyaIy?yazYvzݮ[{^=c{ФI{R*y߄yfUy`VyyuKzZi{ <{z%zȎ~+~}͇}W0}3}HtЄ}Zk}=~zɇ}!~Єd*s}Y<9wpSwuuVrUW؈|;,뇔{RsѲ;:8q)PCV:4.8Ȅ2񡂡?Up Vu9S c bփR.ՁNn U388A/ͬδz6߆өn1T\e7݀tXT)$̯̕6;eCʷˆ imw3SƀV7M +\lGNػځNāa5tNzlߴS<H6*-N}o2ن N%է>w֣A}⇤\fXMݘ2, KԐ3g°[} +0e6M _1 ? 1ӣǾI^I|B̯dܪwLe1$: rW] 1S{z|diL g0\ U{[G{!{ ޔ`{&yE{xbie{Jr|/c5}~ +~:f#MKx+Ca|uI~.yW ώәߎ%¡唘[w!^T`^H*- 5GȨ瘎=Π4rv_ҍRGf,ދ̋|,ƕ{ Ҙtٕ^1Fő,;',#h%T,Qۥ{[s:9󅼓&^!Փa@!" y +.Jl6mHju,bU6+s hܸd-ʥ}wi-sun=0Ľi-_*)U_ˈb$na+;ϧT;ppA7C4.*Iߥa8Mm.ACi7\j|fiԫ)]ޭjʄU]3(í whJch-4x7h׿*P0H됎L랇ڡuÂ,{Bz}8vggҲd[!XTZZ.vlAg +{;Sm`vؿ`~?ga. +3Ì{L^WYe4]L7ok!wI~Ira^=C#Zh`Wu}p)"z7ff&3$FJ8Ҷ5m +uR_,^VS&aR~PfLL_Dw*`\-9]q  TI6)>u6 D`e͢/xqY%9ʜ;åOd\˾P&eRz;].R<oΡ]P{?: r̨\ʻb Ҥ3|m s؟W9oZt]RnÅ\cW#+nI&gyAjsN06HiD'@J+a5V~cRI̫vwtUc[3+?F|l(iU^+O?Rs1Hqil$Wþh=(RE +1BvџnF/ BsGMY9>ܖ3ȗqI ڣ5V_1ȣβiJiX0WVH[8g_/ +n3 ` 38A.|f|ј0I6bv%& ;Y㿜҄#dD.).p'3J12K[Duɥ$s8IƊ.z^48e!R6}vcMiozo0'=~i,3:?-?oS,9w#ROa; ?pB +֞IO ݟe#}ԯN$\l?], y,>&Рq]yh0AqK)ĝBFҍcH:-h-ǟcf)K9T127]qEjL<>h;|U +dpG +ƫ`&!8al`83>.qɂnA9 +; `HByg KB*k㰗2fF=#OM eT? +mTm_OBۊV<ɆF('n3uG~Ȯ#7Њ9[١`Ns.P..콤 'KnpF\? B>-`NWOOWBlfxW^b-_x&*/(j_=߆󑊢zF`LdE:SNʔ@S 03|TOKokto}bFz$4-,.m'j*J|)J6BP ^3ewܫpX.*,07xPڳ:2XOT21|"7=0ߴy}ĸB)H[Fs V+̯+Y(I(x&9JAI'tXmyG=X[8TK)2<TSRvxlȓGO|g/{>4/gRFȶ&A52 uЯ*B幃AuFǞѧuD)B,*?n` 'qQIzK֗4{B_g68#ʉ2.A$69!̒ub1&D3Qx" >ɏnνxVG&TۨÓ)sxd-5KxߣD&1±jdGjJ|J{Z ޲f6/vTp̄ub PmBU#gBg˷)-*E +ar>>Ƶrn[ɭF-IByѸP=ĶKUC wG D}"vN.p]]Q8uY{#qCv}sax_oyiNr( d8aw2CQ}V8UWO\g \yk@dcZt9$u +p-1z(=f) +vě92 w u煼ת#{P6+Dq3HIi%BCb!kc5&U ):X$܎[b2*@PkcӘdoTB_L1Uwi")=2#pI9,RO>T@>;bnDPuCfk^^\G~ oLRcHqܮ=-8^5Ońy*9:-\g8:T<?*C;[yX+I;lRL߭$DvYTQ6DyVmfy%/sIsmXP1Lռȭvow)QBb_LVwupeėO*|+](uHװ4WU.{ 4\m.QwR~MAiRz+%BKz?'{ k҉aa{H]sX}da~3_auQz VM\ĵv5I0LM)DŽp1:5,&4 %!$}ocޤA]R^xT◬M&/B:DwA24?cd&g]5b4a?iǐ Ĉ.OA 6vfvsd(5yTH/P=(a;zUs bWxDa)Eʼ $sgPJreY3w`cFo0|U[j5k.5J&eTor È´}I lpjC8c5J=g%Uo|L58E" +ِ[Ak]J͆VBM"{NrQihЦ@Y?6^߫ZWٯ]ذc؋hKSLj:>O ɲ.ݰQ{5mm<ٷ?^v"}ъw9O&vX7km[ ,70nΒ7|eP\I;-wgFN cIP#qWI ;NٶA)H~7i thl~~dzY Cx2>*c&mb{9f1X*L #> + V@g蒼]7n249=MK% ;,F\j 1klZi؊ΐ.|Q9а$_.!;̿lE,ɥDi}D3^a`Y5g{J=mɳy3CM'jM-iЦm n5? SJE+U~ ;q.tXd~~p*QeS%.Ћ"ưBsZ6-6[\d;^z4`;64藸ͱw;|+&AfLU3XTm)lF'l VɺgcGObbɜ9;v \CL, >B?KGCe"z -@EHILp<5'҉$>8#gL2m c1 c Fw)P+rkC qp/u8#!*g°Pa`vu@oH`"Ž:z_Q<,D>'ӅWP .`xW3|!6 +5 El[",0 e[Oz0~lUO+&xkPc|u$k.?{Qp""kr6isVa=~@W_ +.<7 +2#h?c~m'rE_xs6aG+K 14L^kUp^^_mS^dШ'>}5$:τ!E[bJx&n t(m;ZsF5uqX.ՂBqKP *l%{ٓ{'f';,TT,bhUq2Z3;}T9vwRR;GD + K*/@hUv$j!@ vyבm,W|-͢ ^ ~D_􆭍"ĉ#c禘*X/Ϝe>|XH;:)d9gƖ4aBQ4Ew,C +ۯBU#>SV$L-5gV ϯ*B#} npþtdU$Db&$^\^&Z"/˺+-}%Z:}9AYu rTlP0"~! ͚*@5K?߫Z-P=j>܈[O?)a5 +?WUsy5^(ge${Cm> "Gգ+$踿ϫ& Xw8?g,'ō="/xNM)'EFqrf CįQ9ZY$r!6m)4 V9kJ$# FьX٥Cp[ģ)CS;rFP#ImKGɺzj>>X9,ZL-jIbkȉ8˚?vtxPIO}_ay@:|Ve6ubd/e3<֭ztea'cLaM +lz&,f^_!?l2x2Xyń3D)\?ye ~4O+9$  +EVDTSؓ7X?MM!ԼuOtP Cbt;iްa@gW#@4c9.Do z2>M5i~u0 qswQ9ǸLt삟Mz)>kɝI;io"U)]$YL >$$T:gUo$UK,C`sCMAJMÄKC(g]ٮ9sUG0?L5QM%0Ol5&`Ƒ1,x'{k+mY}-Js#\d:i/NK\8HstQ#-ND).s*Zymnf\1l{(E=VGW9s:?wǟQZsC6A1ƃ6K@8OUY^`7j6@9?,yt4&}"T- +\Y&kVx녣391ٵqQ=beMq\`/nņ|2͌JkzDmͫIR4\~5NlօKɁZ]TC3l̅D3jSS)tWw$IX[wV +WTUw^PeUhWE^ؓ~Wchs sIg`wgs (5mr] B`7JfAaA3ƓG?{O[ ?xj/Z*7exXz Ά})C?`KcMՌ&)Y5J]q':]$؞]Yv x(ıH1eU>_0b?*񸨎b¤،D;Wxm]|N7U13*;.=>SÜj)CM>.eI1/QvН6Tkk+Ɯn\\FFV#Xde&~WE7"bju^I@j@bQ Wk8w_D ^z xZKA _`T}] +x}ЁM0S,rV+ KO&ƈ`;E{irf0F] w86f fm_8c3V<)r1p +hs|p!QP'Ղʛ2rӤej4Y r, r?4! Uq]f(*&umM+;1 +-c8CjL=L1TDJ7>)BH*cHY}~xI,{7WjWާʇhg_YovMKiN> QRǧ}AQj^G syJG"?txt,L>֍p_>Po$^<%}KDS4 +*S<ܖyd;éIJ~JMn>ȸcI6uɖژ䩊i77_5W2' 9t^}/8%wd0k)ͦF9kih3ShPBULzs'0$Y/L3ol|f ɪ\AW#siS-O^I+36xas @M +A hm45V-' ѵ1S+ ~*%~k˝ʉl * +lك=3_2~OgPs +Ccd[aے{<ХjA {! ߲ۓ;O'9+wEHE&JV?fiӺ j05瀶bhWZxo=ƺ 0zhK5mov (YOut;e=R*yMVn,$v:QڳE.yVl;svn,Wi.[@34SD_!MF>J柣ND @$Y~-CMu (+lBpБ^#$~2è /@̣6 3nh +;۪.3Fq3\َvZnZ"/vNFNJ2V{#ΚVse_쑮Ta8C¢!Η>FL\M{5eH~7;F AB?VY=۩Q i9J.sӿc%FVbdեiL`a)kD=W \ne>NX7Ƒ†2IYf-to7/~Uas[`W*v3_`~:kjR("E +* +e)DDIss,f_n6":hmh+]AqñQqSa9{~8|~bh6GZĠםN\h+(E30~kTMGβ1:zka'LG2>,gt X&@?e% +=@Ihs)HUOeX^m7R7~,, \jJԌfͬ8!*]JR:WR]Mɚ PZ;JN.8ɦ,[r*Α]MM"waX)Lbjd`>:?|:?u>^G$fa. +ʥ_S%ED8 J=ĕK{6r zGG Ui<Kg"^ q +I6vPWy^,uc/5@:ǹ+[N+li{P#^yv,ñ-NѳH⺣<֡gxV</nb6󴳜Ρ +nhB˾PoT(W##ĉTwZU} w-vT-9O᭺HIz) z9R'dI5aZGS˟agW=.P1ٜ y?2X)r4VaGXBe`9Q1͚@85$W?D}z2* +pt +;Br\ܕ'> -vCNeʔL-ʌqKHr 7I d<BgNelB^փRγF2AqCR&t7߄{" D9u)Cw1t}?"'[7o̩~1{>Ru* ʖdClutqf2[l~{S4>J$.nQnlP#x])By`r+wLH?VD:|iUG~ժ+&+Rb gP>}WԹkQǖ]WSkqwZ +DQdVd24KGMvU35KJ~4&jwJ*y;X߉˔O@5hw)񘴕o-9E:_̂o&6#V(ѽS-te$ פp}4%4mrnzhe4KX*KÃ29ʩ~'Ǥl|O5ÍB +;^j㛑Q`exH;J\*`l˴Khk +&tF|(8VǡܷR:ϳoG*UjSKknRgl ޅ-6&Nŗ7O4rGmO[du_TvY{ ̏Iy\aRKy&P7ݪJ)l"W5{K S_j0WSW;wixF1^lО伴^'1b%OAXhq)L7j}=9PX=n`ɗKX#CùA *7{ jWܴTByufכd=Af]F=_u*`q+_i݋\^`BaE|S&%Z a8+QgQ[IK-jIKr2Tcju=A ʧQ"7{ٮם*X|,Yzѽ}ƈf:jCo[>]x^hlhNrϳEDkcCǪ ת9c Ht<)}z!hE~DBӳ2S͆i{;ouIp??砃46ٺ^"1R<-65sjpCSjqi6dzھİ紈 41.$5EG9:=ob쾄 v#[xﯦAF+T(C@RQF772I$^a$Eq>.AEbiO0]ТK5ΫPÛG ZdJ*$d ^}E*֤>?Ƅ$dO _tl%$^7[KSECqz"$]*B]}W zT[Rk"n]EUYvFUW\B6-RB^Me2B4/wͺh4Ek5˖<1U[tD>Q!.kR涧7uJc>c +l/i^3;iڐ0sĀZnS +qW7Np:([568ViAFޜ~h9Pldüj2dO ++61--1Ewv =JCHW34܏&x8,&#Rc3Dvz6RSyu_N/nmكvT֥Y˼?RFװKzn9Q4gC^5l`P\ܲG&ޫ` 9PҞٲXr6 +V4,{a؄\tcY`]lǿԾar鴯؏=b!&Yb ^[\aYt$w +[R)i[{$7f"o Xp +zBz'hO|Ō4ǐ|-j +:}̴a%Tv5Y9QK d0 ?$ćH|#uD3 phrd@,@XmVKY@ou([8#!OM~.7SoJn%OG" +Ü3N|/'O-R_1Vh&׺ NPz8de 勊ZTH;XQ6}+'h_|ȋCcuHjBA,NOS{3 L`]1> A rxӴ*E^.ؐ`Q5 v{`=W6뼟\9avGOXc& v1w~0W:ʎ~f: 0/˵%m KRKAcR% P#CSߥfmD5oEx17B0<&Yd8"1wܡ5 TaaJ3p57A>+yIMcu Zd?Bk1x-rsV9sH6p]DGgO| y5S$aE`$Ls +[Ym ~u8p`6*I ߕ`S88sn9O3nXOE /7f^lbN[PBFO.9Z_.5>F S̉R'}ΪѬ`_dX|{dHXԾ3QlZe7PRqشO5OkZrx5u`aǂ:*`T), +DPQʮdߓJRk=H+ +*#u)h) )B6s9߹瞏HZGzGT"93hDͺ sr|b4y $TK "$I~$v(B#].qi?CN ~ޱ|ܷLcOnT~vxj̦5<.f\K<2p:CpSy,66>|zC +E +T)f/:X1}J+>_~Q;^ㆪvs&۸>.k7yZS:˩㜍rݖۜaKa!l.g57Kv0!;ڗfe %]"XT J3aժlwVj=v姠αe=bI/gH& :g,(y 27>aba88fVVqɌT0NɉB`( _"fo! t}Wg_0}HX 9,Qx=~Jٹx>ӱe9M2mFS)Vk-eZFF٥btg0O?Dǐ%7eyښ6WSCyeUS}l`a8i g"1лJ"|PKڝc,$+&PvꖴGBoj_t4I vqf熚(eC!b׼^SbYi1¨;2W`/7uh?4 +!z@#(T 6 ^!R S#>E/Sq9z_ /G%ӈ0C9[ۼ@(٩P ,}XTOkpQȫUG6 x2e,> -?ϭQެYz/T5FL^`tީ3\#̬D:,vw[mDW)TBZ`0Ֆ`3tBQ˟kks41y `\޸cV#z`XHhwA0چFTyqӵܫ*F˪%*/>9 +gS'"b'zL=N)cs*bR)W<#S 癛)K +&L\9WtW!Y17i*%wJ_ 閥nWJ!p-0T`:K6B+SzlL,~J#ZLHBEe߈Eq1 +ڸTD}bB;*OTCnՍl$OYQ0mz7o9NŻ|hDV[Ve֩b7YZÖHl~I)ܻJ5oOݑ%(,hZGҼmRd!/NEWutV57z;jjs^^lDǾ0-a_aL؁w44簍b^ppi&nX uƻ-݂ -cY4_g ?jGIfH %J҂[%ϩC6OzvWzoZtA$?z;ؼFT2/+0@@S<@>0bSuqw;j4S'/4sEթ(P[V^5ƊHkg/ۄw 0*֭ ajyB5TC J(_F4!m, RN ?S9 :״OfOV"յڇ1,V)S@._ +#Q`K|ͨ%cj/&\: [Ft^Z"q٤Jm뙊jMarח`VCg +w"~>< 8i}XT8dzQVY<p%HG/Û`rq;Nm~Ms\/Zh:(MXа^F.꜋.Ys}5`a((X0T+JS 4&~|iB!! !)$)ʰ WFY]E븎3x,˽}|dc +|i-0Ws +Q_GpRjy0׿tjT̎ԍD1څڍ›N:ka? 7ek_%]a;זF=9-b= &Mm0-vD'^j+/5(er^+EL F1$1KWE|fOFMKm::1`ڥfXЩM*i9 +l?+Lw?-Nx͈wɳ\C0瑃f sM;iđ`$O0z*RٹB9@"k5v~.lB?ug]ed8JAj͹um.DO^^v:y;ske+,L¶vŝҼخd_5Z;q#k> MU\J{l*͟ґ3Doy"UDcu#H)BPit/ v`_Sʝ{e5mpPpy=-2[m+v6*.WۿSǔ] +^DMk,2.#ɲ\!{^I4Ԉ.~çlDcBU\b"c jvJG|H`_2rHѥ tHHBaG :Bf{'9 +[jaЧe +&hz6Fdy?>gۑx&l$^:^nx-'-]O 5@S Uڏy]Tu _,zWPT|BJ,ɕ}`8ߴy?p7gˢu\JO(_vOUue4+Qbi?A.jCxyRJ駥Pt㸲rTfdd$ֺFR>PaL'v2M*׵T]`W*cD*hAe#"ɆKO9JKL2J( KgK3jԉfZnL5oM(_>FOӹGi}<@w#Ndhoo4Y ̾Fٸ2YAz$W֜5Copli\ 32l;a<;S?B>zprjsm1tZc̥{s/J{c*#3ހfϡneh->Bc9SJ"չO8'8ހ `yHϤu-*` x[c')Oy\x!QS9q*;$;d'=NY ,|ܶ34qT=ka%hs䬺UX7Fl[ o1apuxf9QGk4;e +˸7荇5xB:yZdͫ,`2?_a[0~9iY Fs3g Ë9u<,yx87 1Ja,O@/gO㔛94 |.]16'^@1'p:XtwL,jVQv@wl{έ̱\?R^UV\GI+9D03oyd[R<""" +.2}"!<4tH~(-r25DH@l"K濣,/S}"+~wF}V dRz,:w&?C~FqJ}JݢJirjzEgU#p]ZF%+[PjewVjlW7wR/*C%%jGx @EFH)&0_Օ|Xu +DRNXA\0JSH307͛73 CWc+U#r# aQOL4Eљ?s~{sIy?y>ҒLָKd-ޣJ1v*fH 6hz+~BO:IQqZUՍP[UD#BM >$ z|?^!J0W8N WzXfщ@'h< +%sdR۔e[$z,Z2H5[&Ht L UO 췯+52j&P6uRɮ! +a+rk!o4 `ܗP)f%VQTF(Z]s,TR|O)O?ho# ]6yл)OU,F٠E})gsٴGyҘp/kw~˖I'Y;TdgYU'I8@F* 8 $I+A2((+y8OϋWȗE {բbW"@}@C׌teYgvֈHofE`eagbN_4!/e%O;mhtWv6[iyFy4ʔat V] au #QYm3rM/q{~tjD 7fiɷ  . =[n`4qShBrx_5wԐ %nQ~x'G[ `+qb]Q2Ըi=UGn~ڋJ(Aݪd E7Kz +M]!} jnh-Cզ_魺a٭Dfrj6$-4nUZF)Zpux'@]U/ٳۿ3Ug`iU}ڰULWu+SU[;uXJPvOŀ{$KF,qQruH.}imfZh~atMBb0*iWC䶧jZmn[nKfi c+.&oV.&ʭ{5_s9dmIA. *s5: 1Ů m!|fl'6#N +Z>\oMkCZ8)*bEE@(27{I" $!0a=+vUZŁ`-xEJUǺ +~~7TSsV6i1=2J眆Jh@ Uu;7!0 +߽\醮%-;=.e/T7D$v{.ʫ|ZѮmcDֲ+-Cu_{>1H1]"D^nR ٺ:E3[h9 7TJOW+3 vœLimc @6'[c`Ǧ8v!bR{1_ӵuoPE2\@;4"mO m{ ߺE1dA}C=WB}[3']\PJG5VmnYG Xyahd'J[U~ vWۅWo]WnGnR9H7ѨAu 1vZm]lUrTVA +sj6lhm,My4A*0vJR? Ĵ>2C!*#q0MJ!:ŏCR|dFa?2݂ch3dBzSIt?%LmF[AxYGҏ0m;GY1űh%[sጒ@9 q_8G>r Wn)jodEzC.qJviN&If8bg + v|sd%:uTf&L0~p.(RU +; _)w%$/ t# +~#u`u[w.qsY_-*'̳ɩk/)2* i9$7fUzflc9}],툏WYCIkS-ty7>T! 26Kݲ m&cӣh' ..+upC6&@j5tdP0=I˂Ė +C{޶$tR:(ϭuOR4$=jluq1?פ9Si|cqF!_z^SK}`d%DT wV>;<'V=(5H%jWMV#9YD2֓p~~J }D]gNSsjJmn->,vg&SLl#>^i8ʞ%4'RJDhRN0hBA0(r0K+aMY|"EGE_R^v4/?m[˨yN`K/5[71[Gؒ' '铯RGhqꭁ]>iIX +5'\GB ćd^ux+[^%e ֪pxE + 6%!Itި@Ҿ#% :*h$r7שׁ55׈Ց'I+6*ЮwȰ%U#zD+Jt BaUؕ 6}uOr7dP Cu}FEua7RV"KST20 EN{^lkƕ$vW(,F7b ˢÞOy<"_).kh[n 9W?gڈ7yș*ӼuA@ OpIRrP($e[iVYR +n#(aFq&mq3%\g?%ӆM5XD3b$ʁW ƿ5&͔D4®KcᏊ . +1Zo +^`~¿`6z q aXǰ)Ӽ܄'84 n"Db.yC<K d},{*h +ڸh>wMv^ c8Iƻ(~j? +eoyl/Dl5Żרpy1ܣܵ^004{ .%CA22dWuQ>okL<5.ſȠiffh7S-|^TjX[wCY*sG^1Ve֗+˃L3 /2y{+.;CtJ } ->٫y6q< WxA_PZ? Q y1>yK\.!OqM +0Cl];Sk)=RZ@[ɷ5JBeǐ$Ni"0 -úR4H~9.☫|Dϸah-)r~"eoMK%4 _7"‘e +QD~0T.>"x*O>酧.Ey+HVy55RWsEk*PxEGB;(J X(8hiqmh^ 0`}_APWDLZ‹]<4zG֦`oyZR|u^gCF#nr)Va5ƪw9njyIt +xI1bIy>}-AگOShKFx6xqqQ +3SU\ka椚̩Di~ ?{>J3mtߐZt]YNju]ɒQYlZZsNѴѷW>Sݥ0Bj+7q҄fU7m :8^;#eտ+*,_CY3MSU*LX.jQȖg_IWJ5a"9R'C\y׳qH)VU-Z.\+Ѥ/aen/|F[?SPkr" +^Y>VH9 &yaIxQfd}+] +U.o.=q-y][viRgk*`/pLBu+A@[)&PYQ?im/K,Y*gu(i2`؀V"fJSs=RU@7+>dْsmY)w=U?ο3D qjv83׽} 1r@vy:{Eͩԡ.޸,珈~CH{ksv_l毁@"lOR."0Fl]]C˧Mfi nq˶Q{56ef e l[IuY_(i&;to 5kZ/ jjp~Ch⨿䦿iRs!G-֠5 +&wa7WAƫXUr8+}E)oVӃIÌ}qZlh<gw +A?=$6-ޡ|,)!<*ǘ*z!8߀ϸuPpD|Ŝe=sm4'ҢؽYaPOZ(vj?VGgxI=V-̹uMCJH_-C]B~2A\8*E8PTΔTo 9/whaߣby\'F,Ռo%wU/ժnM*T Ƌ{5NJԢT9L;y _fXD\uַA:x")V%V/*]1# )ԋ@X"SVӅ4u.f?Uչk%Nj;c~?]Pۺ˄WҌ=V듍1 +E ֻqd{q׉; +NYHdfttc #&vPtQjd1o ­R)ʽ@}<7 &8wyybH04͂@>o` ~M`Oi#T2"-!NSn\ z$SC%Q%;OzcT)!M.wf.Po1U=Bl1F#F0HD\u̞rڜ*ujQO5u8E$7:"І(UuANgulWYE*Z"cT\kTxlx)$8(YBIY`[}.Bb T$=U8Oŧ yP-x$]0_ +j(sOH|/=wKR` ptl>f*ӡuU<=Ts(&zpKA?sLo`N0Mq+~*m-~F7^5惬H]${|-Ҷ9Y&=X'Vu+^ϖEm +Y/0X cAdPc_X VRx6b|C6^FeC]o-F?f7Q3V>͝yFsy]ݯMF͊k^NնI#FZ.7ƆQfeϫCJn;AjB JFw +mԗ6t(I5beElXQ͌ i,)6QS 1zJezVBf ۹ʹ/ HQ89SnE%o-4NJ``,)~utyQN]vحp+e"xN6y*,7$'x\CQL[8.d@}CɏE)1D?@晹b$?7 + YM N| _Td'wa}0Z<9|3閗3~o=Y>l0Wb=P1jmE XR[louv:.C=;.a.BřS[nWJ3ǟN1='\Xr8۲:KXj6e g΀ap%z"K1.c1ɇzɭGTRiVBe-)K@iͬ!u@_`&2q up%P +SЧ|NWP !o-t_ nyV|ؤ賐e`HʏE=>\Tǀ|cҎkIST!%Gu,%[IR'+#T}m3\/df)`n2#\M(CQd6flqGv첵).Z&wITe{JQܕQE\m`p`Ҵ\z[v7OVo9ݜQ}$SSFMWdnyuя: *o[3 O FRJ0ոl+L+&oE+d- +@?^fEkoo\fyJ8zΰXmi  -Nw}OYpz&@>gݪHc. ]7Mz#fe"g\a@\qyºJc\3ܔ r'WQVE D|PLs\h_h#9Z-TdL>˼!WS/bniA3.1Fx@Ǡ3UNN^nPOZdtvWO&-8ךshveSȉ`wPU_cař=շ}m`<<$+UV66do88{ηzkG}ڻ<<7\jvg!5M!w&GmpfSgO3x? +wZsLRq/~lK]QV:om<Q' R]AMXyu ^ȩ $}! 9LHaH8hʡrTtD-*fY]]wuu[bgg޼ߛ"ȹ I7HR7HBHudt *Ჲ=eJtj| #TI/W?{ΝO^'`v'$^E=7ITF2˵7-^'Z"[x ;[U7,QyWrr9E6cy'I gIRm2ZQ +{0K,^H/>>G@l`T=FZnZH ѳ$m¯鵩KA3D;w7ŏw^J<`i$M_x8wU-,/h!pbP1|*k _U;N45jX_:]$ %ͫX+é Miwzz{7`fOE5FohX}fL}k%Jq_b_A54WK'h?:lTHmm. m&"X7rV7l̨b]r+ OpK[{0EuwrfӵFajCCPktMݻVw[FR(Y-VE8 P?)p>͛5 #TtF%3 qhk ;`LVOpZۓ. j&\Cʡ <*g!r)J;ȁ&xK0N\B&Գ$bԍ7fpt(0H23ӲG1d?ź +bVֆ|\[w+tjj?b7hwJCmm#b.^VBDRb8E]4J 7LGc.Xd/a&ڎ @顢zQuֈ4Tqi˽èb˕ 43~,ymoθ[0 +l} TCuLBt 2ZW>Eh@+[Řy0= +sU"r];û](̏{e E=ma^2'FKv~.Оm0Oj(esߺ Pk*!3IBЦs4{^|{6k\* }XYǠD=A %$hǹWǂORV UBꯪr+Ca6 Kԣe :Zڿu6&?W&k).]%],lb7MX][H"}WL)RIrfr?AƁY&I~_IB${XlZXE&|w#؆`_vߢfu3fm89?9 +̟NՎ`jz1*.@爎܋`oْJ_+-4α6@/DWEjE}HRDl;Y+ z/1Dѓ(z)oι&;.4aZ#gsbZ+XWi;<~n"( M'b6!G lP<^\nM8--aG+dyXP^s:0q \p3bWu.,R&rm#қs)lej(^ ,=/FV6fj;ex%Dk%!FW@ao2QTvs 5h0B{UHiGCOzL'pbIq+'_1Lv +QA%$[H~}{1fKٲ:HmWS +ëd}2w7 j< O7i2G;SWݒ!@YsZ~*PƐ6xQܡ/9i7cGHVf3R>K2jZxH"Z")vHD} @} YJ64T(P_(*C]miSJqOZgA(ny8}wν37;?߇*x"D6HaeZ +5K e +tE=H\ƒW8 72ym]Ly 1N<8͍@:> >6pӹ$.7$C$pA)hJewT*FmKg-lm*{{v\ܲsJa>3_*ݑہ>V5|WG_>RR_YL!RFjz S5fځO2< `}I\:XiZkRH*4[(xX$u|I9̺TkVzl_׼gC%*wXR nY)N.9+wZ[E9ľWJ%wp`Nj[.b|JOsdW,R~#* ĽyFdwCp*L(8OelL˞)A vfFʹ.Knd~A򥾺]Di(i]YʯJߟ?>w[侾7KK6w"!eDp5V* 3VEa{:KoEDcɾJ#oOU44lTjFk,>{S?ýSk>Su=|j}T +SU.nk.mcŮ)RxbT<TV*yÙ<+`RC;S^0-itp<ȗ2IZ_0ȡVVKHWol9=fd jb%}DCy{sI*{ZL1r`n}+D_*Uz3}i779_kjxL+u ;FxL.mmQ`sKzK#>&ޗxiBV^\s3_XX_رC+ҭj|S kϽ|j|[X +ΆBL.?\DCqߢ7nO(M&JOiݖw0IJLM,NCOYPoQRSTUVX Y#Z:[Q\f]x^_`abcdfgh#i3jBkRl^mgnqozpqrstuvwxyz{|}~ˀɁǂф{pdXL@3& ֜ȝ|jWE3 תū}kYG6$ڷȸ~kYG5"ŵƣǑ~lYD.оѧҐyaI1ڲۘ}bG,{W3qHvU3sIa)\ Z, +     !"#$%&'()*+,-./0123456789:;~<|=|>|?}@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`acdeefghijklmnopqrstuvwxyz{|z}o~dXMA5)ۈʉq`N=, +ٖɗmZH6%ؤʥwog`ZTOLIFEDEFHJNRW]cjr{ĄŊƐǖȝɥʭ˶̿*7DQ^kyކߔ ,8CNYcjnoldVD/h 2 +R e r xzzzyuph^RE7)4=@?:4 ,!#"#$$%&'()*+,-./|0p1d2Y3M4A566+7!89 ::;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{||}v~oiaZQH>5+! ؎͏Ðxpjc^YURPOOPRUY_fnx̰߱ 8Ql»!Ceª9^ɂʦ2TtҔӲ6Lat݇ޘߧoX\[VL=*b/fMq T + p_L7! }tfUA, !"#$%z&d'N(9)%**+,-./01y2g3U4D526"7889:;<=>?@}AoBbCUDIE~% ہ‚rW; ϊ}bG-ޒēx`G/Ԝq_O?0"۬խЮ˯ǰı²µŶȷ͸ӹۺ 0@RfzƏǦȾ *GcЀџҿ'LsٛFsM6+1MZ:{OX͙~ʹ~y~eL~j~Qc=9~|4~cl@~]̳~nf~C~لOiZ/gP8v}6q}0}>ϲ:}i^},~ ׉_LpK-~~,*~&E()D9vyowy=TS3wI!D)J%OBvwN64;>FVWm +S^Di*bPkpة?%"1#!ϼK`L<n-e2*+) X䥂C@v2l Q?(=0q MzǃIz7MEY; Y@K (-\U&>rI^2IMe;Ya"VN,S;o_%sD;fƎ.R?l ;0Dq>8zDKG)3o+&<4@n͗0EO94#ҐnW9 b_7}B2yːv/ąJH삻Ȧp$ȫވy;Æǘfo虔F¨LsI,KhW2!AjHE^τ _wdlXggΩr!jU)[%B\DCfp <_\?k,.wȲirJRݐ=>0+cvZ{HllLVAc۠ ^{6oCҏSمbȏ:sz 7jP@Q;[wg|z30Uq`!P-~|X3+z2lIђ:_p-FOJ*Yr(".O'qäfrCRJ'dc~h!€?`}WzBd;hѲGϲmT SAij9< +ߨ%@`8xLTqė=,Mk $hJdx_r̰gʱhtG,KytomVK0X?R=Џ ]ٛa`sʠ7g&Grŀ?>r&z`b>&z%sxbw&{~څ]"WR%c"zD zA rs!֝=jcf]rmANJl$ے#ؑ >wTfGFF699<׵.'SZ*˺#-Jl.ZZx%m*| o 2ӝ_TWK4eRsu33'jRFBWl| +Fgml0L1, y+Hu2f;[T0BE{:qntoT]okI, + LgV_R:Kϋ0dP?= vE̷փ(M4m\Tk׉o,H=Zw/EI-LQ[ 8F/g֖'$?[u~fghXjݚ- VImKՀ,%ibQ*e97WKMYiHtXTBUDw-49#iԗ/r]hGވ/ + +lD2 h‘%TTT*Fdw">GY?"[f r5ʊ4`TAo4H5rWS8Xy;$Yr'q vUPV&4m/5LJE:S7Hvy.. kPXAl` +,e: E$@BKr.!{A$A,CY[EA;| TJkU>41aƜdcT.Us R&BchR) + Pd;ʟHbl?1;_:i^mMh9Ӝ+,x+(‡j3=P6u>a}&b (0=.À<2&m%u9_~zL!S`(6͟>թVlW䨸m5ypg!2< PR%wC>ubvbF.0UK$K;؂P,!rA5%\v" +[2gwdxJ:_'Eښ_+^Cژ I! v,V72UJLNITUKɎIy/R+=+(֨v6!M @PB%R--3|4-)#ͯ w.ܘ<;b#;*>$eG +>3"و~AZ$xOUx f𜓜x;٥Q h X(Zx=`dš 8b†id, ϐ!enZ +b /޲І2P0~ +1baktT ?g)˧9 С`.ޓ`>'4\DRdPaxԗ?i|9,t Ĵq]"m-9OD'Ex>#Bz6Nk%tm6BDzVQGq,2O: y{iHcy[]vaZT5 ȨR 345N@qG!fYXr{3^M7HX1ey87ҙ;NP9tn/D=}*I:2s̋%G{7abTBm6ۺ4JZmI׶Fהz\FD*rEyք ̣V-8ˉi#7XmZLW:2 +$Iⷱd`U+z3 8"}Y\E^\Qܵ)<&uZ!FM)V"ڟ}&à/ ď 5 O546PW눤0 fGlEbdc 'ƪrӬ[{K("M/y%0=zFBx}{w6{Y50%,40R}ԓvTp>K@fR$7HU( /10f<,1BS>٨RI3#&&pa5j19#yTH9cI[էjU̟~? +7NzM`k|-kqJ}(Ҙ2SaӼGi ; b:`uǤayU}T 2Ftm̔%OpuDU0m~L-_:qWg0~huw-] NVrP =<]x;Y1iw@8,n\(zqb !$zB&5dn61Q& & CuЎy#c%$7]w'z\0Lk{8 ;fGS Fx¬P~Km%t3MccM(bCB$ _ J,@՜ %ӸZ;.6B)PT~~:_tHNITScΤ5_3bO6-[o 7$cn:zNqnE2~7\NT' "[fTT^2F&+c5r~ԕ(jl 48mWDC]X#<n_ T 45 C0 V~ m&AGA7w@w;Q8Q ?d9#1yʕq_eS]y|d*&6Q30J(WG>HN vAg+[o:y1ډGmUV'pJ{"M@3X|*oƙޞ%sfJ<ߔ[-0R'G i++qNPF\&XT~ykPx>–~u2LX'P MOW rة Z?qU\+w>-q}y/sRQQJ@737Ka[t̷E8X,Tp!PVK$`Κ׵bu~*LlBz-f{i8DbMp/ŲF_<`w[Uq. Y!'i7L' Rz$v]c-ީ%HY~ٕ 鞀ws{)Wa˹ԑ`{[z ϡZ& z +- U@uBP.8jz B{GtϤ1ޕq# ^o2N*`DZm錞c@QY@Oy`ŕ^ )H??s %J@f-H%{#}řPKn@u5w:=YX9(5#p 9#Av(~-"]Qb'䠡ya +'£ +vO@%7_*Z-r*~z Ց4!wBpG-q.a+c"wmqk=WfB +k^0>npu5㞃= m]0o-1:ǒ~%ui;pVO/a3;0oKܼL6Ed@ZU%{ ^ ͰyOVNHLmu?uMBEQ1\IُOui@L7Nk\dd[i|lRܰ3"rW^  +19~(VZQjsfb5~Nl, $LAE \Yv3k"*Ie.gj4uDk"*T~~g^ ~<|1cPx7kF84K(/AI\%HG;'6`kK +ZJAFqKq$5GT#.a;1 p't.t-SSUn;QY(sў*M8= +BHZ# GcDS{d',Utl=,}*vcr+](_1rØ@?A[KDlv'”o>=ԏ[?Q ôn!ܘeoiB]u3PzP'ߧ%44Qw L7@?;gSVjgohop7syR\7V%xL| 3n|2Q|-GotuV֘Gk}fd'̐yQ/;^+b#&~ي2(ɚpTֆ)$Dru:5zj,|~0T\~>*,6Y +]7E9!7;au*8Y?Ң#WfiA~\mB\$OwDhE16:_JqBR%*X3 !O:`Iok2+}Y'1%Y GPMJ{rK w_ L&N NyA'ճmﺾo4gz"v;L je %Ɯ{NS6U'*@djNcvo^=Bi 795l€Aⶫ627ICkyV_}B.I=YR2U^c~o\Ƙa3Ƹ2@eU*Tlmcӱ~ xnNU)o`Iχa]PFŚVTC&ϣ࿋Y=d]/..FBXs+$=}buM>RWm6Ŗ6ᢐFX 5x{v*j;zv<_~AVUJϐ^IjQxシuQo=lK_ՑEkZ\4sqU7vOa J?Q)4C^\k[{3y~M|J'g4Ay,$0( jHl:Q"V҉1X&e s)MZ(W |Ϲ\88&tcpҔa͔ CC GU$^fb|8u̸&A֍9ke7;㥦koAvՏ0o5y'M3q"y$[Y@SgÓ=ݎP1)L \!B;U!)/C$N$A³ueuU},3Y'/Jc .8_[ON-<"NawGm_+yj~P]ſ^\y X,r-|㒒ܳ<L^T},^eDR,nkqց%|r,!gJx=~p{"\eeEN;Þ=${q@Q_\?/иLe>u#Mp'Yn_e<q㼅Ra8pLB=(YK[l`BKB#4;c;HS^OA>Ʉx\+0lkOԼ`Fcfup.wlCnKJIi]&fXPAn1کFTKBoI!ӮZ f)~Xhy9 ݨOC5&|T2ӲnSLB5eD0:yP;(w9mΪnWhKu{`wk +kH>*ڲ1 wp5Q݌$;LvvJ1f3n*Tg@oO#9|}?V0M5.ۀz{" NK?C_$ P&B̆e>(qIu`|ob|_0l2WꂝsCܴLTIa?f(/+PIwB WhgšH EiŮ(G6 +"  +"(H2̙dfr $xZEP>ţC~EF:}< \{ +% rH6N$(߫Nᷘ_%1]2:$o-8ȥ I-qt;'kTjJW^}kfQUr\ulNkHn᫂H*Wd6M2 *{`V%VRoJJ`+"yO|s86Vy8 :+;9ɨ=.qqѝ=ɥ^ӏwldG;fH^2`zBȳ ŞO*{M2MoR0i:T~%$9ED~cj<}${.-+P]c=Vzpwz\S;!?C:GFIױqYŞ ݇>;]mS)yrEz_n˕aI"l|sGvmߵ_7e]֭>ГU)i:D΂G}V W5*{f? +($p\)9D$ZYr|(4D܁OHʳ ;ܫv۱jxLr_r ;Wi nV|Rudܦ;@YNl-QnJȲc/14C:'K&̕BOJ{ߴzfsW|F-q2 ?}Y[pXdY<\v+M{ir8~LJޯ vlL: ?@o[g`}>?UrǛI2Lk.}GpI8QRV%܂L0/PUE ?ɹTcۼfHs^QMC!)$ ; ej uIy W6#LMi9ĦͱP*HʘFg]mߝn+|X$Z6K'OQJq m(B~ljSuZ ťbhWP"z@UVJ΂\,<\HA 5Oaf΍C75O Uݮx7F>QL~:ʥ#][eTS2%c Æ~EWg9i%3W4ފ:}޼0_X|-ƣµVu8H{YF"qĔ-F95E!L/3zLw@"FRmOQ&[#ZO/xˤr~9T00bܬ 4Pߋb>_nMFY%MOaN$ʡ˖~ &($~>tBM%^i3ϐEf8UB '`-icIaͨ+ دR=ZȾŁ=5U#5HR>njky/s6H؃E oLyCG/?QE%FvMMz)=ZB.ϡƋ/•3O85&YKլ(ST eҝZVx'xaV4Ë*H]z~h~ i0d,K8CZy{jCF')b|xNJ>V{0e#|SE1b狛*_R"37Boξ(p3_<ݥ%-tɫBetƓpx HuRuɵ)H?mf@Iz͂qrgM_D|Ce +ӯ_wCՄYK/Ԩ 佨/Y0y̸7.]*ѳa !d[m9#{-;W[ U$mb?ci3ؘsq6ĂT t֠} dlv{Fyt/ټt̰KQ8 N"4ʻc'׸Ns6I ][#?wsb,4U_ f)Eď* uä6Go76ɵ{'CGa+RUA=@5_rgs1OUG*ʚO&Q͡4%nlc=%Z vY Zeਝ4? eC` _wvĦ10KB/*Brv4όwM 0r `$CܝGa6;g-N_&ɰ.` `0M/s\PMf`p3 $A7 i c(y jӍ 5!UiMSD-rBFL&^:OF-T4w T3c q]2Rd/3U\;?Up=@b +TYRJ3O)*+sWu.[L6ǼA. 귒hoN_=C|HW Gz}w\2h{?Ur_ס,[<4DmD〷C/Fl Mr_򑹾g"P\TMIiDw$=` IӐ }6.jYx^h}]"]l +8"ӽ΃ǐL"Hڝk:^֖Tm.^@1~qxTlU#U75:LE|4&W25exz*̖̆;M0do^lpmaIS7kD#'͊$"lL?bADINmEh 8Ԍ*"vұE݌5Z5 `z~x[MN&a|b(ǁ$ch |cq)M_Ɔw>bSО$  Dpz!G@o3a]PnN2);K4 U"p+q 7bLay$04iCc9(6>E3a{ R䏡0`?s07y9'`Lq`ScLr&MP.ڽ,_ru/F=܏=1ltŜ 9>1lם +KX_t+ =#ثL +uuWK̹ u)F@jR_$YuBśGbQl+$,o8qlg!) n2QήU>Ytw(^'Y! %GU9, &>YcwU Mj"Zo6VWF9=al mynqA/2AI̐i +qAN?!9NxlbO{eiYQ̶>SZ .&sbj?1_ǡPkٟx`дY!n6fVJ?ffon06l)7BuyMAѢ&m>>Nj#4J%&|E]ۊ:i2g0io*6zXh +҂3;1"2ҍ+O?KjaY|nMHpA/LsI5cu*ΐDx!W {|mpq%qehrYbBt M7uA- +w%5,x+ z!Ί}|%wpȩxeXx|Yy$M}yAz5{+=}5"6~{άq~p^Q~Md~*XŸ~,LU~S@~5 ~+f2T"P{pUIpf P[AE;Z1ٓ0U)Fj"0΂op~7f ![BPY_EE;T\1撠C)k"djpmfr=[M,1P\ǑES;`Ћ1')}"Ρmfni=pkqr^mtolVurX wtDyw'0|Yz>̾jqźjlr`ntpu0rnvgkbtgwWIv~yCtxz0b{x|bh|~j|l|^n|~pp|j\s}AVtu[}Bw}0z~l;fׇ i +9kDmh5})oviNqꂿUtXBEv=/yVǧeP{qgi卞|l{nohLp(TsuSAv@Z/ryX_dִ2f}}hƖMk/zmtLgdojT3rxAKuI/8xσ[c&5e[}gܞrQj.xylfoDSr d@u/x\ębp vdܫg%iwy3kyenbSq@to.wUad`RfWh-xkkemn)Rq\@?t@.wZtf4uhvjxxm0xyosekz.qR|{itP?|w-~zK'rp{sqԜu#svFtgwwtudxw*Qz%x?E{zb-}|Xpzr'zssj{@(t{vxv|cwy|Qy +}>z}-R|~H(oYpq݃^s=uPt;bvSPPx <>Ay-|0m{opzrt?s^auQOw+T=y>,{¹luSmoou{psGrlatqOvk?=txj,{ @k mܖnlprxqؔM`WsNuȌ=&x,zj׫4lgmomqq0_s*9N uI_|2so|u]}@vLO}xT;"~z-*|Ly(x*yyr z$y gWTaˢĮkTd@D\dPPp-HG&]30;sCg( 1DE*n6ܵaz*&>P3ĸg| ,X񦁓`S$>BG DǕu#i#܌-`xJ!wم:(`[HWeQ2UFD`|:Cd2~TvkdEeUb2̽p ʠ~[@QdF!7H$ #dLt!BOK*G-iCrB.UlmO> ,B2W<+367ߛ@ )۠&KO 0ޏO igm82=D 4FB[!AIb4~Z *fz\OtF&ӝN&3xF[Hjz&3n14bM zB! |+ +/hw{V\lsTjg?қ۟u 깮D}û.5ʺ(wM ұ=Ljeo(u\ yPXƢ8p2232"uh0 ;(3-ybݷ3WdsF@w ,8#!H*9)iF^ +P7Dg3I33D_)JQNdOm2ta':=J.۱ +s`d+uu- ǵiȵ\L +kw/i&G1|91:H^gW@-Eif?QF?/KvřMkz݈uN0:ӎ3BJ]PU@׊VVzDPC9>RTl{=EY^ScyjN96b~mwj[ Zl'd}[YގM:tU9WI-#d=sѣS IKuƷ6i/JO{s{c@6oPU,'9cV~M6IQ1WwoT+mlF0\Od?oi4M4MC%HfM[r0p[p|R’/Ld/_c8]׍ YpFKM(Ewo@jjI0/kad[H>|/ѓL |00SVRׂV2Cæav4x,'L82'7&n&CĿf]9-f]i{Ta4EeNٟή"V_ǔ3tf65ҷ, jP6Ex)ͻUSu@6M6dFVSˬGŦwƠuy@>.TȆVOdj?#驺sycA)w,zl<ـB*7ij,\P#;}}~r4fxO"ZhNMBe@(78,iA#FaN}qǖ*lf Zۋ M2HB-7߅,yY#p9|qeےNYƐ*M}"A튘6؈U,ۅ#||(qW,esY!MANJje6Ç,}#5tPcjOf=_`rhTkHm=op2s(Hv "zbtu5k#jl_-$nnSjpDHrB=tytn2ݑOv)yL |triIs ^ٟtSuSHt#v=_.x02y)B{! +}~st(o.w]^`cCcHlVf+;t)i0aldOȯ>tsw[-wnw\-_AMb0ke#SsShA!a7kO|o>#r -v0[Dn^aaShdL%rg{`j哟On-=rfv-vm3Zp]­ `܄cr f؝C` jRNnb=q-vBw~o`^q&ccrfBti quk_wInN1yq<{u8,-}pymjynlpptnRrp/qsr_;utMwv2>@?nC)HKс#Eu$%`^>[ +(?`~^x0_+OËv&"YD>s5x']~-if~>NF" P^OG# ǖ0<7ӆ7 :sXL!kݱrx{6Rt"+@q*7k1U誘Y}(~\H`J䞂\ +52[{F;Onݦ *C{2Hpuw0D(MHOB$vKѻX{'V' 5c + +sh]T4I DGãTD(2BNlz9eB_ ݫ.#JUbGɰ Pc36߅!3?o/˼ 4Ta1l-vKWZApɾ<>\Щހka8Z5$GdW#{{ߢ! e8l&Vlu4ʚ@ԸQWJ"쎛)9(6gf y'1?JL)b쭢l]4LkۘPpuﲹ)nCA Ŷ+2dEH'Hm&Y3uѷkѽӭ1n]_Z<ڮRvӛpjm9G݂#j}dA-uڠ +0\C"dhK>مٸ:IFq\BVhF'$[I&3BtK\ D'`;I ["%#N\I +|?a8+ş3"-Aש_ZZKO%u6`X{cͯw1 $+OM{'E],jz6+~ Qk a=_/E qbVk&S7fg\"&]KOÑ: %ijeB>%j:l=T1e~/ߪg I0^YV)<^ϑ% +զՏQS-WGpaθD8ߠ9D֑ՃXM' +UJ]I"mteuuE)-3`Ҍ SoO6Ju@$ZZǚ;oam>݄92)@m{>-V|WU>r$Ӳ]qّ¸zEYuɔ>GT@蚩\'}њG9mp.d.@L4c&,r;b ӂdlt3ݦ]Q<b-w Nk k bK%H@ j"W4sf|Aa{8c%J@bW\E':Ehsř=}9fǹTW !3ߔ% פԘ]YzĀ&XIkWdPيb]9gbIi $ O1wu_)xS$P)m/UI .mpsf5Uwl}oyh 4;=DUIKSDSjj:?2*w0P4o+G4O6jeu HW)ϛ=ݮȆs51 okaIӽ֒Wo0%>#}?V5N_r}%7 +Լ{!`D}K_4 +!Q\HҽzȔHN>uA-^Ჰbg%+k58W #wi+q0khcuTT[`5Z[`J &-v**cs0:-7o3G(Z!d  z Q}vx'E}aQ#*'viƷ|'in˵Y;eR{E1vikYT24o/;K |O c +Rr_T'UtKyγzaL= zs#k)|OĀ܇:axim&&^cŽoIѓ` +W82K/ױϬ˽^ipuO:JD:WtG<8YJ] + ՄyiZP-|xm4rQe`dZH ;4SX1̚`wpu>7 H2%Cd>zES?+&e{\Q>+) ^T9ZPFV+@l@ A B +r3L2$$x *,^-ڷ[]<**RInpdk ŻΫ :C>KXi<_TTՖqcs.JmZEŒ:^΄hsVIbm8tSX&^ a*Ɋn^m=A2s^mICca|k`K{"Y١:nf,ڱW x_n~ +!f睥# Aɧo(u +gįVg攷E)?n/ؠbdSu3QQIB`\C!d +P,2QC[Pһn`RXYU^',|Y5G4-},V{:T5zGFdx|4Zٲ u'ʦ"Ww[f^'0Xcx2rKJJDJmB|CÁ=55oc/hNL9'0jI. =$!_3s^>pX0]ScԹ`gi9Q?+,O|ekkC)6bf!),MjQZF_Y[-ۈfiv&mH!`5oIxudP#F +P&h_2nnmMsC?wOt[Pk+jnA ǐHځY*zל`L﵋TL01|w:44o(%j̨5YJ_|fyl00DO+/.5T"$8[g)T`MH?Ɠ\fިÕyL/\Zj@Ν(Wڢud>P"Yd'$$ʗVJ+W>pG[^Gڻ2|M 5kci{ZJbILFPCR7<]'wKÍQXb* +$f»~ ^̈́:)]}pA(+RXzE;b1t!9ݠBj` d> !L7gh%7nׅ _Qg1R2Ǽĸ:@n\KX)'WIC0hݤ!XL}4l5 Vh2,?bLb#(sÀytk]:ibP_"2S&F ߆*:/~5l6fݻ Ӡv(l1u;8qi7mL[@Wxlg Y<#nMDyYZOEX;/C<_IfGuROM++c7S +4ƊaZԃu Mߊ]>]o/m^&=Nh̕.g*>d_$ +]koj-]wz`g`@XRSZ^6uV^og~XQ 濮a%{s Tp4{HLydW)YU&R?FD/'gH7yOG +S0᪄g :po)-.XF:e*diG{.㯙nwn.tY<"`7dsSC!x$g:SX9Y%r_']4K . q cYv.㏢Mrm*ADbW냊M1Dqby9mT'buq7Or }yXK8`微.;~1K}wҭrB;ҏޒ &6 Rr*?j䆑lugICkM|vhZYHn8VzQ3N??֫zGP5|No(RGJ[5&Hs)qq}^&2n:zǰkFmP03;7Nsi+ZiӍ ^zs7Tm , zb@p22{96ʄ/= 4)c x +t&83B-(;^SedSy7yG^H@Es7<AQ|h[\jeZҎy1|i-M']|k!3h{&m5&[KiK%}UEk̀u hT[*FkkOZ e ev]G ؼ;GLW[d;oo3xY{OEk[@|l2섐^򒼗F6a 9uUQ[Em'*uWAw:^WfAw:Rc$DZ9-N7~c + +?;A34VfO 5*DvUe_Rqr_pMv]{қ[;f4( c5ڑGdxEjO-n +| g8 KٶŲ]{r3J(?ұqlu;S7qWA}ǰ=o +nxg|GCTpTaH͗O0U`llڤClt0jh~pڱY_,x',IUjn\[M zDBb<Ô]T7S0Co}2%sF͘MQ ś!7fSѕ&.!mFk(+O Oȏ@ W1fG 0JZ-#=qb>@@gIxFz|޴\E=Yg6atҺ*SY5T9vh  %2{}n}I90v zRf8kOʼjVo:*xH3_ 6WWx4\;5juK::i7rʶYAd~X:J1<;e +(;MsrlڪU[y5vw(k +-OlHWeG㐣݆L9sŠFp6i&xИp0C2}TxmCH#ѽZyڇm{+EAaWdVSy%ې8bש"SLL14$Bs&Bj&d@Y?O+82}-D^ݒD(PR{Ѭ.s!$4Pڣo\i(#u"D8 +:]C>6ڒ׶*m@1GQm lìOrusg# tk-ۤ^G) yۂ2b+PgDWB;T+4Qv{9輵;!f6~/ė|@r~EM$,<`2+oMҿ$ȵk뤆)<$\nnu|LX+z-]:r"Xꗺ.KW;–YFC :Aǔ+IU u+U>.+͋;SN@] LUXKx6 ͑8=*U4^qݗۥ>S韒+Ż eLsf v?m!'粈Yv0zْ2GwT1e{BHM, &fr(y)% P Ehl% +$EVDĶt o \~6-s//E 2<뤪t :mbpVn(Q7:ziZNl*3miИ` snX +U\Пbi0^Kc=!!{pwpyKH&Ș/UDg#M@1&yf_sIrŔ\ Bc7HexXltbu!hI +&) ֩ršbps;Cu GFq~~c6RbO'l"<͖z [T0}5y V|EWrф\2aAA0 /ɷW&aA +AK]מ q\kPU"Jѻ?W{j#'rG^$U)~VHDTup7eÊ⚊R"I^w0^+mOXiMi-T5ȝ'N]~{e r5Ճ-wA-VYF~UgBOJt8y0.{KO(vlJ uS0փyk^?6Wc+ +Cl]Eko%ݼ脦g}h0[[tVۃw,U^|}X?4:a<X s%هU)<@ZQ/[6 . 0A=fxIҗQl3\PBoJ]Դ\>[3?,ЛMOyIOi> '|2kxo6oy*Zo9XYifNP?1k𾠣 *_BupֲB[ 4Xφ}P73d"dٮ&<ăT>x4Y"GXF%Ngt2S 8.hpq܏#~2HleҢ(j =~n$ Y9PKC‰/q䢘&lrS1|8+ۺp5q Z(QӸAX!\$$$CsrL2$L%,*OQuOłBuUX뵊]xV~n,[|nC +-bY@X?(e92"կ)fm6@>_|Xȼ L N+VJ2v&ǂga:y*=>C,꽅zqwΣaVbP$Ԇ3H* +|tc^7CvfCUʆN\A X)MȊQrK{Fۏe"j%hCi24.$ҲɹDӮ?2]HMtaPZ+C9J*_r%QNH4r{W) |em}^e ٻ +.v_.e'T)V4(FoUgzf0=rƣ[(hGjKҢy}%]ʟ%(y쭬0L1sR1w^NJO7 نyoxõO`i0)¿6T@JJL#״C[!)9!w+@,&TQ0GU5a +5\1(-9]s41y3yʍ/ G䇫~IĴ41_35g%@.1N§ N̡Pi'74@rz8Z? i;f +cENOri@Du{A6.ѱ>1_:, Jf?/LCNN*E]٭!mq=p)ݍ +cFMH?b;t% 7r~L&3>ﰞ~6slD'9?6T­ϙ^ 5; +k[}gX0^hq$WKJm3qV/f̔&|}31sO[9"6ε6 9K+|dj8a&kɐ=9wUͩ?|0,lugzeU,}* e-^uGSoy77bC#Qşn[,( l^ 6!ʌ>":jbiq2$V1\$ǕwkGԣQ%[`ѐJ Ή `]+Y)u!*5(HIdaoElw17hYxЈrMyA39ScLYgBل*dlQ P/Džml)IR`i?ĞAY訌:et/ ysn琸M>dSG&HPe*p:vFӫ}9|%*CdڌTm ؍θSVkq~VQ< f +CB'LH? 6ǍZWzjxA|+cshi#a43 KZr?'H:m2AĽ eЭdcM^k^Cj#,@DL2I~tHGǫJ̀e W`_qZb +"pp߄CH I&d2L)xʪ*jXEtJJ]EZ_=@XY#>(UT#tgE UO4E]cDix`Ffw0b(U +Y]sAvjfhw@A,bx#iu+E_Xx˼U-EW'_@ce2b1( h^EN +`V[@-kbn_Pe:60lu-'\j|Dme;tHGD˪&աD!ߪ@M?B=rΕtSwo2Y!;DLž]򮆁˶Rf;˷-r0ۏ첸R}"?5#mk+3((.RxP{K$ ~?uX m(U$C[KIl9vL"F]C2q.OI61Qx 1iQZxle_)O&uZCj7$6} A~8zXmb|n^i>]fQBchJDj^ k]rou#Ih +8ЂTc1)üW+-*kxueI~PE:LR] &t-¬^*$M4-bB c鎳A9ZuKDۄT}pp;dzx0w + 7 ? rlJU/3BK3hf@jm1RזD*p֓2O(Vv +ndmMAO;1S`M-a6)N˛,_ +l[c.Hі%Ŗش+#]lcٶ$ s~&b~In^Y6-쪸ʟ/FRa` Ei|o$Գh:)=kZv6g|V'E;R^t\"ZW +YnN'⢒LiK[!6bjnf$=+ *.ӃKvIchP*%zډ,1-pGsD8DC7x&X8e!j5kL4Y &XqYLA)$]s_g^.[fx́{sHq  o݌ KFaa)1$PoגיDO̐Ńwq?0$װޮxYZN8$8 _ُ$`lcZ6ݐ?ȇY+0H5zቔkQ}Ö!~QQ2&P{BcH|7gz9^sylu^A ;RckU>)vQ 8:oVcsK68#7>^nNk_<w*>mڹ3"ΨŢl` D#ޣ7W-#hD:G"DxA4 >X( 6b-X>*'qkxOOX+{5| fP|~NEzEy?|S-2<3}=`[~#ltGPj_ _߷,cn$kaM=UlMQ"gɆ 5iЉ5M%7R%qvLSG[]]M vKsw>q| +7pL=#.[CjϨ^wUOlTvCe]j20uuFfձʪ:AƆ"E*S'_ +!Z:Qpt47rv윽Ys9{<Fr׃d+G1 F~ /bm1&&x, ^ LtZnDz4g?x7o߽06m3fB|=ksΛ 4|K5~Xp%&(*,.0<664^?|X@`PsB#b$ PX<1A͹O3l.O IrOS#?UBP' +BPT;} *~>22 +EOL_~[ g ,v,cy]zFl(}FVύPq㫪J6A$*H$Ρ`v0;f×9zL2ٞQC|QM5xzAR+Ԕ k*xGjsH%Ť^Vaݼr~Lȡ3h5$؋#2'$ +,FP].V!foDc&2`* _'ǹ{# ݰw%{2>aQ*X SV*5r1V/\2dL9x~dE ]0 +^z[AKmILŤSK``;m\ojc{.]w{]}A][UT5䄚T9"#֑$-QJ֙ +(R;7n^윆a:VVTST@e& +PkLlvw6ԷU8{`>5#8-Eʦhc5Ij ɱUx(EUu=XU=ux}{tjG +4a(=Gr(nËqZTivU肝 F7 :&|ؾĮȬ8CLNlG\nt{Bvx~T2?]ъ?:B': nAS+w."nG%PBRBz^MLpz&*T@ mHh؇Dc΢&ZT_Wj 5yI5LOї5m + һE/`v0;fˡp;ϙ־A}UlK8SQC#kדtYFUVErAF̾!b7E|{e wY쓌E8T@V4U4<7IIiA(R@: j:8vug*tE@EQ*r 럄B; !rIC@V@]_ӇQ5UW/)aY/-Ry%F2"  InK/i"tY{p8d|Q\Đxi'6ĩ/UUi5gԧyebLY(ke&\1q(h-Ev;wΛ6 !5kC(xH@m՝N&וy UFeaf5n\+#$,۾.wAڐ&T%_}ؗY6"s 9G&j ơR9aWLt~-m ANv$&! 2p0t{z$?5Z uTj]Ġ`9t& f,h؈!%gS$&T<6ncK /'z&bp`F*8b(@H3x!}': yo8IP&\P{C@Rt(ɓʌ*rH1𵐗&dx'McČ`$f>m|S~䃱ؕ$x0mq]Pe& i#eF6AWB~8QChiTɞ <|]z[u*nz!bg9Ԓr3lq Xr3" >4SPh=m@A8 {Ͼ+\Ǖ--F3a@4M6;ҩ'Z8JԐpjj6 DzQ0'չ=;Qv(X N#0-z#}2Ң>ƾ#Ahw8Vw5C/[r:mU5fYH7H)N6S PX'>}<5ӽe~y'NNdtOݗdjM Z̓x3YAdECM&-ڀjG ož>ْm\-u ZTS#%xG;Ѣ8]0^`#Hƺb~ںnA-9*ViTR8 +`'yM>aATm#GђZVZ˪ݐETD_l }mϒdo8zPc)VdjGT *:YϪ z*MSqKP}W7K۫Ov*om;Czzqt}JeVl|eryItV2j)kb腳h ?|lIlN^mzQr}\E+ݫl([Xp1ٔZ[m@_Xi䮠pvfy?q)?GZ3=@W =T2lvsdrڰP챢ށzE     q5YTp +yOCŻReb &l[Ghmb9M%>]8!p~{gkl’B42?ȩVnI6 +e%2G-8o QP6ncN/J/FQ&= }-9>#, +>nƙ,Π z,>3'ЏԍI6Mo$GWdosfܐT:jGyhKڻ)k[Leٓ#ceA>Vl oiEǪ2p˪lMe.{J~IT"Cvnc53}-"ÐhI'ِ,kHM"D[YjsUZCM:fD˂+)U +Naa␽Zfk@ 0,"IBLtrAlĐ  N9Vr:#Q1ha x!coDjԀE_dLqi&]8NLSNIS/)WKlƜ5==\[jTv]٨@(WKsm!fwO)iiLڤ?鑓#tɕOL=?ٯ9,o9̳t2UAP@C6-!d!@ BB6BĂQDkop94Mre9*ӍRMd0W:rB5*G1GRBd; ib"P'dh8^`B5yϕJ\ L΄*nW2b߭L)3t*E&' sdr* i@s?/=:Vh,~ߗ;{u15k}6EnA;xobhS$u,N%ɕ8j 'q/qO=`S)г ,Tרs=@o5-z$^˚Fk3(lUA?5(!4v(_uw1ff:w-}hXKvzqAOQ NϜ@:&z$B/ $Gc*8?z0;ߗ]/ZZV#sY]X&qzlKNCd P¶GFޜ=;èj!,z5ϥ+D`C^n"NJf90 2?}ɉ=yΝi*mJnL6M$_e A ($eEU Ȁӏ^9,>IoGs}YEHBWh֯յYTwL3rS1MOeS-)*d`[hh%؝jӣ͓\$|[XRK@-_JoЌ+כŋ8V"]?/&{d_$]B?,kʯ2xF5xun#s +[oyDs?{how1,8 fL?CVAyE% +K.?)-amU [5[ڜȺMtM0o?s}*Ϝ|-.̩ {JZVu (lIneC6%FQnj̍;\M{w 564q@p${{bKXQVx &\^fA{O򒻭m.B0b @ħ/d?4m/o +y0wA6kloz=vVtbd.RC{,DŽ4]@Г zӁ4#L#y,xK|}]XÿC>A𵲇i6pD1|܎,HψP(@c ii@Rq2[eaU^FR6Jz!` {v' fQm)0}^(6Rc$5 (r~P,y9wM:(^։gDHDϡyl"0A4t!5F5bl ”#@ +)ۚ+Ou`;\ mqׂZ4++'8bqu2ǬN Gt$ F7 G,)O '6bgSo/+WuQ.mlc`rj($oQM +0rIF?i#@I_S>8Z7gW-[ܫ J?&[1Ck\B"mф;[ + 7qD +$fØt;Sj͖%qzfg,;-^Q`-}"ҘGHv- 35Sl.J7oÉ@ 5pNgmwٱٙmu*ꊸ/#7H NH  @HB\$77!PxE.ov[O8bD>Π)Q6AY-aWjLGU-oF7k1Fj@3\=ۉ <'#Gޙ?uߎo qxeP IÉh1nzY=Wu Mզgԥ'(e]-gCGi.];^ɹ>~o[?) oOP^M!=aǠtRl69m^rU4\ O%%-,O]TB*s;?Mw+Pmv{ւC)#HܥO)ih\LC.!K'b1 HQs.w{ϟ/2Tp c6#s6"bI)i+˰exVz:;9 sYAnSKG?vOW{$a R*ը1o7l ˯WC^kh+qf7 :B|J+*u}B2#PCѦˋS%e*:g cCh܁li) +`Fm5{kï 5!>s^sUXt9UJ厓7YΆ-P7 $*gz0W]yl`\:XA>s97<5'&cE=ffӕDdyix M8ZH6."4Fm Iz9)d1 ź F+)mju@a7gDfFiUcԝRڊXxi>6|XG/@@+$kaQbќ0/nMҋ]%:c!רZTxY jq4Fּ]Xyw?=5a'v:u]㌵u=,"@n9 $$!+E@AHGBBpEA."(hA P뷙ӗ}Їw +oPEiԑ9qͩ[ q)Q<\Uh.gY}WS(35QEJYj)zS h/Pk<^~'?aS| A :8}F/R+|cha + 4Y^HjZU7 +[C1 ?w<}Aw{_Kyē]Pmp\+ؐ- TźˠRVYĐ[tX;-i(i7[9GPq4zg6@0=4kֈ\c-MANTij *A+7V |ZQ4fmld/ 5@ +ݽ#]w̋Usri07mN wˌ|!WQRQIc fWlerU:Gg&{ q? +n. |f0rg$u͚B869A$Vˊ:bVoi L,EUJ@!Og)Л@v4>4=A[+g $fy4"nv,9r1gJc:5J-AYL +:J匞Y*ϗȭy5Zg!W6@@6,GDOMBӆF`+٘^-+*uj/iuUcnC9K)7hsz 5]Nٰ;Td~>TJ4& *ow} u?zXcΑggS+~P2u.3MV&*1Z,_e%I#\iPpYRg/PphmsY}~'kGs4Tj`ޅX~>3en؈24"y 'ʸq~tZh/5kofصOa8s߸F_$@3q˰>'n9;7^^^=1.5?jD'_X,D,Qn?t/J\p &w!ב0؋gTStZ*j| D„=bCB3WYx{ot}5[,w$ 4LBA#oaQQ\xąʈ}IHNK ȇߠ Ke's}*_};v$p;$p\,1~ ?$ + + ! +9~|?}SRwp^@YH{VDrqQ"Ş'VpoTU$VdDױJtzt +*BM"{i1a=~oضR[ Q!q/eUV.yVH[(`IʪYL 1KWiE2c9rg0]DgQ])ܚd]ѯWiMU}:o@:vN?ćѱ@Fq?.[cT(y1oM70œh~8Jh.#lQDҭWF[3j;E#@O<~.;YKhk&qtd=rT}J+zPUX}Ψ9gTz<#8:<1)y/%O$yevUm:>Cn^!R$,@P18Qr .eFҺs&o|<#AD1@q47剜_NJ5yvAT8a@Â*2 +hc^3~13JEi颸r!:Aj$U^NMrs!&xt~8ۀ>4@sWѴm)9PV-kQŸiP8SYFR4c4Kl] IC4<Q zás!{2 ЅfNxfKH~JμΟuF^4܊prfJ@г:6BRBd +Am-[[ꍏm@Ch[kd+>~r`vS!CkBD+Y]d=a&JD;Dlw؛7c_so` + y툈z6tk4 6֗7Z *-Kآ&%ת#qfB׆cʡ2 GMTC?.X [ZH5:Wt6譥dUEFIҬŋ(ZǗkxZ,z0= >=P~?Y9=1y~4tV$aix%A!jLsLdEԶrV!tZQ<s`i ,{߸?xQ#/Ne`%zyx+UnGz)xVY'iNCV`k"|FyT&`y'_z>#n/F\Lz2Cs/)Tb%Ӌ\8yU B+|Ȫ/: {7Ӟ޸ho;A[,8N(V'O7* xUzjޝ;Wd(aCV%l`PPyp<}捑^gՕBkQG5wa…g7pkŭYlhd˿L^b/IİK(9w} ۿy7S[Zh=(L0~l.}-ZYn@."@P +gSDFd{W5d˸:n8 \o3K>^=ݻ_%%4$&8 j%| A +oմĶ^Ƿî:fԌ& 6-LzH| b?ӑu[}U +^^_b6QYU82Tݘi-434o'iͩZRn +ZoH͟sӹ?}W>ߪm7 +b#1en ?#s"*aQ{u5k ixtJK} +LjH +0}0:[gAM vtv3tљvZuծ]uC;rCDD @ !`BBHHBr;\BZPXnŋu ؇}f~/76ذQ @Bbh\Yuun^R! lQwLs6H-M{#RpRʒKʓ7k׌MrM'?gİkS!" q8@& xw3KsޖG!禼:􊑟 %X~H<齾vmWkaİu~AD +(Dh>F,AC~I)o|J"&xŭԤǮ03bgF}PM}3-z[6|ǓoK@C' 룐A PtD`#c{xʢHjl80bÀ!s'<jc/q/Ӄ@ | +8- QMxFeU>iHR|/1{.K<['-<+AIgPW7 K g + N H]iD/X"IYEMo( +g]Ytd_6]8|pR~ =)L}Uz{@ yf4HsRA:VPRX[CYqDu*ܹr. Y%3XlsZ~=*UN^i\U^,t{gP5y - AEr(ӣAeQq>IY`<<)`?5Y^2]b+0gnϪn]T_\Vc/=˚%>x[@A#I=,-B- g Vm<Ǿ_%߭PfZewJ-۸?{5# %SryUC ݠ>Ф'XʂRlFyCrsTI0%ŭҐǞ݌!Wi KFMvWZfC?]>jqF-VTyl?d^6b#Sl0bYKO̹4KftDuE5spx!DGSvWLv|j'mmcUZգ_E&Ѕmc~0 ֑ܙyWk:nv}þv sv$4y4A֏K磻2nuJUaDG222qwQ؃RpaWPgM/ uLnmXivu:3_0%yN䍡I/ɴQ:8nj %bP,|Tv@^@q;$8ΐBOGhOtP___r:!͆i`=li_(x1ra q#Ь$ $v@mdx8$ F{8 +;("a)^STS 7 +Ә>ɟAdL bc!3쨠bUom`kRS2i@1ȏlr>>^@=͚#K+ڴW+lc4`}_81CQ~u6hxF 0l? y;H !?)|$Y"3?iV徊H!fLSI̝Itx#{vMH!!M@0cr?H+e.%fNMcH͐/dLk V-I9wȫ_G 7^P6P%Ȩea-\`XL)jYFX| ך3"紒jro/&ꀣmjv;!NzA1 +1+d)VasYV.o*X0N?'Tg<'TZs{ZI=yw)=?S4О\ p|*N{?(ы +Q#eMeXqiJѳRSFz9XFRwOMnUzwOqKqOVgKx}E5qcu(:ʢ2 R^P)R @JHC"BE0 A\ gnև}99? ^!HyYz@-F*#1KcH9}b_Rh2/s/gf 97y7 HPa 0WRX3aA *v=A)%(j*5ybf?7 +@\MH@2 P7]APeB<*#q +r|h%x\N/bz|VViè- +5(n@ +^$k +$ub +wkd߁zf0]1>F)\d7KheRUr:[Dx%2Q5I%euaYI+tJ^%(G-il \~NSyU0.FyaM𔋵dCPq d&؜L,QdJ)BJ)dB֋$SC wNyߧ6Ʈ6/> +qJhMIlm"Y+q &WQ%+ŕm +Tbs@@ӞEoܭ-~b0䤶2'rą >UepKyBBc^3XVVIqUz1 >7O;AtzB;~ICțF-LZ,8GK(^4#J]cz9@YA}O_\;nzGPLh%%lƲ.I*\Y(ؼX%mK$ik ^-!Bs@i +?lu?ov9цwD%HS2{31| +n)c!5*!/Q)Hj&I A |sPsp3F>M/Gl|tĺκ>mw3ȭUNӑ98żbt,Bw2IjVs:L&9Z&9&^ MaݕɤvOeq'Ey+_hbh'GDzCȺB(kAzE*f5Ό0"4ӌ)ftPnjXo]+o?سB쨅手e36M$Po(u +v02`Ry=0^G/z*TN k㷩a#3 +sr%ۿ +Ve ˴?si1ߓAԇaqIw3SY*v5(Y51讆to40xQ9rl|Wӆus^Y~mKw|NQ^#Bqsғi1s̈9Zn0/GϷ`{|{cn[:6-2vk-oVZm-FC q4Fcqƴ(c j&Rߕ}L{#}9,Wϼ3 , S!VCfi}ؼþMGNK?z8O.{—`bc?[BD/b>bSPo93){J<#}Yw:W@F4 WAZY۾[hΪ8,v +]#xA7̀}@a zZ`C? O-"ܖ#>65ڷ;2"{+vM%\ -ypI^vq2_gQMg9=ǥ=Gg>(*(Ȏ;Hd%| ,심@EERVOU0l*wo{_;Ci zCg н|_H)Om;ݠ0ʃ]ʬ_Y4("65p`63q' ܭc~3!>G P~؎wr+ ..:rN@uᎅEc *lظ +zHMQ xzAԾDkW pN8t8@`s$@fka;PYln "b HQƺoc.᮳cً9 ܹ11?` v뀍5}wG!Bj/YD}鈿S ++5wqY.棇xcy/q14o(v7kHx AAn8x|A +e=1ı.${5pנq +&+0ȋ9 55l eԄJtJ{UK?Mj>"k>G>EOsE7ڙ+2k1`0)쉑KxP{ +]D#؄t +J2:xՙ&V"_8Cj71RuӲ +6YPsMҹ>jY,BOz;[Rd:MRhg75V]={__Зsbc kAENBv?k|?0j78H89PE +-aoPoꤜYB#k 5*a\pP&k, +E|>O<3KbXC㟡m+y~oߛ`b<&Uȥ\59颦lY€VɋTg*uũ 6cdJ3Ft@6cv`^GKq;}^] +h;c;H N]/eS  VUfRe $7eMZYWF0W-3|@oΗ l1a ؜um%]V;B=vB\pW-%\gKERSy*ʐU(E_0}&79 @͟ +S߮\tncuO:>hp{+!Z#9RM2Ǫ* KH)T*mN6M2յ4\DgB9_2?B p%MumwuL@#pBA^ ST::8iQimlY"YY9}^Pd9(R6 D)LI3 %8)|'r2$E9)yW ro?(}Sӑ) ֩ COǥ]%c7M5Y,iY!iFy-_RM-ϻR?{9,Rl|RRF$5tYqE7 )ɏ<ޑ)  Y4PSF5;/xWg-^f72.ԊU!AyW2*R/}8Bfzc%9gʥAgjĥ:NwJCrgECzu6Wzsmsw~a5eJmN qȈԪkRbWH:&*_V/+w_rDgfIkU[4Pe1vGO}MO@ٛK_omϕY' YwFHNM?x=G_sb:Uݔɬyɮ|ɭRAb/+զtU|J +WmR}mNW)6'|cDŽ6%ňw3\Heܩ%w_J{1 GV(d2*uTnVyxիE5.vmyN5ҏ.b< >oDrZc}[-U$rD$j {.TB2/^#.SjПS3gi{ݒ>'Oqb_B]\~gݑ&ft{w t\ ꨎltz9)z68D WoZ?u#ꇗT ,iCzҏNF<,iQL?ЛO`S,W}ueyUL+vS;3$~S' j#*eߩ]o^T,7Y+O;'=#e4@ӑ/rdbO,B&xȏYhuX#wvݗ +C3깢L!rL:{NFN&&%ST˴}P<4Mt +/fVwWkS%*4ҩǡ; Ra:6p`F~ 0cFnuF##G! +E$Ks@9]0D Te8v,`X` N70I>~ r>ę["fȱ2E>ރwf6uw r3W)˕ 0b +WS $x9[LkpXBA{c7$;C#@!MO/ X/AbAh)c52 +E0"Z+l xj=ir$5w« /Urc3\嬃hD1w!av%8?)b|Jؠs~S6$ o=OQ3MAdpm:f2ɷ@Hq$KˡS YeLT~Sz7I}t _(Âh#t! NuM5exuH،x1bCp = Ȣ{v)Ki5)Zޤw=@0A}N7PF,`Ȅݾr<`&OlX+m$9CiFg#Zd= ̠W5o*oQ+~(F{.0F0Lw$sD% lggEw:v/@2ڿ.bϰ=l.R-:{RUp#V$BB Y$9Y$0Baod(PW+^!,E^y>9/yw}qzP!qO( CT=gd W o#oŸ_F M"#Q/IѯȷP(7b5. 0w~B~`9PXT?9; @X\V?, !tǻ4̡Y%ԴjH#uz:~CCoX}:No\{5MU?ͯO+r3nwfB` 9HY}LpuD(09ZMF5M.t+y&A ?,'L2򤨈2% `uM%;Ěsy~QC| %'bzjb72zjRXMI\I-)'Kb mB\@ḨOH8Ww~rCsk 3s63Q64r6[!¶K&~˙F"D]?L 49.5%Y =7pH`1],Y1W|rTMOweC/0m|L"H +Qo\JhKٍU}_6HϵIӹ{n OO?|{e/ʏU{Pu''L٠KT2^fq OhgK ^\RQ?& +lLjwxѬw݂{"YMв֞\;Tw}˄ nʦD֤ctB5YN7)S92 C'NEEC,PGI1YR PJ[rY¹}'}K5Uv Y/Ηg1c|I'SCR(NYd*R!Z2_ɞ*!hTAc2px3H]}=@]_Y0^}gwt# cOU EttAVJNSrY&U+UJJE1HaU@5ikwxN|ҹk5zC'KԘ<^-j3$/K5u&-Qp5 J暒Qr4rn,Am@7dK[>Tluٰ}së otxՕ`ߦ*P'B2p5 (\R' G&w5\gZ׻^<|}WwVPr9꘩{.+a%R!(Pq9g83mRa. $rt >SWV:rk>WX}rKEGK 2؀9ZG@$Ub\TDc+شB-h.YK}6(E[%XӸ$.wBly; +OU+ڼGr꽳ݳҚ7y(n)(A=Ǯ52:ZVf$+̂J]#EOP)=@/q֯/qxpoӡrΟ}=K+3FNȺ :VMi ӒLC5vDS7<]~QmP.rF/Pm`C߽yݏ:6Žў%GVg  uDЏ fB)7^^Lu)6Z2>u䝆c Ъh](VED$ +*d/FI + Œb#ngT-.uGܷ0n +B39+r?%RC]9˻RzU.y;w;l`Wqy-g?cS_iy=*| BKZJO6>b)MSXT*4VUj^cu:ZvctWn`>ӳ~˴[9N;W/9'%j:f8#mϲLviTv:^֚ۖǔ.[Wd1uV#eߴj%?Pbv$k4mv!&2yҶ]7tG۝8 /t)]8IWN0׵^bvWrRsLyc?=*˷ /m $KQ TL eP`F80+c_ĴŦXJU$& U% J>=r25j"#C##KnD]=q=ɑgDGw>ѝW!p|!ݲ7=^Jp|Rq^>(9!Q( HaY1!;BG.;QȞX?2n )~c3:Q/H&à r"d(|!/1B?T`GMG b ֶj+}<Aw#` 'p3nI`ǃѴ(ȦG@=# :d Ry=[9}Ʀ߷ V|aStD}Hp GP''C>i>ԓ}<9S|P6%_z=P5uv1 ġP/r. ܙIH@Z^(%Q| DJ/&8X`a:$I!a xa;{K!Ȉra93aӡ@ +eqqu1Syn-\Hnlf裆XT?go"aHi9C +crY3aaH @FVҖECm<$ 1n&x k&i}V3 #~{Pi کaa5, >.A C+Ĺ!<20DC:oe@Xu QS|pS\(nD{;rPo,'!6@f A c8Lש( _6 hLj] +䛙āh'#NwY3a)X<,a&Fc42Q)mkD,Bg_ ܒZTO.P&6+%_e- / _'E}4pR4Bo`,L\jV[x~IvX%=!+9x-7+__)[T-=YsSn\V/*G5f. 9sPl8PY^X#*EP.r`i^|onI)k-筮EҗvElSecM셦Y֓~G>A^W֯;8"߇UcPwGs-}5bc)pڳS2$kw[4UՇ5wtO7T]Kzuᔦp?VM63rz\?Y Brs9z!p2;ik#|r[a[!g=,Ʈlׂw1XWef ƫVD)tL^Nn?Γ8rFJF7qxg3Pr|UO3& S5`їƽ}/0~_5t<᳷9h[C䙆xO$_TN r0󖻍\g'9YߎAZ-՘MOd%LM59U}v!5J@XĖ1fGyPdвp.O80v9f< smOvcb8fZp(%-$T,,5K34HDuQP"KٗdZN<9\vupdi}{>Q `>7ZNHM$RCÆGda+2ZB'pĂp2SHr] +j yhC_K^hyb5b=lО# pQ,[8XG*cE_ODNCVNš)i8GU;ۈ&_HfPHZ!I!q"EmB"~>"pg#!(Ohg3aNQ4NB8kC{-!v,t5J d @T~|p7c1?#HKAo*V"t@' @ P{}dY7` +b u: Z34b(@,i!֡%`D(0~N} G69?CL  [(B[= q.Ш=4Bqq%xg`]y=;{5x5,k 2)Blp'0\Wx@c2;U ._ QM;#tp[\6scc~pG{ÜʘT e0} 5alZ(~'gYb.cny8=לOO11v +{*D̿D[!އ-L쑾h hśH 1%:K谺8|H!rP6 ca=,(^%~wBx/[bE܋=!9a grܑN6C=ڵQTUnE/?%'bW/wsᓸےRȬCAIɃL;8bXɜ!|n>sZzs~Ē7 ѯ4[؝>sQSYr_?ߓߑǷKWҋayu!CNF +;ڢ0xǡؐ|Ѹ#i{KcmJqkjobMZ:Oo tgw%;y}w,p>zݭB/M6小\!8D߲^7ZՐUPq̸%5:=iszGRUgcefobEf +b,g":z_Jמ 獡#NvF:unrsԱLvSQpxWZy}&6K&w*簩2yCgu9Irr{A"rYLtފ#oserɁ`{&^ɛu6LfJSdSy:qMP\Tee`KBE~Cb2isjrqؤϷ&,%!T ; (]@{:!PRB( R'DD H*" qwPagȇ99ߒs9I$(BVK S%> ~"^=7y^as`&ETSYAʨcGq'y3좂s‚nÔ/.w-XOlDde1%PD _*s:bhqИvN~Vqt`~xv>ǵ6Ç_TSq4Һ"މDnW49z)p}8EGדrlD@`VFExߡ³JdT=bH2`#7>"ak{?~л>;0y&6)!3)l09l:`9e̒ (FPyyX햅#`\/X˜pQ<cr9Ut(PZ=/2*PmC|zu;+lrJ'&I̩ZgTn$VlDt_$ X' ڤEmۓJper7ujRzdYgg穾P3Qֵ]SNA&&t.C#I.^hz-;XO#v>c>N6nkRlrk}xg.+98=7Q; pa``4ݣARP.F}CycJO$ ]ㅾjQPpav:MaC/ao,lfʹ%?wHo, ןDY\$o4(^U5"kUfJglYsVXV^ R x_md-;]:fֳ{l`^`h>jd~rgc" t^hXx@@!`CӘJ*䣃t'w9O~[=>*~fnsK;jZ|[=8t#42B/kd@su:pPQD-JSь6t7t䌞[_Ce!S +"gf(`*`Tݍ=.ne4.OH"Q(D'P\ЈhCFG t}JaFK!k.:7ict5A=Ș0EƬ_lWXi?M12qJ$ވ:&$*eQyPEY+:긺 (# ~| G E 3N:8ͺ;8Oz@5!8&cǴ +|5;Gk :{nq#x9g 8fӸ/<.ou[@1?s!p@3 if o^9-j y;Rf5@nrv' tR/2}e_^S\?zqfLxÞ7$>hp ANAF\2r6hjіI,[t;RZq3~.Ӿg\^3E&$ߑN_%| +, @`iRkCٽV@8y5l 9H:ff +(wĬMқ\?'?z u:Lw~v{ S?xJ;oe;5CB"/oSlKlYk3)Nd;9ut3{ܟ1N|ʸI/WIs >@e@>AngkJXO]%i2Bӟ֯eǤ鎣2Մ!n 1!ktkk:K7J?(}\[0G}Eb=l AdHQ@[!Mڮ{W{zn4yX)(6~;aj<ⵠ*+6EI>9?nj3qf K10$H 0<_^ ꝉh4 ]\ܒ\w,_!5{omwrqqQ{/3=.iH}!徽jϾ&)id`Oˬc6'vMUE]sz=H٤[ ע/Kj{FܕXRgkܴ?ZWLdUE7pQ=’_DőEQoQ3C:~AW= 1%ޙhFIiV V\-[SOxgWVS{zTg*|$1ZpqXqU_-khbOc/scs^r⦅sx!!n꽫QZM}y6Tvnj +Ҁ' ;#=T>)2U>(I*ي.Q$]qWVS4)u߀`_vP@cMjM給`:IkOk[ +lZ +ϗΉ#j3I%iCibVvr/]$8)NIC5Cǝ/: ;/1n&K `ŏX4jFtM@- + aPBzVYaLYㅘk|kObX3ٱ~&6r6ȻOOG6ɠDW9i"ӽQEhƜ ,0b*e9,'aՖS3c3{DQ4H0)ځPqE! +<Q=0i` 4LOt=.a.ʰ"aDCE4TQDU8 cPf([ .Rn(ASxX9xG r09ACڗZ1Jj ֨IGբ8hJ*\'8(>M\'ot b`8dLT;YR6*q~uF.J=QrNި?(KGyR$%zQQţGC1 0Vg်Qf@e;b/CxbQި$D*,,  ]彂w9zЧ[0OE-z c LZ` +c16\0j +#ڭaMzo0|?@uDЧj*[>*/x}P~|ݣ|ݥBY0< }c% \*fS1wM\H tdrtqƽ7jCd n]7{G}^kNtiD/5D/4Dj=|f~Rc5uԙqIDQ⊈ȾCHrsH }; + +#xZʴiZԱuZ>sx9||񐊵n.5YMAJ"KA 5 *#pL6#-pͶz7ӦJWn]Rc&S٥";H+,%p jHVJbe)Qa^b(,D y)|Z)qn3כ X)a zmVoRG,K)kȫvٕɎ|3LV&V%XU?@Uw(1ſ!1Ő(ZeW0Wi x6}=A{a.'M6eKȞ&!>6!$.ݙ[+tOfUUFW#ȑWy{R"wypьÝs8>Zﵡ7"fi-hgMoKKiIuHl7Iz7QCi +n +\+k{'B>p6?7{qevCd]@?ߓv> eЛbw8Gv廝xw{S;|)W[E?r/~V迒g9jfjk`s@=aSN3w1_3"ܑН]QM^i@AH ,!!   aȢ ѶNjkkGfܵ"hE .qj3/s{{sfW/=4rl4:&eUԉU'br(PV_}P#>NW8,9u >K~i]ԅ܋/a坟ÝyDUD^Rj NOD{Z\oO#"V7ЊwXN)iQOͿjr˹jʺZ\25/$7'6}&o 7}״Gm:i=ic l:;wP^Ս Ϳ㌊|QMD[}fpNۊ<zǷ1tmk|cm_blԶݜǸv ?6OvwP;;ye*pALdRԩ3vΰOJuvuO*vt/v^^ٳK޳[s.=͐^cHzak=U>GhùwK[w@9(+JcԾ"_L+)qZ;@U=h̦E;ȇ#J$ëpKi +נZV7n7ˁp;8]~QBi8 c>H7'""zBJ*'T"}kC]dR!EBXd/48pܑ~p֑ ͎,xx5quoC('u"4c )d $L.9t?$\0Q ‚̷C|n Pݠ}f>g#Ѕf!8w + +W(|!g5q ̤+$a.9N )Br=H$$(H-@TPiwgpZwl!_t1 b v{ cbh01dU!$Ą Va8*Ĥ@= >re(>/}K _2AR]`O!tZ +WR`HR~E$bP +ev0CKq'@7' - r\>&@~ aأ+{X>߀8rɀ7(qVH pIj*$&9f̙!vh7z+bMDbGd*FU'9oTת+-Πӧ<S@?IH䓐0)IO0M_=_3[|5略3h5gx/4x57xk}10=c ֟.)~ HEnZ{4:ML5y$҇V'c0l{nj]^An}SwQDMЮ$M|[:A8n@,ҘHB>#/~|qĒ2U<}̷;u 3+ޣ&Op/Bh3Pxtp_t=ᙨ*рK_걺I& (NBQ(e(:\ Ź77ǽ#g={ U[Zm7SH!zʿE-!ƚ+ƛ9ji&"N}} {o7sY Rʳj)s\ΞMoBVkNŲZД!cR֐ȧ̻$VqSmDcYi@~<4VJ' s<0,bK%!dW"fŹbR~]ʀs> *SINf패';Q̨<Ѡs,AeԽ"xBBZuh)MְBXRȶ[ȯ)\.<9q]QMi$((H*"@V,f5@ !LK@(h5x92NGǶsȇ߹~z}c)̓*u96Ϝ e^*3WuZM?YP2r}mob ZfkVPa~RM|%Qz|Ǹ$~(ŵO%n +%ZnUSOPj8=G`ߡ_ҥhܟ)<fA%z)U#%ܫefeE䶉ò3. ҼMBZ P+ڰ¦9$P%+2-%&DqlZ`ߗ+ ks9l3k2"Z*?﯊"you@+a{6 }jKKKbA*huE!j iTo5&#YP>e~L`C&ZSXQr5\k޸qM>ʮSkMmeCJ)׻_V& *W"5QXN< @>Bsoh\!B-"y3$0T`½z5:<̶ɖøPFm[ÉZUGJ>EMʪ|oHY8T*Wy-$W6Ec-sFF*"odRJ,48X`f:` -ؼbt̡Bpק{+y~š@~,6<_ɮdUL2d tt[Z?tBɐ!䭐\oJީr1p {@0uָ]r]Ky뀨1dzX]ksTu +BV&*)LU*CqP|Ce¬Aȿ!mpLp~Wy z{ô1){˻O9w&)HWԖu㕧4K3!i03"Y3JJTϑ+ r|ȭ^:OuW) {hshF}p|f+iZ@՜#H1\%wc홗.:3.f$p㦥NNzj5y˟>?}?Oq$6nfDgpG p =I=@+B;D7xxK>ؼ04+6g|`rŁfڵCk3eO=IW_zFοC#fwv~Qir +os+ k cLV-&۞˲?f`;Dx; ejgA'зhv 7|fkg/] z ٿկ{x`),@ [ߙ@C 8`64f1ƳGm4c5ȵ4W+jv8N Z] _;{ z LpA8"4```"pP, RFC` +l¥zb'&jA'^R +4TxPQ HG</chm6F&Vjr +l&e +#n#D +eSNCCC@:*"=S,kP%;LQRBlt$js_%nsFΐـ޻9sG^xWSo-Tj}'润 MuyVMg/hF5DӠDdEa0$L!g*Si=j0DG3t9G.ߌzFZd-tm%mӅZ!?9rNGؠq;EQ=QGNZ (M4LfΙIJz{zX[3ح ټkqyVcW\YgCSǟ"8(s9~P~Tx>좸6xx!IM8JEo`iǒ7g`Yûl;x +ʩg[at5#}!UgєPp6i 6-)>$VG7yTE_UF?UcP=LxI ds0<Z@{-ΑR.¸j8]ECF.-D +ǣ_:N N&!Ƚ2~"RVws܏^ZqO%(ߓok"!dc@13E4wкXD]c[lظ ]lq|,úՙ3 +\+ֹM.}7מEIRN+g^3?*I1ބS8Ä́!9&1<&_b7r2Wi1_ì͍dIUTfgT6k^QIɷ<^3{{j϶:-畅w_u+7nJG騘=C<R}ZVry^).jpdI*/Wy`vs-q-[ 5gdBV.YMY2O(g6yK.omZ>a"^.#NzK\ g8@U+beV%y:Ewn_Bu.Ϩ<PD H)#LQA,"tІFpF RD *1XQp]f%'nf=G}s=WR*x-^nAIܐ84wQSQQ;aQP_B61xCTT0^,p̕_-]Qךnܔm^`UfWH+v)OmRIޒ)ܤ޹oEDBLH$ oA26.98]pfnt.*[;hQ]&8+e6lDzBY[Q+HouSEg|2R>H{-H#BK&E20\ߖpQ )qXt)*+4W֕V"ҭ &ۖg:J $\IN^vNWFv -h[i Q^R"K0T꺭bQ#U+,-}).$)" +&{d1pq5k7٨&+46r5 j:^q:(X̝),dEK9wkE5/snAph}OQQQF_,Õ2ڃJwfm4Յlț5{V5d7DbRd+>6)uSu墈&ކ.uCq~hН) +Sxgz7.^܃ZZi>5Pt:2e^iRuI*Knm7rKs=M2 JnHC{p OpCpC:=zW? +-4 ]@e*{磤ϖ)sg.VY97[pp֮(f):v!;ikw۪n{B.^R=lRMPzA]H-u̕IrbVύ>u4BcuGLBd.XPWvﰢqy7N}7{;s& 9:t}C@HĶQc$:2%@`u#BF6_s*ppqp5~'[-LjL.7h2h1=D[!b܍Y?.b/Qߪr#icؤ#7&s,17]Կ+_6dǁ\DU#c$&3+Y+&lU}'|2爦4SJM&-m):S]{ýqwx+}P2 +d.W)6ncmm,m +ib191 qBG|KV@E1aɂ:3jQ!9N,vP>'Sߨ־XԤN]O}&gI}D]\wa% R)i~=>BO͂82ٙ!.g.CX~خe6JlQ*\iTS@H.!y`b FdA@A 4 aJ"cD'( +(
Pϱ+߱}`:{uI ,c`#ց]{I|OdE?Xc{< +8b¯37*535.ClU4-B 8۰::paQpڱX'v1e| 2F9#a[ lA{̷Sɥ,s0\; +"_ h@ |9 fрW2:pb5 a|'&Gq b{̽D|^'Fa7BMhXt'=o) <_YؗڝW(5ܞdZnQcU!-[j!.z5{%-dp_jI:Pw1 d_hwWеL*D:臕fJ>Y)hץ(Sc +e&Ir2j}S_l_W- TC|)3I]':&ͺ(f^zLd/.XBVJ/)y+nd)˼hYh=w2٬ )vQ,yvi%)YaYYWCoadbτd`Π6AfҊ`u ؋M,hSbE nU/*H,X%%.ls>abAǐSN7=p w +%!9kųx)-vbE8{`u,= +*̃/ŖŔ fL7=[+"|WhW+BwK' ,:}mDss^R(shRX\)wPCTffU*'EL;mV1$bل-mWC_^!S~\[~ uI}q-v P߻`G)@N9@ΡKհSk +km N3<:fjm0ormڹ55K֤Uk|YxWWENѨQ?Jw0%wrf@Rɼe%ǘג3Ly)P@w_wOqץu*N:ڕyw[6~_U%1/;{xb ؅>K= c.Ul&׮VsǭX[-uMu^uY~%U4uyqx"*ʡvlC5ިxxŃJs`Vm\clTf3iwNl׶ݴMnc3w<>]لmqN +`TRiyFxs.q|r۵yyo띭}w8>9|nrolʖҵ-ˤe=UΧ䋜o)`"<#“QZ2\b$D+ mk ݾKvxr~Kqf(/]p6Q43` (; e /r*x> ].K< ^9e>gx:,fٌ M`tWDL+p`_+ǐ5|U"wxP w`EĄ+͸EQ"\!dAל8#P ܆Vk=!㼽ay4gTh֩ȑtG] ;z6& +,}sQD%IV%~pYJFii~Nu?V,'ZBsS` 9}yt{\T_b޼1zDw5Q]_Z|#x~sKn)$U9 48U*婄\C"⁒RX?"ZB =zOᨮFgyfG*˒V{3f{OBlMz 4eεFO >pZ`JUD/y:Ľr y̿_ # C{-4k-CF(^ԽfjppQ0f|7\^a3d{wUҕiM դ 0ь]}QNbWT.ŪUV^+1\"h:еg=Փp>j +-b oЫ*CH׵Gh(MAcj1:QLtwxBOg tZf݈kVߙ^b]jP!SXIsGN/l7O3y|-0?a LYB6b>@p-3(.7RFvsնD7ó2?YWyĪw6vXhŽ]ٖ eɳјJgg]ȳfPQ%L^`}a`aQ PD0÷Q )Fal7Ls:q&3sޜ0e \[%%I8ù s>pٴi +]ʞQK @ ?IoUWp㠻6DC{=7ff:47BsP u~ڪ`v? lo>mnVGA '&:n1ߒBӡC U(| YO"$=3!Q2 @ׄBz=HfX0IF)_u@wPlP ( PC2hM? tB"A +kSRsӚEs@a=2`8Ȩl3q}JCHb >$L$)^>8qZt^wK-uD'3Ÿ2q'vABpaRNH^ɛB~ CXHPCnnDOZu T 52^HF"$W셺=W3uЯGnj6{ΆF.f#W'~#{;֫e=֥?:CןdNvå_…VW.D.rah+i 8Jc=a} Xa2bak7lcwݿfwܳmЗH=_2p5YIr4'jqbjQ3o7>xDxG#G퍼y13K~{ԷofÆ_$TT.nTwR7\v43g7p$I42w7y Y<=Aߎ~m1[b~(h-hHh\`%li<?"ﻝAtWΰ)83Aq^aQYpZӀ4ۥWsN)LjK$4%Ygu 煵Dğ |1SPk_yd`ZT[0VFr2zeN +K<׭EhJ3y5YxS}k]|tDP%VHEfuIcQؘo1}c%殺9Us0clƐfӧveٷ͙.J[}FG]z%WPt!A|BT*˗$S^X`EаtP7)r>0Oc m@o6Sm9`ߨIvV8ת\+Urg̬-l/VʣBYNxBKdń3_ ܣگ逋"`~ǸCBGGAӢñG%8XrӜ+wcNg3de7 }|aQ$G}%@~HJQbB'Ry"es8Ba+z|tٰ֠iEU9H.%:!_YW˫mks{H=%Qi/dj )Բb-in({HWFG'5ԗ25e;8a\sI}iqp)2t|b +~VA `T;!V.J亻r'?$ +K߱!u="!{KsH_[p"$bP[*( b ݜB~xmuSv%2MYY^aS̃$0(8qKQ[Q&']%3ZZ:WtCY?֠ȺYwrpnvC}V}^8vw֕z&Vk}j15,(-aW¨/U V]uTz>+C4-(lA~*h7#};jdEqmim2Gi9%5\y볿_x,?:_/aa ճ`>GSʹ -]=m]]@^7^/dٿA0Xnb>/!W[cv 幷%ޮB:B:㦉fz~t.tV.=Q7![@$oGx(3͉OF"Ʋ9u5ctmim##?r>o<Y, Q}hPv ec@¤b=%F:ފ] gBgb=3) ΙmU?nqxkzq 7/ޜS'Xc@ +v>ʵ sH:D&u9_[sc>oχ`|mq2oTh3q6٬܍~Ivl?ᮝn9~Wc2 Ng9ឋ@e.,x p iq6.a]xj_Ǻ%,e%V/YUKʥ#LyFr6#Y,/<爯1E#T{'trcIICm׀759`w +ﮃ^ł_9}PV )x=χ_u1>FH}oM+@ lzx> չle D((`W% 1`A,H@Dņ(< C-O1D!ODQDĂg0;=;{9F 9PY0s C Yҿ#DHNb:D X +ACFr<(g3J,Z=X=OZ8 `] h%+"6!j&;@:5ͣ1n@hm +}k7jGK(]48Zw }0`W.e@~5Gn+jM :kRsG?:=@ON}DoG=b{}`]$7bu)bѽ5t?+f 5(F?C?b>`hUŗ`RwŮ`0o4H%$"H>)k xCxjth(m0k0QLɼLDcOWI%KrW߀|ExNyn@߅BV5ջ ]x)[,<:t6ᑬt7J+&BZ7pC]h2ehTsE9塡|(T7Tj +:U3PŸ@7QMVen)wr{q]yMWՌCkp^øZsƝ{=fKm`f9/c)QDy P+Kz?'\z?#qnU듸c>;sC V}ҮBܾUXfLXD%L3lw`φ1H6G[g\qǜqy,wy"y_sW8-q;-v\#,s:Jvv:;9:wlqfLm|N:h{u A!8bnqm* +[u_epKؐ**2,m7֛l1l5)0.7TJ6 W\:dk\^V2Yg`(vF#9. % }#cwJFscS[ŋ6-X f%YZ=_ڽXU9 ֥t'+mZ#PM88>(cEV~O8qT oDѺk6+Y"ʐ-ʑg{fzmS,maeye//L:}?>4sЬD}>͟Po ;`k@xry`A1Zѓ٣L2eit,ET-RBg}[~=h(1:ӑ (X2 d 3lj2|/N&I I?Wbиlqr1_5׸S3Xejf<.iSGCp" +80(ٯ[u^ȉ̘AȈҢ}QXqR9Ӥ'S E ]|j)ǻMk"-&1sT?pjPEq췍Ҽ3NZ,ҿqBj;(v<.@0wlpvL8!f)xy\ԨLȵ" uyGEuqwgfd`.誈i*e60 URUZb2XYK(nQ@M\)GO-hknQ999s{}b<31=uO\u]D1D[~:s[<ס='ˍykP0e P0I(HҜy2s&3.N#56CiuXShvNޠGGp>36o_kE QY|7jdYc?4bIQ4I\tl-4 6)1D")!ΐc/T+b۵ \z/NFŋ~>\3T`'ٔuy%&G,5E^rR!+ea򗤚a6IѶE +$}LR¤r'Vaܦ 7w 3wY`%Rf5Q|'&`_ԥ;I 2ۭ^8cGbt8Nşi kܭz a5_b[7 W`=.Z +׆]4T[]Mo:`+@. +L p? f' iA̓0 8 ׃S +{t{Ȁ>-fn)Eϖ:4@ro9tXr0y TO&`R3`Q19*hZ]nusp2Nm U{0C{2OAy +vP7A%PJ^uqW}@w&cN7sG80u +p>-*ka{l(H/xArA$upup}DwPA;6yDt3=S-iw8O.ձ]#Zr_`HD)PY^K:_KFn )kp9}5O= G; pKŦ@ ؋+p By:xyDM?} :[KWO21 F.~EG+#ɗK q_po-~#nT]:˪^nb +8K!N>C<O}'iǠd[[k;ϯEf\ wNrgũ!p/394L`""}*/@%Spk6\KÍ8†NQp: +jp2`9Nű yy9t>`:G}vm(/cH?5'Ip?P;2z4.c: 'i8ڍVW0.bfzWt[=h/ + n{h˸_E zyɓTb5 O7?OEOHhq`t Dg)`Cʘ!]Zv{*vkphsѦŭ!CΉׇ7OZ4gI{Y*w}? A/zPg&2S:Qh MP3}:5<@SnT6hZ4uuqUҠ%YVkNq+5WSHOQZ*HyYITꩤމ&8biޡ'H}1 +"'b{d86Gji`6D3-vv]m / %^^%^DR[- & +[v\^'_H {BWG7&3ҿ| )-F{lM16ͱhIbƤ2l}C<@,L\5G$jW3NpZzfyմyQG}*.*D=P̎NQ-xyO |JOi:D'mxZѬSQ7uOg x $>[TujZ*W+F^kSRΌUighUjP yj9n/L]ns!I!X2)!K CmJ,S,HNe'e%9ĕىeܬJiybLR^[(TĻ|~$A& \9 4{IF ǪXnZ㻨1b12h![\npI%\BZ_ +/7+)ەN}?&zAI(^s?dN~7_mSJ<ñ TMAeZ$Sa2s +[jfKS\q7K]*Ӵ'Ԥ1n},)F??2 J/W袙h!kS.s(N9]Q;yIq#IlĦ3Ein8U(1} +$pGn?cUk(b,% J,v-I.. Eu݊#ʘOʘA'GHotE,9g0@X3}9ݓT84ɬOOZqIP/y_,*ʷ8o{PzN-gߑn1>c ӧ#% iJ-,KRĦwIp^4;D!:gk{Re܋$$ӻ0 +Lg6)C8cl7FgaTV?x B\,Il|ଥҨղYeY&rM"<'"*WB[+XIYIoR٢M^s=\wD\C5`0D"83ƹBqfL7JHCvKCviH#iȆe!Ԧ.e.I +^ ̦{~F`8[֘B99c@"u(AxI$ %_2JF_Tf!شzS۴Ne*Kv +PrQ? +_H +\ la d5i!݉tOR r+ZJWޕeE9X 0e,sòb 3КB[m(xuQ!b#IY}XLa[8 l5N /xF6#n7LŖ-lڶ* joAe}u͏Dt##s*g16Jҿ< pnPBUNP6t>2 kgBCfQttR@Z| 01O'06 z? 'Q@86!=Õx-~ 0h兠V>xڸ\[ 9/G0+"<5`#Ha 8iAu#y㼖➼ ŜG;/"WX_B_-'{9ȍN2I{F(;޾^S@y\|N u'^5Mw6'݁t$jV; . ={\\ ry =f +0^-z~I8m|E&w͜>ɤDtح;DM"P2$ydIOK exJVғ;؀DW!-tUU񭸆2Gq?"G@ο\!/"o™89iͦ=zГQ3pkMrpqUFjFgъSm$3‘O"%Cpb.đh8!x ܒNuY"o$[ TY:Sf*/G|6Eр&E :=؝~@JDd j|<\5x]7\uK18 Ψ)ؐ<޷=||E>86pcQgvǡJ? +`>e TNXI(ћ`Pl67HCNI6ܒCHrCEIίx̲\fimc?p}a2lEa$&4lLY(6COBao/}\)A55J .{]8..]n r[ۊ(%*XԱmSӦi3δv!mӴt2M3Mil/2f9ߞ>f&mJ`OfO-'_Ʌɍˍ "ܱj}6p/{Vp\qz܊5)hF+" ӚjLjIMs"fÙ!v43gNsCܠ"\4wYSe}~@DPCܦ+;t/m"hUc*7'sMέdFNfXa젶 i{~0ק=hgnK.UEg_  yyBrpeCmnNSgcDǐނA}ۘ~#ۥ悺\noӝQ ~+BY_٤+Ш>BIN1@QD,60aL@ش æ4g"dGٌt*tUVG5U~B$Zʜ5!M^Z{&Mpݵ6W&dw*&]g] ] ++"\F5uWep2CiJi +zE{RpqbS#uEuSnCw}jwςb_c٘B5Y3xwZ. +וywy_sjJ`&FOy]7Gif-PO՟ +f"1j=d\?_T䴼n"[n~i~-J#0GLQ;;ZPŽ0wn)j2@eE~W9tYV2s܁wyu65WGyu7HJxZ)st~P@1лoA^nhRqp@P>CfdJ U!#:¤zq65qMMKr)=kJu.ӞF D5-ʞ[ +d!st#2ƶc8ia=R|+,a_؉pH0ç] M&)|II74eָLZqhcq=dLO ej=N'$$O`fbI"qH+FB3sH\3oFH28O1p#Mύ!Z-v87 dRLL=e\,'`f< )H\8شhlX|s/#~qxqo n=<9) Ch_$uh +ПfIH^6]p) D"\ށX"vŌu+XEJʕA|-p~I|gėPG@pS%gi9i%ҿL/וP1M}SQQB_CRJSNhHER !|dB29>eȲZfǚ550żmw\]繟њ@ҚCeSeSE؈xxu`E D994|Cݬ`@c\ 0^_o !(`$' NRp>ٜ69mV<Z[9ɭ u;yr)ɘ+ƫf0jRӢ P676 +}@R;Nl_lL:X;:8 u'F7yۀ܋ouQ`= PSy +*_6XAEl<sDw' L7;0x0ZSלV/R"ȭN'w97?=G3sǼBOٌ<݋*%_꺑aуBFqd$$o+%9V)6 *5 Gp-'.o Y]> C+"/NyYG(2ꬢ:lΧq&9[<`_Gz)s 1'#`uQ/Z)ʤN`uSJY1ks4r.f~/Ȣ!ӝ7:WGP3ٌV"uC.b,lN%3_gpqoZ躙Y<8I + ᧾dJQϟ0ԊhI>K\P\͜E140M\ts :k42JC斒e.F` J.$A"gѦV84%^:e^.R/NZ*4؉zzu uawc3vE= 2,wwȍ>6^X㴱;MI(M"pX2 ʏqPze6>WNbOr۱t?63/QmvU揱-xN,+? bIaJ.l?=*q|]?o쵘ݖѨ)nyfQ%*W`U56YQ1 ^GXmnzxj3%Gyg{GFhh:!m3;m}PcjQevlMv`7v9Xgka}9VoE{X^nA+'C %bY(zԿ=}bi}z5 56t38zc?*ð1ӱ#9JYXR<,u*BSPۓ C%(Cg( +<'bg$LCRimE/R. aǜ|+W㬰@=ծL?2ԡHWO,TcNiB:[H+~vHZENһ\b͡\l{Jza|7[ +Θ_!90IB|B\`/.> \/E(TL : <&JAigV29Rz)d>rIv!cv RC⎙!c1# !a ؐHĄ +ѡIBTh0=4W -mSL +'-kzaRYA#[]dK3f H0$h\FFhM04ᘮiBdx0%|09_#M273(Bj+7& +"0#2L*8y2T2E$\Ct2_ۮfFmo^R=|yߔ-}ԋRR-)>Ϝ"3*{$efEim4%wW^zQM ʶ&fPndXVL#1Z[W,2Z2cI>&6j!<@ۖh!Y>q4M`,e,x 9*#fT{RclO8MIE,%eF+amڭku!u +j[5Vm8պʭVU_Z-mߺC[[7䷦A-Vsy\۾HJ1eRl4^kЯBnfs nԢ:D~aj^)K`eổf+]M"ˁ샓4(-wZ^;ir) 㞓nwF[Zi&sMk.:⽏B8jijpkxju-HN~spRb]05g9#э܆AV +xE{M\0pvƎ4Gh 1.::6zIù:bQG, r/ֱ>[#>AVG%h8ٜh[mӝihml҉GccPϡ_ONIt=.9_9%tzuR glf13] &;Jw>%}iBPWf2PWIU̫8rf`Db405nt;xZj~yl ҧp>HKo[ȝkrf>7vߐ@a5쇃L  B,$B&'fѿHi5\Buz}M=żtC:~5V)@C(M44sћ 4棱XׯuSDg-:XsE]>c}X+]`F>/jh   M@'_\h(Ac{)ezO=lK59cnE鄩zj>^TPnBHȅ@B.@!B- !"BAQDTRuκεgzvvnݥ]9o|=y2|k1;nmmW׆p%f.Ōb^pe^wqbpGX}qQ,MB!R}[;q+67Dĕ8.n0np̆vr|'p"~b!MX‘8p1Slӛ+,ejwѽW6\ڔsxiXJ$+܂d'wc.#2-޺[b_77 I}O0vG1QݟP{WH{1jm }=]8c,Pxub'k&j~GIF(}Ls1.è( Qv 0 .E!qbz]BgNtW2)ZXKM於C~ʚ%X$|@5敏)pS=e勔Ǡd#$Jr#K*C@ZԂvenx:) -f$sSk48?&"fE9OO5_{Hcq2Kc^2F9)_<Ay)(ѡ]QVE9*(Mp+Фtl ;|Us^lcQQfU=|ƌsdL3NY)GQF@:,xphRRW€ + WzPW [jƙEsjN1Ǩ}|H@1jO'Pݛz49D&N7@9z_ЦG t|4j JQU*;,:7:?L>fԏ /1*Go6Gg!=GrO4\Q|̒մoѽ =?eiѬ_> Tx +`5\@mj#5uiBuc:NVefZq1^Tr*L#NKT֬`o+&&uh<͔zSy(CC xIe_3LVe(%dtSV@uʹ[hUIѶ +X6# D(ЯvB / ?02xmY/sd?q5iݧg&#?E^`!! rB*d lnFf2SH/!-|H }z+NRi2Bz'6@m W7Dd;灼i06@0{]1K%5edX+aXo/m` ƣP;612@:Xvp {KO"ޣbrϯ.˥*4q~d%dԳճ|$$;G؍=g~Iރs{Ecpnk*>'͓|]%5!qw4V BB%}lN:PBp$aIvg9s~DD$<Ɂ' =Q%_BfjDd{=YpN')|FLN3,19%]`aB.(}INPWʤ8xd8Du:1>J;돟Ч[o pp p&t;1({@}>np/p_b?'v5Q5M+[4[Rjcr}Ǯ{GﱷEsS{^ =9כ `.AFhC+qrCH_i!eWk2[EB ;ɳtUvZ+~~vuGMD]쟀F3A#bÎZ̙m e|??[#(FXI 5hHKS?(4HИ9hb4qR<_Zق5b.@dP+^?jFؤsbguC |h4)ڏ$/{;vk.rrkmOqR-Yۤk#ވ ?;@_e.hza}D Bc>رlÎر;ꔏ<-zUv5ZVJ*T)W x+>hv@Iy _bh4ICgV)B^fUllF-n~TTj{OĎ\봷hh{NC-U'5vk}?UV0īܐe-5LbCfaJh*w\*v:"*p~9.ϔbUnܜoGEچ{hsVDh_wTb-pMD(9IQ&1S9DqpŚjdt/ a44ztc-Mh`yн\g̣:0+*"EPEaeXT7( +.ǚb&Zq_c5֥1xXҨZMD? \{0t^>|߂3s1TG9y%41W1~PŌV1V$ٍ6es[2͔-WJ3-WAMJ`?fr1 6 k`T78bEqgO9+h`U9Kq&(%a,pFIJHc0'+ ?:cx#%S3=|K!1'tTN쉽/[P%5)J)Iq$[d-`.s\ŧ<+SJM2ZbSI Qg[)#Si)ZdQ5DJH5ʜ4LiK+Rm9-QtzƦoԘТKj0;1Ue +v~ۘ7m]Č~2g V|F2-cY1YVEg56@cehKoPxve +G +r+^eti)̇ ߶LUvyWMP*սj4ʳrUS99~nբf@ pWKXN/`^ @8)a3/ffl^˹-~**uv4{Wnuٕ-thFt2K` Py;Nn{7M.v77\ĵ7TN(WRjgpG˽_&h'?mM^!A918P~!0qGBAlp.|7ݾ\Gx`K:9:A$'1 +G f:GMhI ކ* ]{.CvyH8ZZg8 U|J}'|/Fk~Eo#v{n;tk`3?M—Nñ=]|m--M< W8/t?úB9sIm|y=C魇 +ЏF{ok:KkOB<u:=K[Dp\џDlAOЕp@F=+1ɤI *!q|@#q8մNjB)odJOXWGta(V2:h䳣:FGqK]k!*WmWxvjgvBm1<{/H.ΐ}"1++YO䱜LYvNATúuLM&آMG2ӤO<JpW0`6``6` +$&!IsM4I&kf]zd=Uuӎv6դQҺN:mkUv޷dz{>I}R/xW%^սŋ7Zʥc:\G&dQqXtS gb"㙏5;e2|+ +O =.V%?{ewV,Y ,de#l33a*pN79nek4y g((FsP;."7)R.JŎ].%Yˏg m K(dXѢV 2X4Lq턶GIPݦ2=Ke6ҿ7Q׾H_Ny5K/Ib$SCrM6MNJ)&X:@w8]eos[<7C_kҝ6GYyҾLh_Fͱ 3k6Tmqeioi⧣"D{(Uh:D,xlO}fۯ_\DVyFWf/k\2,'XL5v IM[aS4,d +48/QxKEDd'{VwQi> fѩ6n5zqmIޚNuk>VֶJTzx#f(-Q[仗G~C(7_eJ"(YRZ X;TvPљN3eՔ1[(80EQ`#.x O~S +U..HgI*1'k*j;ʃ(`KO>=&z(쭥z MIv Y =DFۤ~&~OF'dDwK렴ĔDPKINA? L!w("d U9@pA҆GI#ydGΈ$ ?KŻ$ }*wJkYEHM%ZcUVQ[cȘ06HD:)y$OyZ'$bcxMćOb_O7xG?#~<Ši1"ѡ5UIJQ٘U!}z I$m8Ms`/68e|/Hu^dD~@cL<0""2 +* 5"(Ȧ(( (8* ++˩₩1n&DQc%i[5ǦMjԨI44>99=Ǚg}T:++Hϖs''- ŏ;q?>Əq)S&ժt"_u~uyzYWz+TXGO~>/~طb-v7R(=zB>C,N)V|^)P+[]G9DFx!Ngu%yab Qh@#`52yi>ZUƏq@Vf*%cDuX;;M,$ǩW5Ġ1 +㱟LVUG$oV*V[rcգ_Ks4g [{/^g A' +hEc)hdc)E +ZV,""[.v._iswr# kG>>wpelwUSVw JhYG%Vu.ZꚢZL-q"|Y܊TVjZ֤y-*s?RwTcxJ1lD%G(1,V aAي /иrF,؈lCuR#~=;iAo m +1 +ǽl09C"J (EӸъQ)5UkTtFF[4b0dǼa1|`!vS\7ya&po +K#.M ̣>0dQvMvD}}GEepcĠ`Ԉ\Ƹ5qiFkUظ/MjzbNs5MSTk7IOsf`f{{K9YeIPfRIIHJWzrҒR%SJMJM]j%7)MG`A,W}́z@y>9JTqd2јTI)& +`ҧ)1ݮEXgޭX`>x7e8نV7m\30*ǔ.SR3(;[9%ʩVdLE(}t jK4l)w)We 7v2l {Emg6k|m~sn0(z8E BװH~Rr_&,K8p.+*.]tqíAaa= Gw1]_5 ͩPFs([\!k\>ZiJɩm*si-䱎jb`;6{[ Vf6SDEVFr{ 6xh$2.c}cc}ǹ}7TGKH1Ia1y5빑oz v^x2 + 3#jrK y36 Y+0;g6~K8N[ u?E\vih2@o!ނ18I59͌# </W/RK ե e_&*F;Djǒ7pjY`\ U +\eN>aFї2gl MVżuؠu <=w'-]U'mu}r uvxa}k}Ӹ_C<ω <74}tWE/JD3|t*Ш-6KANw}eE|y\Y"qyW(29?9<{=;BDzQDJ^Gt<ΐ))y|X5<\i0w|G'X4HG# |4J=ͫ O[;i$Nb''sqbױsqiRM6Z:umU]K+T(L\Mh B6&B Ć m0ډ3??~:w}~{cc/V0]b -|Q_75O op}$1s4WG + :kѡ\i5ϫ~j%?L FX0i*\ъvif/hGɋ*ɒ5Q&>d +eEi׸?-Ye,-5jԪJ-ЬyC =ij׌!ƔiM5a<NjĮ1]ר鞒ה0F,Yڬ^FzЧ}c~,lZsLf1;5mnДEami21˘F-Jn\U c>nzRqU Zju~?>./8l>Xz{f,3qږ)Q)&iU֦-,xwnm~LъksxUa +WާyEit-<3M2s36{f 5dw*nנݧG=9bVr\Ym(TO5wU:koÇgZI"r=I8ce^FːH_mUPY^g8[R&Au׎*X;EuTo͉[=?kh=Rioޡyd,}TckDָTߖۿG.Ljj7T2|[/iW?ճ@su~NB/ ]m|5j RC%w{jc@霑sE՝GT*^eٻހ_p7ά濏YjN~#?yJ +ZUVnTTeOLCS-isCO,| $[[&[=>Vy54ИVA:R#Շ}…rn1*bQe\HnUCe٢CFS]C;'Ḵ{Mb?9WY73hzϣ3N Aۄ%n໣RU_*KT>`RـUA6 j`#e )>s2/]_SIǴ0:tf|0^ B-'F)ՃRuPAED6$dLeHKɘJ 'w([*H^T^r*7C%~(cFgJ D H~hObl3ɘ*QIʤ&*Lժ ըT@aتuL.EL%*Pl܎3% Ce{ˇvLJmϳ?ݿ}" JH%:bv̠RѰ$H@phĈ`ּ>5&ym xyX{g(b5 s/w)1WΣ0JWAJc6ԔG1 #uơK?C"<˚eße.o-q3<{>Mzmx_ShB?ʹ |5[By=g®r'oϳ.0gK2{9 2{2r{ 8|oaׄnZr1xvfK04&{CYi>>椏 ~q>J%?A۹B>zƸ%9j]cF2ur9ACa?/~곟B;i8'U9@mcAg|FW(ćW$ ^~Ea{3ظ!'}q=/XRl $Ip.G&& ҝjKt>oKOlH1ӝS{7$ۘ~S M̫2ґZv>Ϫ@VOS;tF=ğI |ݞpOѩye \0]׹ i"'kL>RXf)'Z:%t,ev+-H|';!.'v5LqTa'&3iB/mt9.hXIdn9L?Ev( ,r5^qOCr1/$v9u&q'-[|c!.yds.3: +On1.̓ي +U2E|$E/"|,||\q7˺LOgTT2CeO8[S6[.R^/i8:4D# <4(GJ31yJ}P\M曓Tp$:`v [6 jV^?!=8-:qHCh(fSwԫMԡAS4>. Y2a ݩЃj +=!vA@{ql5[=0fO53\6;ܠICtgUaɚR{Xi Tkh79|uq 5D,P}JEnGBTaT,5VŶDٜ*e/Hy&)7U9]N%}Ik2*#\gsó֣T= W|$^h)Ub{ +Fʳ'+מle'I.;FY)LTc|Pr:#x>3zhL9eHc_#yVR!: qq)ˑLS,yJO-QZZRL#}R\ z@IGeǕ6|W<h5 ћȅL|}^d+ +W\QhŔX]tȑ_$4(c,J*t=TO\K%7MEF4 gR]AQg]wEЪ(* +-, +BmăD3iFUi;1&ͤNkNc̴L56i֣c,d?Y罾}FL+`WJQdv|dȕQ Jv\*C ~;+ιOcqX^8V±`>( *id_+;IFYIJdT'[y*u)ڋ'/ыp| <<_h&q;(@1τ;~$J ~dʼnJ**@0 :3"$ * !yURxP JlL_qÿ~Llu1JXbPt|R.Fz#ìCH Njų#aKgpK-/p +PH9ĜE̓}O?/Q_µEgKO F+k+:w%KF.(\/Qu`;ϰ-DMT\~vPBsy&1O _?f4`9VAZM.?Ppxs{Ez3r [d!m\@̳p}jΫ)$C7XlaX?X6N`LM6s6U|RMySpw+TQ"͡|ի^3uK a·A? XWY +q/O=r, w}qKCM~'q~g<>,O ڙzb/ku?#|agD:a/Caq0&Xku7F4(8!8G䠿&M sA ";`4"hu&x`x?NsfO8)w /:r΄;M6HhD9pɈH#88rpu\,b%% +~O +y.!MwAQj@|ν:+OQ8|H❧I~E?"sphBp;C->Un3o>$}|QX5=:7j ~{=Hj=k? +Ux3z]W]Rt+pk>\P\fFi3[GP'^uz|:z:~CE0-{/J'i : A ƸE+Zd$,%ض㷋\DKè!A6]Tyxscu9/pޏ#N[f|a +Gb]m;V]a;l/nvS<7v#dr EA+|2;17bۊtf.v#ʎ^DZ=B]F yBz}d%,ã%2vb\lQ*'a{:sυ.#U{~=7QBy5df'ީ~.=$8#`; ۓ=beد~ ?:CZEKo +rzSL9q,Ǭ`#vpFHo~:b&'2B". +8p@wtұkuԣj .3HxU32_ Vq G-*3VÑG&ȃceTY 1GT5Ii +De=G(\jycm+U5qr ?'L84^zJKXk'/SIF-6X3k,!K.l-HWMbHQuOzU&.UUfRqJL/tBEp |'6\p-^~w[62UcJӔjTM3Te|S**7WUV㖫hjͳUk}Eso*!=pm`cmzk.|q⛃SbUeMRŢ +MI*NS5[ֹ*ZS;IyW)7urR٩O+fL9p{HC +U |w*_ԖTRST:A575Kslʳ*VDEʞT5#}2.5-cD55,! ¿4`$|e}oJx  b I5AI*;œVYKfnVbUQyUۺuն]ﶹ]n9 d'y^z|*|̍W%Yety-Y*R OGrjU(Ek +&-V_vl4~PVg~”߬8Ki̥*PfYI(/TzT) jhQjE'Uo@ɾA%;Ċs2T\*>W?a;Rԃ|ǤJ pϊ|THپx&')ʨLiԪP*JnRbuDŚXFwlU|^U կ+|DUݬmo W +TP *1Q +<|.HF3ńٴF4P(NiO;JN3X3.kᡖ&lAĵ)0(41{$f[3K7E,^mfv)##ψvl/ dx:4z0^oQ&R1&J ȵ Ny=/亭Mԃ>!g}6blS|s>imd7yp.]6E,`c 5YQ>9fq/r9br9c/[yfg0% .mm,o:HCYk7f-Pl,`'&'ߡOQ!zt~"'(~sbϫ5*]Msv,!{_3hl<&Bh-TlDŽ0 ň2=r?F(8 +a:tPuVr4%-|4.F&1BJg蓳q\E?OAr3!pFpvR#<+;<au:Qx\(.A]6}fJ#+{^8i=syS~}=*:+G /P]Wiԟ%.~J~B.i\:ops0^/c_>Q\f +4G5t̻jL?~ʹy -JCxЙOEh47jvP}hũ߄3,ji0)(' +L5{ #u̼M`pEWhT՟W<~`;۹v0Ŵi%mx} %rǘ as9jj=7{L`e R5:%.Z;}Q`O#6Zm/u؞{݌VlEݥ Te е/iVқbX1\G.t욱k.l{]Z쇰V+#]Lb +Y:1~6ktv 5bׄE g?RX a2)snM?ӳٮ:e05&9(Fd}{\,XH.&=Fڍc~t!셱ۦv,n/f:z43UaKH}$A+oX&fp:9/:jQ6LC8JdRruaĉc;ǗN8NvlDZs:M$m״ K֭bBJAVSV1Dm0؀A h*h6&.ZQPG'e=:3Hì1V*f젗 c%Xz>A4lsGX 㰔gKH ;;Ѩ$:u42to>.& zg=;6%ʯc³x/U|8fwcniL".|5ը\nsL]:Yuv0-WxZ(m٣fA,ǔVr vM{RaG^{jSWKVZliDҸZJJ[;lWʺPɲ1%(n۬mjS" +ۧ:\G8N +6 +CC7]'caVDY]-vJ~%uJأjw)UԱ@ 1E(llVG!~*h<G +W^k[KBzNUy9-粼u7 ;\MZL3v@gi%r1O5m + ջ+rW]'OWT]HU+ީJ.Uq}\Kryr{oj'荓@.pm4$x#FE[תסץjWU DJ[զ~UT㟐ۿA |EFpJ偋rPOtk#Z!kR]'D~vy*婩&.W0#gGڅ2j<4)Gh/òRYcm݆]h44O#"YePP\u9rWɨUy}4t'[d"kdlUidJ%#DN *\d ԿEسA,$!=P ˀ91B4B6Lֺ"og4t@ mM@mݍ>T𚱮ib8d6cLll&qc|-0'3/<~w4\|tzFaɪ{Yנ6t-#Hb3ı8VjXCc1dOT +3oce}~z.hE75L\=5-Ch,I5$so%{sIFMı817v0&;XTVfH3׆A!s++z ))"ö[/:@ndwt/ ıv?~ޗ}S) +kyR{꣯s"!Rt{^sk^nh +Ƃz8K!Lt?I!q8feep#TxplCN.a0UXR|e>oH])a0K$SgX'0ٟq%=y2ղ1@ۏk#VR+{ @^y3xޔT'Y{.o?$ %KE&<{ŋsgW ml}y`}ò{ސ͚:Lm`VKs%O,~ccl:W {ś4썓dŧpO/yC/s /d"oGG,~~ͤyIKLWW^/}_%Կ,jg'ހ Ufyw?6sZ) +:2qӺ{Esxq~&̳gcۼ8m~v|;׉8iM鑶뵵)F=Cݠ$@cL ILHCC􏩈C$PP}~{<$% ݜ73 0(_fѯ=MgP^ O߰y ކ!$=~7V!Rd cse e:#h$>+xyK+Dgt*sB?Lm* у_u]S25t,v#Wȑq?>2S{R#aCdC/6k*< 3ϋJ\;-[Cw6Н@wY4:0 Gt7)T 2d V9-hm[=c0g!X=GG xl'[p3=ѲЄqQϰǦ![[-&v؉c'vة`;fL$GS\VY<:ށ(Na |ayjiȓ*ʝʕ3ݔ=$[愬epf(Hicc{SP2(:x$!(*n?/UK/w6$gGL*)r F%O9s* rg}-ckl@%!4 +AhߌM-]N9K-uma*$MG+],ljj@iCePPo)CН$PnNS!6J@e4U6]?MS'hu>[w4qu:@zJʱ{-hAz<2Lrr®Y~ΚE~A!ah66@A<0ǀfq&m&А ឦ ]` ta/)q ĮQaE{HYaNaV6 3]Qg6{9d7l[ Pb F +*e(P*SS +J/Pʥj-2 ʴ:ڱj 3Hm-ɞt;oel?V~YpYKbr5 c̉ջc,NY{&Μ&38]p~ᣴX,k:gHL6}?ѯ' v ?mI[-~x;gr!q68wsΕΒmQQ]·˨#rs[ 7c?}&{vdVĻH"8sIKi&xA;.Gd##h^e~WN0?HH3(qe3~VpNEj'[ٜ;nG<$H9X< WU~H<^W^ef\. euqDINۿ^p᳹ϏU6K<`,D$+5>>ɿJKb&>f- | +Ol.>IQAaM2z 2zQ{u΢k~8 p ޿z]uq-l$.%~u9Gem~~|?D~bz":'~BiUh +^VXe]SNڟ&hq48Zj%v؝lj~>^n.NC)u}v!~D_v<mv\pǝ;vd`IЈ"v;;eZu&v;#bl/"Vc(p< 4z"%kЙcp_/;muiG:ў؊ @ENA{;ӱ;arXeQÛ rW+b f8S a@䩾";=}ll>B~ *YoaT1v|*8=ط{Lcz\cQlz+۱ݍ>l`o6 ;s:>GNU QuCt~1lEоkپ Tc ~o~;@VdjYdg:YG-e:5c_ ;~σaWuMC,lr2ژT2c^y;u£)TE G7Y.wmkUh9WJ4fy$;B5ur%X| EΊ}ṗs&o/E̻,HK}ܥx#+iժDb񠂉jO˓˝lSMG;lqf܆i I|HbxSGdQh- ϻ|Iy"QX+3SD~ +& y24Xr5 9gϢ)K{caq+X³Yφ$/"\Cedj(fsI>'ݲ=&=#U0?;ӼMvū_nF5#\O&~mXflؒ! ||e6;A+h9/)>O&d\25 +r73D V:HJW xmǶAlcoC%K"K+>|pN+=`hiy׀)ޅ~F5}faX5 ZZ" +nUƱ3h:Z+neJ;=HYB6BIH@P !Ѻ/NT;նK2x:ɇ0p=!?}f^LRpφ`@Vr@G Aw"0<A!\ŜԪX<71 1 '#hGw_C0" 5m ṫ` ",B",BPGbP !BpS/ t3Ϟߧ"$/0` %:BrXa`F6;XApٕVb\r>i:_PK -:G/Ґ9c+.q|h"|X ~5.5uбFl 0a|x=u04.zE4)x C$Hl- yױ;'jn i\ W8tl-бk؎nA pNlEMlaY6{ר` +Y;y80_w97=Ecg@Ҁ= бQR$Ή {P1j` B΃Vݕ Yk`Õ(,7U +U+'F|` + ^EMB@n/+iQ'B/ paT/D;C!XB"0cr>Q88/l0݊M?xy~n07|cǎ0q)SMs^(d^^2l/WYn_zWl۾ܵ{"ވ־o|#G?>3L6ğ=w>1)BY"D-U5ڂ¢CiTSźƦffpvv]|nܼu}ŗ_o~OD%}y1<\'_ gK"0X8d$ D0QPp)#`@L6-F8n#mO@zH(=&c̾dݽz~x FEyy = % G X'$`(,K?W-=C o"[ ;=Qo;p0ȱ4Ï?!Idr -bXwAWM1 0 +z޻}_>xo=z;xɓOkMuDT__ba~CٖsJ:CR Z G#e&\WfHKi h0a@À 4 w|kfdKeUh_ݯAųs94HASe *g)AxӀ n_ToO*HSoTb.W]ޠZA Р%4(ײ3n膆>nE$YL!`*_mԝ/QsР 4y"ySIfuaƹgc,i0,5pCu~S9Ѡriȇ۝+]xWY"Z:ӸdM3^Dv 97V0N6CC4N۝#>1tdBG*@C'ie$5hͥotРUrS!\ʖrz$N:Ҡ#{脆ƒn#Hi КʷkJ +A˱)sNy6K"cwgI=q:E+6 Zg +Uo-/4CTРa;rV(ՕБu9'_4qbf՚ *ʶ̅ڸ|5ǢT۳,8Ȅ#Eƾt^鎗{<6XjwУ-VZzQQYkF}QLVנϋIh4X$&}49߻w?cW{YE˫}?Q +˱lpWDL|rV\`ƉмVmӰi4l6 m{Pdžg0|ǐ0aV]ց灡F!ʺ[Kn۹l{`?)`oh@lǧ"sf\޼-RtɌ)Nm-në= +5e'#1=0htHh#EAg"F Vh•Ibm0;;6 7`2>A :SvIQĢU]1W B% OXoL[n` `Q/c×hޫF'Jcs_+!DtU3(˗vjYy`xN+1™-x[VJf AƻC),ȗfjkۭTkëK/ck$fLGz(6lj;^i<)7m}Uɰw>&t%4aS&Hsĉe!e;l[԰0ݸ/WioƮOW}/>{cI_ᜲks,p!m,g9@Ov.Rgu6A$Ⱥ[5X=ښWǖͯslwrl$&";$&,aqJ'=ʲ[_vwMæaӰi7X?ښc˖9_ 0tJddD'%x:,&rA>'>\0EEh`NӽGWpkz^`x +Wc"R,Bq&<$Lci7_uA[=kV};Ǘ/ b$fǢ* ˱\PW@i.wEfx΁HmjiHW#-]`0(̩ IHL` HwEĴϙqrxsvB@E͌:yn8~ ^I3mfځ6MmҔK IJ qCwI֣yGﻭѾobKl˖%[^ p(t4uU}\?ɩk3Xb?<1{B1 )ʠ)u +e;5+jK״4Œ^S5x{z~q_=a8 ie/ŴxXj(Q@ӨʨVf =[rSPԤtuEhx{~ {/ͩ0/!=k[8P&ڪY V $7yMRULMogn`##4n%ubD@tPf*haTIȚ^ʸ,oe>OUq x -"8g3h.PԗMЬ] U,*WPW2M~K(d+\+x{ڍ^o_=NioYz!pg'ئb +Z(e^ik{dEDUۆa}B{_k_ӜB3sޔJ(6y +%<$iCPMAcqd"mnf:p~0HA\^0K Ì*QJ 82Eg`*)=P3؏6r[h/w`}o羣=\[u᣻nj:|ͶoZp7ȗ|ImKu:mlB%a50as5ޱwDGI^{Ivx\/$ٝh cD,IFIdB#mZ47"TՁ>m3V?1Yiޯ-:B}Ky/eN(^, +юd,A#$9Z6mtoJZmio=aqS5ݾ|OӂSacO0.v8hx'#TQ*LIHLʆt ޜYޖ~0˪a +аm=ć“!A)# xB1B 3QFg2R!@ R`, }owYr6[iì+auc71'R 9#lD}qNܱqZӝNUzuuk@zWEAAP I\Bx $F$@BȅpAEVԺ9;m-ʶ?*9M8bɢv:jh"(VV@ߠTei4EJtLpavwk}n䅜4~1+=n*(NU +<L;sYINiBx6 +_sZfFGܰZ)HB':!TUr_JDot$ H\$\VQ"Fa]|VaG ^j2#(Q6"*r*&!i"$]0 k A]0ݺ4!>DZр/rz[IV-9~`qL45z]ECmdULDD](ՀOICVt^DA$"C V[+{$SL:Q 1hG 5M|CF^kʇZx3UAPi/  n҄di=ۊ~i+zd%C6@>k\OX["d>Еq]iB6gx;iذ% +gd9 $*MM//uxUakfR2ȕ\o`*X( 0,OƤAq.<1*; O[T{j8lQƒ .3&Ba:A8/ W=hS g4IC΢/}ڐ:=kJ]* *8l]Kh-nH6j &_ciS 3Ҁir`xaؚDy]Mݧ 1M&o +Zr-s.j)kjTAAdGO۸7`pHGܤM$Հ!o?f*wm2~\?h2b۩z2lnʯK @1'TYY0FG)2UhӲ4`^2nK֬f{}Vm&pҁ-ZwZܥ5UUz(ԦMjJ m3GrA A%h4 `Z ЭlVy1>g~ |y؟~uG? ӷmHozޯ'|%:WS 8#^87Ѐ`SӏT]=r{L&u~C*gN{i%8 dp?3 x \aheeh jOy`~RMOU!KrUh>Du38lj,J0pzT~ޡ{&`jmյk˦t˸("R(HɼX&QoAqq˓2,ah6EeX=7eNަ ;63e0uOɧ4]jnH"QRĀvߌ帶la,/1 G#Œaܔ>ehi3~1k<ʞ1tȧiPQ'5D^LRMl)l8q(˛G| 0#xeX+)z 9Ys{xJ1?o'ud^H2kq2,9ʄq00|hahe>o옵fϘ+&;jpQNj$ +%h>鵐ifqfb5\Éߊys&``1k{ڦ4vTᢁ/AɩYL"2B5=+ v:̂*;\q`r!=\= ycʚqOZO:ᢉ/),duPbM97Fz\Wjz{Be7&H΋ ( un̬uyP>8Z?]'[E(fjY1)QUoh"^jN^l^$oGs4o-Ҁ28>u9Ƚyhlu^sKO3;(jzIyD. As\5KT1E7w>u>3mu *].NQ!iWcZDX ޲=7B^UtpQ+.hD2-hM;[l'Apd:d;,{OHgpj]<5jT:hCjJ +]QI%d@ [[ߎA;.}߆w[|pRB\G;A-٤}SKUT*K0)!D=eRoh`2xo.cxk{wt#;]ds=c?bv> k6`B:EM{MDZ"VE +Ӽgwo2oM{ ireӇŢ#3PS + }fj;8wym>3tE`uÅzAQlnwG6{xϫkE7]HH ~5_8ɯs뜀gq+>~?>].Lm`=acܜ>"ˑ~RJaiVUXaS/%(\bxa@ @Յ e nH\tzK?Y)ƶX f#fHvuqҨFą^DJ +a]XH:$?y.d_Y«ѶOo~~ZJ^]rrj[Eۛb.A\Ԓwͽ xYbN8ww`{-CplInF'LǬ/F>-/,zTB^O>{.V~1vtnYHI׽{Bc{C: >gώP:}$%_z^US~nˢeϪq%kҔIe?R˒6^L|,Oxri' ޥ^y/ >9}Ǿ+22AnB:@$dPɈğ?Ǐ۠d~u9;3'ܝd}/Ds;d~>O`?T.@WY4v,dG$xPt2\11 ЧO| @<(1>0nN\x??G )eUMuƥ6-k8b#S͢v횮馐J~Ү*`wo2`i(`!8):W@KD|Ъj){g3Wzǫqdq 1>, Ay-"8YhfNS%o_%B)X7oǶ;LyeT;- DA  p[ZT ͷ4zS>KkL7tDa 3fY`l^{j{~8 &x@ ?= +R7 +EUne2^dQDLr9I[M#D%@P؆~?VN8 o @A$o @ (pM@/6,qkًxդfu㍼*d %vk\Cn\ӂ9Xgh ?)lń(9 +R7DkPPqKf9T$Y?. c(w 5A3xی{6gsv` ;llHklԪa *,ђY.I38aOr791fkpoui6ٶ0 ( KJlK-Xo;_*%/K8 P*cK3\iaY< r|^|ǐk2L=>_USI;İ 6mNH OHT$+U=Td웒rl+Z3! 6?9(zI!73`zѯP^e-'ڜ2a@d#LҖ*1:HFמӼ(/J pEHy,pWt:;7 ^)m.3ȷ '=Zs&6qg +6q[ͷOG$$_py"!hgT6! !E f_+Rl.[buũ@36.}"~'>]W6SL + 1f񌒢Su<*qOhfuqi6gAm8%h?w=Oe4Ĕ=1a$P[k匭sH_g7)hv!oFVϷ0&96gtdul`5( _YT8PG]s߉5{4;~elH&{aL0Ejm<,P2|sszl e1- +?N٭s׏oPʝ~w8 JW14Gu'C0VЮ#ԫ%JFWV]R-fE`%la*2 +& 7Ym((C U5XB~dgr[7h~ }hč87w*A?:Lڞ64^or]҆Xѝ&jL/RiYvCA)Tu6Ae} +{48=?pkbPVg(3]BGiK{hnzicgXeTCP T!١} փNt[>59w#;vމ)/)+F $ev+Ӥ(󻒔.RPtSj]Τ +eGrJc(D 5f&P}j-~&swl&n.Yh)YQtвE~Nkbr[iWra;=VCjRic.TڄjP E &P)46_.K{OkVW<>D:Ewa>r:lHd(qm6r[uKT[|ks+AutpP.0Vhaf' ,լR:!]: sep1"@L)FK%tەYݑ@ 29!kZb.zۖ7.nޭY["B>ߝ1cEGC z)?"WWc{5: DUՄ/ +jDA?iW7lZ7ʷ;[%NJd&Dr'IY\hR60r-ʺ6WC`}UI$P,1oDAÖ/V:eņ-`,oY/ݱ)|! 1iTܽشDιt^73h0!-/]6(֣5~c#턉ӗR05nl:CLy! a1Q_sOq!)%5#03g!0̃T2^6:ע4C_XW L: ip='>sCa@Ci4kP z#T=saTؽ;`fVg  ;`xN@vvG! R\!pJCPy8Otغ.̾߄?m?.N8BpDt=~8+[Z!H[Ck#`X 0- - "dl2.b" >c @gaЫ\BXK&=ה%?}*_Ŗ͐iŢIbhX<" JFA0(&~> C e Cfpc/شLVbJ-?k.A7_"NDˊǣ%cƒ1;;AͲ^bYgT2Cb!,OK= yЫ7DvZC&3O&L%Hq1|4JYqZy->i':OJ|C> 1d#LĐ3ѫorٔTÛcM'M$cؚr]0IU=uf# ȮZT!΢<0ZOsjӞqkuQj-"eA` @XB$d%!@VI %$lj@AA VG;ߙuzݼ٦$DbRfw9WiQ^cUT-U3f5URmJ*0P 5ṗƝK@ޱ C? + ;61|3$-!xUF1x&(bJfX,tf(FނOg5p}o1(f|Sv/%V})$;͚F.MeםuEmvC'hQCݢYаsh],^trx77n97Lw@,Ddu,B %k{=eե:uS.uܥʐt*ڿB۷/7&V,tOmx} o*<^DAxbyލ0>P,8OkĸDT6.HO:{9F#OV{xAW~%=3ϭ/?ulmWۂ%/=J=:U|?HdeP2дpy7g3w{jd8⇃ȀX <&(Gdl1?Ƞʨ13?3vjjqBn8J:j`G'`21| ;7`&oPh1G a}C )ȁedD#O/6 P{]䈪F (䠀Kc.#KqgKhpu?ŀ׊@ؿtAC}"c_zAW;(v@ہ;\BPn  :w#-ya~ C'z6 UC_ + B 9t ;{p?*NN& n +nlw p?8_QC< +Lq;FVk)+>eRƜ%Y8ωgz4Q0kMa?M47q1콌!} Xu;1pC:b`!7Ey!%x„LiRK33oT-"֋2$+Ill2_;$'I$ʻ厐7Fz, \ GN-M"EǚT`R%~BL&6.dN(&pG~H988l' +]mE P7ȌE2&GrpI/9iγ"Szx2*}L|DjP'^81Nh~ʾ}8K ii1U vp9l Z$N0gy4x2L6AT'f=$7< Kl#&s)' /S՗@ A +N*1hb d| Q&O%xΗL(Ɠ+jU) QS4w75}M{Ҁ6D6%h'h ĈADA pm|("F-lTže 'Z88kaVmFwII7 +i~~~}FY;A2 Πq@PB ^WfΔT! sF.JsѯzJrИk8W\+e^_4 1b ,oB! APw}A"NUqSJxBrR9aC۴s%Ime]+nnYfSV)) !cHɽ_oCP% I/ ֔J zP*5aniԚ>Z*|a98fkz.7q{ʹ=O@dA (F0aDY0H R'uJP + ;-ִWSXmzNf+2~D]nt1k%~fo2 0~Py]܊?K +ՉLMeQkj\rU[kתKmVHaыLzqWb1CO@s0 +&߷uasQOԑLe-ZyUqR+ +Ygԕ[j2ZkkU6NQt.bA&b#VgL{BPz7CF7}V3GvHwVeU+mŲ.5[4my6kR-4UN#rH|jx>A2 91PRo<݂x.NW@Ʋ5΅ʃvz!0$lŜ KHH"N_Ԥy=Hzg04Ay,Ey٬,G} "}bg}OXeeK'!vD _0Yǩo"ȋąs^kJ86׍z99`t2~@2ȓCByvK߿靐E?)ԯ&X׺5\L^sv:F"ed? ƿK \⇻)t{]ue5yn4nq2ueI 1@&d tGeɍRR؞Z`nvb, S!O" +Hu rK}*e:.װ~vxcOѥ$Z"oieLMoʲ@[ F{^ ؙΜ.zD{@,D۵rZ ?8rD݁A bfL6lL0V;f`Kdp3% d 7 l+Gq@#[8ko G-x +,=j] bOrT!H4dT2-pSbj'tC>ZMISs?Ç k +LDFr$j@#H$C!ױAU&46Aw'(vGUNkp+o5SB!JbD}ӃP*CD}qIE3 aQ*qGt7Z#`&gV[VpV0wEJz@٦ }}/DІ.ݐr%`U 0j(6 +pUa/S 1f-u%o/&|E@j R|iA +~9_y" -c>CzϐBT0Bh2@EjpB e(;`uzP/R e@SWI-A+vw>o/e<{g@|˚]b={ǖ lMi24kp/70D'^' RʚBka~mg}#|%#3a&ϰ&5==-:+ZQԣuTD+ʅuBf! H,'$!Ҡ("e(U(Lx@e(λO}s7i /l>BG/`X/Ш[ DՄ.3#6'=0] 3ĉjқ:kci!i{JFӚ0#NI@Z +݀xr 9{"=qH{\v[laSBzYF +Hz1|`D>e1̦X 5Q5P7y7@?H @O< qzܻ,\>5F})b_d < y`ۣpnapE?tݦ,p89 ٹi$,~'<=E3ch/qǘcӬ*h䥄gx=?1x~M\!_;_[ 8> yȷ/5 Yt Ac|bIo#e\=;0 cÑ͢GV\_͘>؇:Cɹ>q%y?h] zjPo4L A f~ 'J8=leC5Q QI^M|or=񁬊@vܛ|ܛ,`:jp!ul,Ap#@䐏bv/f<#|`l \QރR܎V^N9OJtQ'i= +G,`Ow& iנ8 `ڹ} 3 ѻkJ&DD0 GMIT: wc;rjޑnct3:S ])lG en G `2w, oo~g1Ag[$KiPyRT'5kkCWlǷiYjl|(9Uѱrfr% 503o':M,s&[W8nR)UK]^6a֖ 6X~%dgEl|AWIg)E + b K1F|q B̳(V=1mxCY0;̂c&εk\,č `rlLjxcWʴ|Yu6NQaK:|a6.ݮX:ҝbMf*7CIC<\:W{}w/<صSS~ՍuquDPQT(bIl$$,D*0:ŒZ;NZ:nǵZP*2)UdK9}m^|^K.7VzaZjBK5}F_\c<\mzGiafӛ0ܻ=|j|4쳨Ǟ$MW?l{I]voqf"k[եm+UnzZh|:^Eh[m[?QIT"bŋxFR.p\T*m?;1te!WrΉDyjx,k#]!ԳQ>ňX&gk *Y>cȎcd%rQ)#5Ңq+QhG3bwF-!?&H#!EjZQq_qY_iRH #ܰ8΋ŊhM\ sp1nq9fG!~%d͠3Y /RLtFkӡ\Ob ICo2 : Ʃ:KayU4c&ϜBp,4? #G2_%dBR+>a.| sxF=qs@ ݄Y0)։AXISQ-~bOqp?;"s;TR4HH6•%t0 `Hp\"b4GvnM-13Vw_,Q1_@? `g]!gCzztPh +á.r=3'CM*${yCBEXtY m Rw26MV/z/钼vH?i3 lhS`¨DFf(Ь\_ܜvCrH1D%3O ;r,jߥh@aEvy7;S0 A1lz, +8HA6 MPnK|bH- z9DWUB𘂠z'~٨]BfoU A %@ǰlr2p`^cI<BW(w8 V)%$uWT5!zJ _6+_(ltrH e&f|U7h2}`t06 +cP2A J$7?OCj!L0lSAG~DuAYgV\7?QtR6?I:?K 94d0 \`Qr$TOCl6Vh%o eLpq__ӫڣI7?k~"-ցjWuDd !I 2 hQP(ThI ǭ{{̋yy~y$A'b*37EmJO%\OŚx4C  b'iݑ/f F}KF-%:v22vfAi:Oǡs=_H`0Z:*J?,m: 20% qqChmݨ6foT?'j݆49u NU<*А^ _b`406YAP24]f2e\w|D x~j&TxXp%=6s@4j rѐǓ +) [`bc1` i,p<f;/_ +|A;sT!5஘I 7X- eI$->CX?\Ij(cO3 4#76N0 Zd{߽\ml׷m#šC.9 !ƶ˜LV]Q[j6,KeDŽ =<Àd0 x9h@ZjKf{p?pjw˓S?+<ڕߡcSX8Z-PKj~!Bl0{R2Y:=,VGr=/mDP\s`z[k sBfjv,t^<{ j]7wZu@E מVET$xb%Rν)S $"B˸D5ŕhڷxHGz,߾ோ;^5YovYcS%]7+Îj~jrXUPPl,S.)Du2qrgH\&餢aH8, DO7"@@*,XSiy}-z.h umǟѨ1yHJ%e+f% b~jږʑ!K餈tXHFy1_d 9i9%FWa`FN֏oU6>\w1ҧ"6TU"Oe!<32%Q*f<%Ii#b|TȖ 8)GjD́dtm-,_tmkŃ]_t_w]|`eDAmLpfV"tnKR%q)yI㲇%dՈznLHK +B@ +6X֬6c7WG0}wv]:֋5-a9AZRNV +T#$Jđ%"\hrLǟ7J#rn<[%/sڥY-xg ~5=?Xt,S~gZxB/sI$4IŎ gj/C5z*4 F.!gCȚ0 -Em-xlۀl@З}pƁ}U7ܭ>"Ϳ{IŒ81k5Rji`MK vXQdbF0 v<[_o7l@陣UeEmz]~?hn/$%8vC2]$ow/4WԀWKկh!Ab;,å` +tYk24cGfMcݬ?Q }#ف!'Gz6⼆pq^o 7}:Y0y!`XNKg j,eUL9or^!p]/?4$BQ.X=㴞0&+Am;2]>0GzbL;Z hk ,A}kPdk-[me{Vg]1f=Ϝt{jx&{9:jo|}{׉ϾGt~;߁pF:0Yc>:̓|ޖy9ӡ7Fy:-p.]gQMy? q +.,* l!!!{ I 7kKGwKU#-X+:uA=zL[8 +B|潚|w]=hil*5{.]0wp3GN RqU"֘[>asbOn"){>G6bڸ-Gx}HY|HC4ЄaX(AQ> a@TNq Gq2͓$ߡ(2)*%`8z dE!; qL.}6D3e|4|Es262'aqh/Ȣhf3 2* (\GAi,; <As +Ru t:3ALd> 1y +J ' JCʀÄF KTaP-!DXK/ldAV'ɺ.g Ivg|[xbd=xM4d'ѡ`1IgB'^9pGCI<ے!ٟ +tNf@x&v.Ywg!>Y/yB t&xCȀ. &E [D(@/8nBܖ>BE<C!ρ ُQx /(#hPy#o1&BPPCUꓠ4 ʝ =GBH#3 KGR9 &'}HNJ1&QOn=[}KAݝ <Ϡ#4>(:qLT}å +A1(Iy -|v{8TgP^RWhʟk4Owyw:?.)4½a#*}P23L}*QhAd$?ҵj}jzoW ˦QӅQ9g0"7x&XśU@|e渱jGʰs)wtuV+neEc88ᑾx_~aKyrpf.l=tГ|{]Ċ:&N'ؐ=ա#1+mWU]GF&K_ +n[nZd(0[mmECSC-_zl/yAo"ؔ-Y#zY[|%+p2\+9TcqK?gK:-;,J/Y_8Z4h 8NJ),9yL~#d+ȷ.ͱLlK2ȟ9( vmpo]_JSMk{As_%Q{k7%γfGpYeM>'( dȾWOz4̣a[4;Yp؛=n[m .ѕ++ۗn)ztAGd9׉+eU|Yy+׾ʾݮ~.'0FfQC5&2%?1Ad袻[~mC?h9|{ɉǪ]]mK:j\]Etm_Wly8yƟ8H%CESf_˖889v!5dl!ҴeFiK4L^XYA@3AZ6]MDj+.;fw9&G7%ƞgTF.8M$, +%tIIlb樒I^֥N{:+vxof:4 kRe i"anH^lYXVt/#\Ԉ 5=/%z*"9z&,9j649j>$)j%=֓0{"_B4{YS.uEp@ +k%Y5_qOfKf|Pw .F +&BWLxYN\;.v% +#<{+UͤHߴzrLNM~jK +ODdg%222YI„)x䇑 ~d7*a:<:~7ǎ.DDaDrxY~nSћjᮽ&ʷmZ_s2P"wZ~ܙ *d 8ᇧOq#Rgy)~[& `A O_B'=q/n&yd,@؆%`mY`Yn`ug=w4{@7|I:H5 ?BHI t`{R"n>|bf/s/m!?삐OV"xF`'!,ɹ 0z}OX ҂Ag,7{Ɇ_g"D.ǃ +QvGlYMtBt"s+]*W5Fh+ !:i__#;?=G+b `>7ҁO=3@$fAb"h%[WWGmtp:f}6aי D @+5zq$X?r'j"Du"֕ +S g8@> JdHJ[Q+<: D3q,]bk,d;2{!8?Ds3듀UHXAPAK +},N&-*unH2 _x+lƴEwÆ؃Q7Q9/9}pŀw3Wq>&!?{ԯZ{d>@V#֊ArArUU=,7J$6^Z^%s^[%*7!q+C;Q 8/DN&A-d_Ɠ|Ň-֑{@w. …lٲt[R["WQT;KRgIO{[7c! qe#C1$WLhb- +#G4g _4egy?YH_κs[+▲%kҞ+o.J{IEeW@ܩj$>đ|)֑6UTN-g7G8/yZ\ИNn}%7,ܫQ=V!Jy27ңv[V-@g_Bidg'=6M%sz_e_- ~6K]nt^7 +r 9戞;O?O9$w&8|[ٮ]ٖ2h[ͩ㲦ԷƴwI dgQ@zlZhRjwZOkCf>VEuv$ٳ!}*$\KlWv#Ir8}`ZjMk귚}#ꆵVE}Ƹ|{[)!yDmH@6o<l&} ԭmݣFyN$,P}U.+*wWdS6g4e6d\Kٙ٫NQdsqYUDH$[G dΥ‘2VrG6O]m5n6;^.{vW6g?h䷙6 +[ ++eyU; jks?լ}0RiN0-1VU0.{$mJ l޲T͡ p<߽Vԫ{58xthWflYWf6nIY\#-lTWO0vZn|Z^03 iMqTU?(˷y{)L|28k݃(7x_h {YGՌF6Z +Ě*yeNfSkʦԒ4Sb:ST41L a&.&{S͠|>rǔmݭ%"J};uʍbBf\.1M),,ոLZ^ُĀ>ӐX:)(UƔLV&Bٜ3(CU沧iFuh:'ʿ۝j[W[Ģx=rzSS +nW&./fkIiViqUX٬5X9SY׺-CuyTe4\ѪuMBXEAaIXE@0qWzZD +REAPAܵEܗ#n=3v +cNUԞ,gg|~zy}?ѐf͂1=ŧoA4ӵV+ok2?mW{$QRYk+;.b}˶S"{qIyy%w,>{I@m˶\6E~у*!ݮ3FtmuM原Tյh'ly}OqOj# Ǭ;&a)*>K_X?+w᜜}md}=@V^`O2w  Y٧DN6 u1ֳ.3&sՒ"/jT6慮;TnuÛf=,=sӪo2/ UYeCswFRևD"_IUǧ M%S,\RU\,=㰽CQ>wݩy'G,iY5-yc\vSѬc{SkRNo / Æ/?R>*FGRGCo#zTFtb=tG_]ҡkT%^ 1MmDd+/d/>08g6;>'^:1U>>f6#9(TѰ臝Dw]۽j/qTyÈM{\]ۑފ_q3m,k |VS\1s6zڌ1יӣ vyŴ#>3D]!h`?Utr뮈ӖO}[8:>˼&<ت};hVFByCx]DFvAu:yDgD7#jnfʯӖ"kNkzżr =ZkCO]JOxVcz>Fȵ=U͊t2T8w(C@u752ω.4>/N͈V/y/eTFWfɯfOxdחa3/N׷!oc.܂M |{FD7$/!5Z!Dul+Xvv'_=7-)_3{p~jZxY4C +UClw~d5IJAlbY?hGXaD|K#Q;#JÎ7n:Z(3 +BHc?d`l.ATVK\_0l_Lj*P5˿C)EpVCԿ.4YEjE( "A% #@#r A("HM׫XVG+VWZ]gߝ/g|g]ך$i VcjD0!D +hzG[Cq n@=_\r}As}F} +ns[x +ϫAy9*Φ9|f9DY@DB(KD*׌F!.mz?2a4;Na1vk +ZC狰oR # ~H{/px*ٽ_ +LJjٰb׻ͷ=o:~y_#!|\qw| $|ÃQ>P@)wusW`Qn2#5hyR/ף5n3Q-߇/5uM  +N :!x\$hB6&P(APo8.S3)mOEHd`\iXf6iK'Ed Rtv阽';' :>|$l*@zg!U 4S V, =vS^jR +\g [ͨ.Ǭ="w99)xOHKU|%i t D0^y(ewE&:bh F 0$@@)=Į%Ωs?A şS~+[ovlLqɥgr"2.GRIZYEࡄc|;+#vl6Knsc$SA +j)0@7b-ǮȳCcSSfz3%쥓a㹱.#->J ;,3*o&e=d}06ߐp]PW%n 8r r`d0q-=-@Ѝ}M>*g./.qL'꒮O+IX") ]E7!=*nFgfONTF*=ERώ\>fP陕}z;D/*'Dˡ9a~5i(akRe +-D}/ +=˷Duz|o.5-Bg7߿f6x@ wqo]GSI:mu~nG߶a6޲z1hQoge!̩R^[.*KחkUM/+(L U~P^^Z6j`0pXWwT hu:yMt52-&bEKh}]m[UM6]e_Q*P+K+njQ@ɵgCe"y;B;9S w!!tC}fh@nj ՔUOguUMbW]CVm7ϐNԱu/ D{X[~|pL[V)DBwNc=fh rͲ]5gm[Gn˞YʆܠRzBNQH~T +Ș +Ht@ĖZYpـp{C |i/CC._-+aNn݉[S;mŴݭՌV"0G)js23^;B|3$toL>,u'{RFj+E^O?dr7 N07]X!@*Bw]Ad Bc _ݤt{+k/7ZT_ks76mDna-r[;~cx|D_|J>KˎEћԂEG->v8T)Nв@]n|;)T{s%35q0Ͷm@yW5;dd&GyS-<D6zvc_֍Yco,dYbjmt"\8\ۅHMkD Ds;^ ,4㹼~ocd 8= TxV{ .\;vhH5mL¯.CwC׏ma3>^gsX~G[BQ(e>*  MCraxayFc xGaw$xKp' l`3vog&_$*BM# |Ʉ@CBZ(( *.\,\ xH` X&c ࠇW!fpU3+l?D"\" Hų: Ix +C =q?/8T 籎簝'c??g5|M˾Erb(xS(b +DZDhĒT /j!8K"f5SdZm$=m2] +{ +-HEbfy"z} ];ҏ|!iү 9ꏨbD2wa1xd] ԠkyXzLVG'zB9 q h( F|?b2 ?ɜgfn3~_r +B,#dX,TzGPA}1a4{W#"f2ς友#; @vȨAKH?0q}5HpvE,UO ɯ)cI +n e@t 1W͈1Ҵʀ﫧4OmbEۄ?+[+M:VHiPv}>dj3q]3r57`g0o/iK9XߎM9#sdkQ5nBN y\8 <; ?QB+ y#p!uNxʶ [Ÿ] X&wg<%ݫ:0/<8S6|n:9@틼H뉸Axh|KD~F!ZS4.y} + +|&t3I l}#fr+Ȧ0k4f,9nD$s& J{jUwQ1k n$o<.x:rVȖQF"vIv$5 +Jst0k울 NeNEOU{JX( Z0D] +(ަi0E&pJהFߍyǷ ʣl2v2&%ݵI ť3ɵD K%)^U + +/ +Es +!Bh`/ {o. +c2{WTKEV}9{[I rU:]M/6 %}_7[͖7[|ĒC_dD[ :U7JHu!ܪ5*5LNe莖˜=jС&K<\YH)ʨ+d nQnz 1!Y*bRSv10x{J.7[$5; לvU< uSTbt<%7GEϒ׳dYa$8̯~Lđd"412D +Xp;O눠kXMaщԭq-5ǷUWFRW%TVzeRkYE;')O'̝{/!s[Y)(J"j& pk0hkZ1i8f .ZU*+{H˔Ԥj<|/_|b +.1]$[=gp{W#vVvYB{>bc'ٸQ9jU#'!@jYR.:S%񫚙'+|*'88|"*;R%S"h5[KLqf`34&w3T1Lz-#6-.Y(l5+ȼ&WdC#- n +Va#FpV#ZX+*_ͿE{Wp ``#6ფ!ly +@N{Ss\»JC:՞A=q;mAԣ͈zL(Auy{oq`w0@-vвuq1Q -q/xl#GN *v:s9>Վiq\r@ o/"s;ٿ}52GpsgN kdӻ iWRX0o39jUmW;'2w(tێLݒc} 9. ra ut 4|$@MH3v;b=IQ>as7[MΦ[sf +fjvg:`Kږ:duȎ1{\E+WwA'@?@ίXΟH m!f[Bਞ_l쫏^'1)i}g6Ky+wVn|8x8]Mh_ο-3'pC"HvY(9yѡY&/J9hZru3W/~,=A}ny;P gD.~gЗL{(m# a!: 5px7?ՙSa20 f`FP"JQ,X"q%Uc jtE=.Y{uƵG"%( +!;O}}'~$~0Ofh#v^R+uBW e{; F;m_ x(6Q}اD֍"j)]5GPps`|(|H?-"")bϏ߈5X/v~nH>6J-߳* .C4'DD8?( + + А:H>0ZArCOY +yJLX R`Ev%,M4/q-T{cDAD 38Ӆ㡽.Cw&]mqm{w'♯E^d֬QSzɫly]jyh'P=9]}GK4wV{Ju#qg|&xBSFӉПHD1v( Cjxm#TFtfNLPɮ+( }߆}fDTDDYaVePYM*X&FM0.59Ѵ1ihKs޼[ą3r { ʏ2hnڒ۪1Sb_ǯ*Ҫ=RDna_Y9sMF"",MB0R߯iPQt &VX) wj+\ټwIl徼Tʜl~Yv)(NBQj& +S(Xlaᮔ^;4>#80Pk=uL{Ӽ/xE}ZhBg./c$18%#p0U$MK]O=O>d(NGQb. +w`1JD}P:}'ih`A=hcZU4u kbMeՉV9iҊ~-FX_r'N>++D8E; +QB`4ԃs5ԃz{vH[Cje-ZEM+c-$u))Y$TzU7 Uxm];xs6pk +bJsS 5PH3@/*Ʌ.3rev.+k_ٶ0Ӥ{,wdh9(w辩KpBr_:lEX z.,^.Vô6T~GK5=Z)GvMw[n̳>\Q缮kD{xv;a="zNϤB 4MC rfh a]';m$gxF[bFl6_7 o7䴺)AU輺ɡQA5h8AzvV,Ns!eL83 Gx*NgLбB㐱Um +kpooȱ>^AwP~1?OH1Łi=3LL{յ3OǨޥzZtnT!ACӷyFsh"D3\p-Ds8I?DMy`%6U" lBgE b eJ2L^U++fMOe?Y-k7g]ew+bG)F)O+a5Xs\3 )ς@x+܊f֟btRk(j/˔? 'ODT up~ `$lF򙔱xV2eы,?xO{*PuAo_t?_#?%7j`X~|0^@0WANx絔Ahieޞ`og?hΓ|9g|Ht7B|{`'  zh%hp440ppX%B0H1Bo FʗRQ>= X=Q[LɅCy+)hEˉH #[!`|E~\BAYpS8RB7(ˉ +ro }bL x`B/Hb͇C<hƠ3̕A#z jAM,H`Z&)&5t>2L$U)}~D^ KK0hȠ ]̝ACo l`rI$! 2A%r|INeJvv :2hOZ1[•XB\RJj٨B: Bw,\'u}GEugqSFA"3u +DPAd230 ",BK5ZWcM=hbY-b'su߻}9(zy'V&q_ Nq%]ev^Hihde-r8hQA:'hE"[|}mqBLb?ǖ( zŨ-,rw( e}ow?$kxo%7WCgҋ_w?=߷{'+E;oKQܒ(['e8s21E3fNPxpz]8oW.Z ?Y̬ Y 0/2]7\ +g'\e +/p@w$@/#@oZP/^z~>+]}A&ݙ;U'Eb;w>3_q)0JƧ(:@38]z~@Iw}҆<4{~ެ>;ܛs\Z&Uٳg7'dY>=x5qχ&G<ޚ~f

#z}b!\ C a ZdC_E yN68=qh~y&sL?ݢ?`xOn>A]gwd-MwN6]V@A`Wal-pM9G2p:ҋ},b>H.p ,ݨ?$Ev/6߹r{Z6A[K:K7]`'QkԱO/&f~e%<疈JGT؃q=ѱ{#4=]7nmtۯ6lM%YK#٪w͡hOPc8O7cq>_'d$8,d_۝P=>Ұ;.AԵ$lSlEGtmMֈ6eY˩1sC9z:N(#5hWұ0e7gRYp" S'g67c{g7'upKJFu=1Ŭ-![ܪYĕ6/Yn"UVѩ6̥2+yy]7Li :Ƣ8н}I ڍ0۔)oS1ņ,؛m ;s䬞l/^g\Pu1$U)&uMCR.־:acE|sejkQ)Wjvţ3q$2 +ÍxAe Z!3|gVglnG^[΢ DY f itMuZ<ʾ$ɱHѩII'ܴI7r/Z52ĉȴI0.x82LcTe} +AO)tX6eiʟPj=VٵuZaIBC]U(ReS*,˶I+-K5;w01E]#.BdSc +PFF 9Pg?\Nay4;ʛfq+ Fuj,ĚqҘFYdNʊmLXŠKhLX9:RXU[<^H}ݍkW J8 +(8g6NZ`jNmzN?f`afnMPEESkٺn]6eyZ(*X +)JYYػہΞ;0}'MZB׋ǽ2-c$)nJjG%W?ō'=vpUB`J56<ցYki3d^S`gꪉ~E+߷bz + |NXc.tsȥձK,i)X,1$f=baoy-~KU^)5cFi(ޔmJצJGxiqoMnx$p̆; .X$lhIix^IUDnIcDܵZ"sVIdގ5^u+7r~v'l3`Jy*qEX[Qsl$S}Fna)kֹ9[V̭3ʮ؇-%$}0=5P-gťʁi\&TwWQXJ(W wݣwy2df3]/ӪKR\;-] lI6h )wHp8_\ɞ:P;`yVCNdQ7F׍j)3u{&կro7$1T(c1f`6ɝ.`2Wûùf6hXt$ G<gSFcwAUQ˴2-Z-~ˣQ;"ijro`R?PTY@Ƈ& cO!g|&_$#%;`?;}MCO"h-ݰ} `;+BgDi#3~n`k/b݅ F I'3@9=.ak[,m03Lv^NOй^6Am?tuނvU*3N5?evSO Hflo|oa1:w4;pPA7 -`s̟ɹ2;ك?e[V`'` x@7BdNqL9ćᇡ7\.,P.W/{rg̎ X>̽o,v$'ehB| CG{"$(C iJ0~OzJclr}jO][B 9 <9Sb(T/yf(ў:-TDA@'/R'yN[ߛ3?;nD$_}š-&¸P9U^x<~4^.0#;ߟi%G\ )PaI6Re١Ԫ֏ k괶MM6ѮAbN} :F9UrꧠǕiE`_PKufT :kA+i_ !7!q6Tt-? A$b@k"q$>ǫPZ%vٱDX}ب]ti;֨ڹS+D7Lj:##ݢ{-T3$88t%|t$ˉWӵ ki-Η=>wqڹ\wYsssy6%6{6&]jH`T$>5@| q4Ay@+#Wӝt[ZF⋴dډS5?gcb)+ )yLeKgMi4Hm5M'UvSUX*iIXgk{YjveVc5 Sհ|w cemyUWo5+ o" JbZE( K!@k@E(޸junkn۱vvt;ad?=s9s߰NJbMH k) ^ ك{x s%' 0!n%&,%^JR/5|ϹR3qS։ةPG2{4!xW!s΀e$ kg|¾Ct+J\V卵WI*9}V8=0MTL$[ƒۘI=!CCY=2/.H]r³ זDظTuYc繥ΕAt_fMMtfv<gTF0즎Їyj^]w!S[lϩ mn6gu4Caͤ&s>*Ie#YBCDHYCB>9Ήװ{^.p!g 0e b GP5&0z +ޝ,}`k~ I_Zȭusf털\;')Yh?P[xJ$  |s×jߢ7 A R7 +`LRʢܺKeM + "]`Ȭ3VVs͆v~YQaIH?+)/n(|+)1"4#Ucpу. {F[UQyнŜX[W]_]j6BJj9%m|cqP4*Ht+rޠ5~#0t`aB 8Y0O0{Ͳny\VQS +(2UXEj/-唞ה^dޗd3MD1AJ^W%fA=X4By#45Zѫ ޥ~E@C]S_kͭif!azSz;\Yu:\YHUITf"P _]AxkC?4 +`Cz'f,@w +;kW j0\Ž-nؾ$mˉuY [uMeW/ة)ZxM* u]xpNA{&q38;p;@57h~D@t[ۛ NDn^>pW BCȃz`uP y2cc}8ܻy3itu` cOx>>ޏ;x}~lFຕ@Cq \֥)bJr:ɣP-g< <ܗ\;JܖᦼUp8^E' 霽:'8^vMm -,U)Q٬jifM~/-߿-4˩ŸS۟*p-lQ犓|P:Ma(UOUϰfRn1MPm6MWf7 +l0Ԭ7m\keYb׭Vh %? Z+jslgXgzj~:J[EJ,6PnLW . )lڜk\]n^bԼfy\d\h,7W9aSs\ Nq+H +eu-??;w +WtX1QcJejtȴ* +OY4KTh;7h.?~vP}^P}n#~zБ]N-:3.mKvʺ{:+=TFiXCEqYZX, +SvfU6zY_L.4W:~Frǜ !{vziBЏdO%⹷7ubM7gjHwP,,ΏL떢u͌lsdvq);|a\NwYo _G=97Y#Y.{{3~,K`E=^&W{^VocvJ4yRp }بR=9$A_ٍCf =s c;eH~kZLtNr"}zpppc-4CJbe6%%ppj\&#}YI %)֘ꌉ!;_3T#R4b JIOde7 1P,,.V:,UHA@*`-k1Xb]QQD#UѱrԊ:k+ڙs@wŤ,F/(GFWύ8;jSxTQWc(a>_# }xk+$|dm8IZ%BN(If4-yYrR"!1ba\eLBUt|M,9"V6:p kv + A>0^舶Kgųf] ޹>-)9;r=$eѹ~Ȝ9aّʰ4$Khz: w=}lIV|(fYb.sFx <%!e3˦˂KQ~-'-Vy[M(Yc^IWؒSڎ]*lH!)6=g;ؖm^!I.I}*$BP# `hKWjlҪP3yU UeXxUYRzVnQyTWW+>j +a^c{s2|s@鎭WU[` |7q8P3kH̐ Y I{6+1n2w55w1lmxk:VXX\s;}FZ:K+* <moԪYG]׏[\?Mx,i+q1K6HVȆjdCLN2T+䃶^7τ={tW +MDofm]2 kPO  3CwǀPosc6.C}$NKE%q\[Hv l#z,za ˞u?0 &5M:0h`<c=F`ӒrXBz\U3X>"$d382;s `. 00(лв]:!e +mv0o E2 +N?!kvN}'5) i{M'܋HDrA..iT5/Z\/_\JyC2h/`pB/뭐yO33OW:赦;X_*8kx!v7\[cی@77,]N)KOgͣp4x0mځ=jz/ȏI~"r~T<</qC.נ(++7&F,(,ȲܖEvvrY˂+  ".!xCEh&Fmc6If:i:MSM[vڴ}z<_9y>|e >X6e7pmŕOK\@$ dXqu,xFVe +*U-])[kkݵMp={aj1drrr_w~ko7CfC $r"CkKGmoWkqKp/4 nRZ.GRZpP9E;}VC)g~֬(b}Bq}Lq==WΑHH둄CHW ׇG17r}G͛`!:)3aNi(-)>)wfi^Qg2z{88w}Hca kl!Mw07ߟWЧ>(U Qϊ귙.=CӞOQ[2 $<%b޿{?@ωlsc9ʅ49Lføv33 @fkזs5ތF~OF-L/jOJ[>})iNؕND"BWO_zp}b0L +&tRݱp@Gt>ի/`wg[]6^g@ێ֬@wV?Ӓt3Fݼ^wKZVw#ơ#"$9p7\G߷`=` +ci`@J0C1)Q0󊸞<+ߝ[ВrE 9{NÈaޒWޕ m'2H1D>O1wW9K(D}7 +A) iN3X&{m.,5V4 +ZE5=!8)Ae_HSGD瘃[xqz~\__z_ΒhlViI]lvcI>Yb9Jl5N-,+̃RaYLPZIXn6iH; \>b';(}-ügyQۼxQ}z ?jXxc^.=.vv)jdҐ0@+w(RV, Ư\2ZBm6^V{Nr1糨{{i'҈ߕ>j@k<ɃȣP]S!> kjX?7vy@E}eaOp}P, +(q]ՠƂADET,NPXh{,G$1qu]{Xۏ{@xgygΑڛ%_>`Q2l]f(2C/)멷4y赌A.| b38~Z9P rxë;<+"Q1ír\\p4éUp,2!9V3yLYǻH?RO VF*gS݀cju#`WDak261ZCcIڲ*K%\@]+!=bԝC݉Eݸr6ԯ_ȠAVh6#GdeYPV: S^ jO-Pwm.߃k=?CIl3Yw8ߕF6eل\dikbR5љ&+"CV!V`zmDQ7+|; R@.Wtll]> 7 Lb|II}g'&w!h!y6N(F{;Q׋]# DuOrLhv/C?[7lO 1yI#_ҐWhv<xռmּExD3=桍i<,`!Pqk6@kA? $#dYM6RDJvRK!u/+~xI!쨭PpW;H32$t䐍dur +.2i.'WG ƙ5H?2|B>N"u9RkC:k%2SVo>~CG7A8RWm! +GzjXjMf|tX@Tjds"@# ~I p'4q7F \hK_hZG9&ۇGx}Lԙ0&He%rM8O_ŠS 8tZ%#R9SThgG8A'5qU˲h%|:bN+qJ'98̃UYê +4jpHՀ&|W2cjAxQeNW^/'7~}6}pV7lGX3`?`8nsWu2:AC=84aT9F@YBz7ˈn.yJ\C;N;tQwðԹGT{$aL敃*|Tx{JHfKi +IA3!!Z=k, `;孁Pg} lʎcPR(bdl HVJ TX)Iy'e~LY֐՝FRK03Ov@ol=P4[Gas8OgHy!s!) !!9!5!U!쐣eOKC"#TțY?]8iG,=c~3XP7la(<`G`q8AZc"[eLeʳƕLFEq2ݸS^~EX(\(I< ԝn_>|r8nU =+LXcFie%-7e&2Lt\E)EjZL1S-0FlSϏ8gj1=6 ,Pe s :W|j +{Kھ>XX? +#e&5E\F3+Pydvf>6#hE ()Tm(O|ǧJx bca@OdFwƒ0XiQGdcNR̎LNNγ]mMV71.!Fh*a`+"}ccbuX2qH &̏(͍͎)K#"q4!SR4VhuGEyafav .D&葨((("0 ̌ (qh]\Q0.cMh4rZ=&Ic\kmm&A;8}}yIirjIjI4j{'JxU?3~F[6a>(ѠԦ*CPnH t$=WV^PVOW5MߡYgMYHc֋*^TZRES.qaQؑlZVudD9TfCi*LiPlJN]Y(_Yeƕjq&˸KΘqZqC&#CT ҏ}mf`69x%RuԘTfbK0ʬcQbBA>dbJxyhI%){rs~0AZy(R+‘R9HLT I\E4L6-U]ު^WY>J"r,JB2`y)PK8]LWA߳H^FB@̯}a瞂hĺ0ǝ"ĸ3ndX宖Ns/nO}M><<P{ + ~u@7hYGo ڥŠ$;Fc@G8;#\<өG] + <M hw=n];G;65+P`0^ہN``~ jCp(C!EAeaqC1}C"? 6je6րv1.Ao8]@8B{|a#hB>n~psynu󘈿+27ԝXg&Qs459=@{?0# pZM3lF{p3,?gyٟ!a{(pm>/д.d/`=fC70ԧ'J"H5K\~¿ƍ b^?EnD|B]k4RCIX= +(z%-BR&kOm?rw޸p0>&?62j4hGLAIþxq1GxPR*Ǎ+GsMԝPS20l<@?F-5Aո޸5ZxWXwq+0"<⢤MT8UKƱs qW\ %uW7hZpYӉQ\ňv ¸C8? ΍3n&<ĉIdGoN~:G ӊx0n11W&%atrF&0- _NI~GH) +^?`ST!|:lG0V#ӝ84ߛш3۱j웹 Cv`p>ݳ10v%%U'8V? _LHjȹ{<3&̬)8>'$r&cp{T`: 5cgP'vö~ak?|^ .l +>/څCք|/@܎FcyG92]ұcq6-.Rlw/# û º]bO~qui;X\/=R}F4XLk6c9 b r&G/Ė$W|ٱVW.jձ*]إ[/vI!-;*ޕb$7SjU=c;3Ҙ?ov$/޸ذ, +Xn}+Ra%=W.H Ƅ'NQ?RjjWr^\ Ekp4riӊ 2)I~<'yNGWJVRn0͐/BBl4ԉC6 rUe8T.j* w4eߓ(N*;STu˯lU' j^,^h71nFȘyP"\ֹ-B-΂d,NJ`/( +bAAdHyղ֯dۆ4fi,5L}2dZU%3_S11׫=W̽H;Xx:O#c㳪EpFIB81(rБ-6!Q"ګ{dwY>ey&& MCMZMZr4;ej\A+XEq 挄2r˲S/dSYlN)ݹINuVR55I4)I%)RCm|GiJ%i"ߋk$UNjr!ۥɕ,]]EBLHuW ]򲪣r|ը纯UUT +U+Wg`/*!mMVXE] k#ݳFz}IEDŽdAyZ8Z1~SIOrYSZU!ϸ\R㻤Ž@H55 IFJd$LEb<[ðu ⽉f`ׂX.omBw{P ޻bh'bh*6FU {'Za'|/^@,%#k& jU8"W-EĪdĢn+ºKuG|qXYĖ,$&niTUk_p +"$DԒPJ2UcLUjj:Jϕ:Gr#y<꒕Ȭ g*]FhI#tM#44B3`i M7-a *tfpY Sa*gC~mw@^dQbOE*<7Ps#)7Fay +믐 + +̟j_v;\y)`jcmAv3yf.fN5`={e!/b򥈥Rpq/R?- T@iڔʿ4A~kS>jmVҾU^#_WOjYQx?Vv&gR\)"K/ʥk%O<Xp1Pom$5qQ cXFTޕe) +SM4PIYhx>]B IϕR)51JjIb۶21 ocR P RCk(b Wovm7) + ĚzrjE oTK;$]++>v۽ c~ǏZZ}-ͥbbjW#0Gi%oFɺUh$/5?(G ~ŏc0$~b9EQ:_|F^}I;l 5wKa MchJV0E:\:Ǣt%B{ KuL/gds2y4]!T=AOI.?H+XMXPܥq>gA*KczM#c/v?>>~_zNo:ptp0JSjc &C0&51II1/gה_q0ބ10fXP+` -6ПPOls&\wV6= 0a&~j [Z=W^u_:Rtzme.4+k4xƠF +)O ίu/`@hȉ+f7r}!>w7%,gҹYn!Kktv> KP_ ٤4*3ZzCǪljjm3S/`R _Z- +N!Mhon6\[b6R\wϑc*=Vc=?jCZyF+n{>@NZ5/bF*#r#7i{YQǍԨ+ƌG(HyNd7xg{=Ê6Wvg"7*l.an ZЭ跔=Js'jvLbR53fg̈YaT2c?5b/ScSbM#I̯\%gˌ\}2|))enE1>=*U)@=Da)fn$[IcuhuX&L;by7q3qFzWhD{o͌ qڽ]iVK4+>B =5#>4`%ۆ)6ZSmD{d[1ѶИ`+UXj,m\Fλݳ 1g$b>ã0{ KHe K"I&L2IfLB&$$C!"ITBR, @!(}cVVc] +B_Hg3s9|/>X$E ҐcU8E5IsT@U-wdґ +G@2#Xa:Ŏ;BGYn;[ycq9.YK$_mqg.j]L,kc acSUU +TE<',ݩӣgFsQ3Lw[,q+'+yN+fNj?g8IK+MuJ,Ty]̈Qif3ȝ;KnEFk\mǽvo[Vr_\GF9ƱlMԀxꉧ,'r`fE8;J9SU3GK= qɔϛ\o@^o1Mfg<`9={4cVy+󱕒c}fCV?8+/n-xBRgT7\c_-or}'w*Q?_n0#b&w[I^+Z\xm&}$=o%PF0 7f|>xhA,BeVVQ2#*RzTVj|&)ԥ`- +V(!x lc 4o2cؒbی-͸"ۈ+c/bO~o&j`C5o(]k(FӜUʥWj֪"ͬFnԴnM=ɵ(zۚ\16&gJm<h|Pu<شZՃ4>\3 i7ѴxMmthJcuC2Mjӄ6ii|qkzCz[Qk`mD#hl#Yy&-)tS4s!&E:TKXܗ.S p8.jkaR3нAWe4ހRbc‡/L>e~>g|A`fNmh5@8 +q P`%:X>qBx_]}%~1%ޅ&V#7B%B70vޯեh>g^}~$%zEs`@}xËWbCaADA z,EL +Fe;{v0-[nrt#Lqjh8Чm>GulꖀiEP0'oeX׈?L0?gpjJU^lbPx;w@x#F7b;&"awQ\r㑗#G~-QXM7gQ;O-SQp2"G#q$*q`i9-2 v/kΘV#cak6X.#/a86`Cj~c>11_Cqy,$Ȱ31;VcJlYi$+6%90HAOPKL=PISA&ze?Z#tI %UoW9R2yWP~XaJy;RU496*pz9֧1ڌv d?}ѓݙCM7!Y'KG=2%|'>KL!rl/碗͙s1  ec [Do=9 V8PxWtS9ڕ{QNUy^ g#?¡3m>K,;&Ygɸl`\*cїyŸ_΂j +([ +OaZ p6¥jJ4 ꚰ>ªzB$a-@Sf4(cCO# 1.aV-EWQ|řh/΃D*.m^4aS$E0 u3J$a"?JE>Nśi^t!:Q%,r\pVhЬEv6VZk`n&AaԾ& EQH5咸Oͫz4KI='=駛qfܚ%piQ)CSU6UhԕêӣAgF&}F F@NZOIB[%*%qX'{j}񻋿UZLXj`-P:FC#j -1tBo Ag}Bcr:#w#K V5HA 5Їf$&"p|wHdꓰb | .&7P[M`븪oR#$32R$uHYjA\, iD"*cDAtH8MENLm]'{LwXǿ *pʐ%DqEׁ /BEyjMl`֓&8֪16Mn?H}~~=~_Y⦎%( )Dn(/WS:`ʖ@Φ%r2mKʡ|2LhLcx, W<$Rk3`\r2#s͡jgQ[ ٙek,3ƛvsZ+*Pb[嵅OΗg +S3`VjeSˌ[ۑCh(u:.:.Xn0g<̙+[F_sa +SyH1g`^.@Us$z 4fp'Eg  +m=E'{xK4bX94s퉮j#MCd;srÎ]ر;ر;h(|Ful]pwr߇ {)5՜ ݌>4Ap&B4hΏ{Hc_N`G#I#ůKuX4`;1'-cٮqux-tɞ%CR[1Y~ւ}8694.HbU(Mm™&>v ~fتnc8!;ݪu.4@W 9| -Mywt{>Sӆ#I? {YrU +nGL_M%݁{ց 0=&&OVۃAcYp drXw@0C̄9P eP`~aY̍;ټ' K==⭇܁uޔ_8 l4r9 scxƎi )>s]u ~˯| | \K68ش +/cHgi? ؂.c*Zkl7ң49Y}]ZZ9flAMOŢ:#WϚdDeo{g)Q~hAN^Z0UiEUUnE&herU|w+Wrm]w?<5nk0I!vßWÕ_n/*}cJ;U *4X;<1*J,{T\,POfӌov?)E]C)!*mU2a.mTAE5k)7Vy~7L9ɚ?^3kz7P2VkJ6Ҥ3J &=UjE7%gaz<+Ŗ7fn^ jzM9X=G(eJSF,BW(5x&k\A yOcB+%FXZ(.EQ6XU<+Ė|l%69i!]552{+w >4)l&MPjX&и /SJ +ج%E҈ CE#\ 06C9,{rhaVᮚɑJ쭉}5 5D㢒46jR'ktQ@#bjxFOCcNjH, Q-נ\ZZ#mPGjRRc<5>_b5&.R)qo%jd%ŧix|4,ޤ5^JأNh`'P-?<*?we1 $n "" 2,0QNHAA(MqZ5q4զM6mzĸ&ƚXa9}"ΡlX?m _fK1SU@Y(/ir'+; ,E&C2 2UZJRSdHPR>%RBNJO %$HrT#,g= 3 +boeȜ6RpeE+#=ItsjLJ26(\MG'qA;Nw(4bS:F @QU5IVcwU^56=VS5Y!r>><ekzP +)iluOy-P0yڇ>+7{4>]5o_pS$l7SO7=ϡ~z&j"9Ff3A(h ܚ4K}i~i<[dZ8ZL-P kygຈmlyh*^/|3Xs"kĚC 7IÚ%%m,1ϵxXE# C N01ҾCP"p8iFjz͚ 5Jc{Jj}?@?6f p뤰;YT':ppzybFcI7xZZ+ow^BmxBP\wFzw>{pbGs֏ ŜC9VM(gU8@xuq?Nx;AEquO/Nj[9WuqN ?%wŗ̺75f/NLN>V 1,vb{%Ө;[|;xR>prWJU}s_DщTMNΨ@Ə7𣋼:~ŏZu[8}D|E Bm'|.85go-/(t"шZ:s_lخ|l6bsll29=قldsۮ"G'#$:D b]Pr\l.`w,$ +ϓTVWUD̮r]9 ];A1B9 (hr4*Ѩf,Ry ZDd+G#r"pvrԾѿ+`ܵ^ Gk4:ıT-TiL\Yn0˰/2,3,31 $vcxKʭk7V괪*RRU~V.Q*Jc;R{;G: rq+YȦcn:JFd)ﱰG}VuWj폴~UeZr6f_T=/F|Tg"S8%S[K8]ͱlsrx[}嘽,Lr fÄce.vLg2=&> 1,wvi9Tk%?k5t2Y$Dq"nG9orj8`!E8\IL&1B(iK{SH9#3jJCq'_vDSyʝi%SK,'r %pj6iLU1݌2ьQfL4-0`tݙ2y_ d2LG_d}>'.zi{XdU˜jɌf*3Y&w/4i'p y-0ws_c=om)]Ɲo6t=&ezX✖.kߜ,ȔjٯZ͛ٗs J*fwa=V|En+x O=ūt?*%o^ΒVGaֺ"tQޓDY3%R=V +=xz1{GN]a92k=c`~53tRLrH[(m $`H#Ϸ\_!9 +}ue1ӿH4)$(~I$ =5XE_Z#_t ^}Wt,RT$k$S @:;I Th$9")Obp/ yvOL\Mb&&+#rrC|ǥĠp!ҮZjBVCq$Y!6BLaCSl aTdo1'"lgqHLݢէ9(Ji+"J_1uBp:ع DSbsMa}aܰnBywx fkf?T#VJ٭aH=Aa+\89JI_4)ҟMDZYXI׃(ORS_US[Ƕ\[U\=%=@vP5,O8"Y=%]6mzI0H_)K0l>.wR )ZL-vj5!/Cp'V54Xք,(z۩g C|D' z "9&5xZpT% -vz'57` BcㆎS}&Tyi0(:5 : HtRwKc)j<)^xrS긭Mz[95YiGcݲ9S OkI7e.5ӍB 2{2ceey(Kk]XXXv]`9DPEEE-}3Ѫ68ƨǚ&5UcըʹMG϶&iLL9l?qg~e}yG㣍 Y&FaV[O?r&4ݑ Cƹߢԩ~?pҪ 'Ki.g]l穋 LhݷS c)+C7`?vj $ur.{gxhV.37kznP7I7M;*D2f;y6U+6S|}.UXzM|;]jsrE5zH]< t9}" v@ Z3a,tS|=t}M>|\sOzO BVȆB}24FQG@Ǘ9 3ЕbA\Ru!u>}p?^0zݣ׽C.RC('~n>_~fb/%||% +x6Otk?Sn)qG>H^WBԟqsϨسu8Mhl6uF*ާVc%>V2e e摍ϯilJfSQM49αͷhf x%{Z1p"ơ4-6o,P +4jИTmnq_x y-5+8{wn}W0zh%KdhVWfvWc!޽LӺR8MI~Tc&X[Us<1=/gjjb(Gˋla5øph?YCif5iJ_&U}M (Q]HѸ8C5 TFU[UWo<=ߏ2pFqZ#-93#gjҔ>(BlQmp֘~+~e_UC,dlְڪ!T~q >O ИUEktI"2¡9*PJ4@);҃i Vgd(͑TG9du(9YgdrF wAF2:)SB}ځγ$:P^ ,g3_Yٲ5(ۢt%ggWk̮*\Jp5fuoQeEຬ(]EgS WgђL1P%PR]e`Yr#+sY<9Sg<);S2Ez7+;y*sIaSDn[X,4&rP ^pV,o4 P!2WdU薡PE#Q4NE +-ZE'̃>8dY]p9dj FEvǮ"!fRzD1j56$HӚ1ZsL5Gڴ4uSv~LLJ=}}}J,}Zҗe-=/kMي\!iZP[OhgJ(~ԏ0h.CM& lXoӈ`]~񣇋5顸{ ٽ ]k4N>brK $B])f:[`ki8`ogg~rr9H};¥{Ev$9P,z)YJ\BcU?t-=7L0cQq-)8ť|?ct$`]9sMxB@w~DŽ q` +a2B5XXQfezE|^&WT_?xNDH x&@QGՠoLNי1]e +?>Ǐ?Y>c2D|oI9d 88>//w@<)3̤NL ?>ď0՝?{wgROyN9%x%cd5^ \{%e3)/&.lخlln应wEVوvϓ:^<@G!.b(?hDcy2ĶuDڄ]?Sm+_qх*?J&v%} (nC +D:\fkmt*t3zs7]/Rk3ɰZ嶥jm\Lʤ"iʒUTZM8K[T`T}wj9ME$QnrvJˤ3i ƗR-gE)v8T:Lũ#5-u&0PiєA^MԬI_ ەO_ kx +G҂Ҋ. `+ܔ9"T0k#Qi*7eyG(AuY`w]`e]]6xM0xD⠉hhԦ:M4=$ΤvI۴;^37{y{wiJ>SMM,)O"t]-)n~]6pDo}=׿%؃ +M|!.oNP9M1#U3&_,UVSSE嶶i] u.XwzHb=xpgيlAS!|(^UEUY\QYM29m^a,-<ٗоY.e|9)-0pvӍ*-M0 &]*pĪ̑,Wi*-5,TRŮ:&5UW27j{/h*u]9rFc3e.KFB|P e;GYli*.RQyVe(S^&OF{f)ӡQ]FV>L+y>FG*^3T͑[|oF׸[SQ5SӨ|3kWo2|ەۯ4 \UZ FoTQ=Fh.& B +}/P06Fk[yoHCY2uLRz` XmJ-ocB2)f(= &rkxȧPPɡf%*!BCC݊S\bo+6znڌ5]0Pp]W>mĤX6&*%p¹J[4,p(.ܨ0/6&|f,4b96Dx5ƌ@=|mA{D'Rb45AC[R@pDT#EMaP<0iBSBca<}P{{$7eh6ugrј?v6ʜMncښ 0mx9c8GXfH⽓1[s)V)m)nql( ".ɣ=åM$wc:<_O&(ӧ &⩒iX tSK(kRˆpp [eg%yt2'9drcN/8&s-[ֳji'7UjCm^0}ƛnr ]"W4y&걙ztG7B=V6,Ԣ\1ovaM]QD:Ro ig3tt:~͍[`+<(f"$#I̯e'{5N1bhof=Cc@~ Wad 0*r޸ΞqM&:$fϼɀ$`8dA>ؠAd,-=qB~#M][}wuO|ʯ~g ryAXzEa N +n2.SY4yy]C4b9eh'{̻Ja,#tZ\S Z!}5}L>U3 xG;h^ms{V3]8 +Je|INS4hTO[}?#ĞIybZg)W*7eƻjArz}}Fwㄧ ShGV4\ԭ~b&Tb n_}ث ѫmM-v,ϵ'`| />g,ƒ8B-^T*G_L|7{٢mӉ9:w [iɨ Fܤ`< W;k,ExNT2yg?fۈ_FtA7 +Z9#9NZ֓I:Y' +\9yv2È?#KBh&t0UjgBF5׏p6XfƱZtR'e]o;v.p8qNlp9&MNv-mvJWrT+[v h5[@QZXA\1& +  +!:'}<_+Qˌ7ъFQ4$M,c]OxaYEVW^eN{{J;Q>!ctM:^FݏNtf6R;Iha:fmMEQڣ^C")RQXs< +uM!}*FjT tj"W5=dƲ7k 7,jpV7PE,+s_ܴb%4J{JCvJ.Ym\)Weʘ&+W*ߩd~ +Z4ZHAD˜*@#S4hтk"6P:MAZ]ƴwRIJfa X<1ٔ0974dnҀ9~sb}kIieF˜, WrFAeY*]YzJ9ZM `<ĉeȖ" X-V(b ֣.:c +7(hߡ} +Skj.7-}G\ܓ<:B %CYXc)O/;Qb-SOYrʫYVI6+^UrD~ǤZ[ث&xFNjr;^Ɋphƒ8$:j0.kQmRD5jq*P mZjj9Ffy|Bu|A5WU| qOPٍQ4` z=^0>Eü^P.Z\jvW]/ۧv5GT_?(g\.&9=A6Á23|43 #7964U_.4&ab1۸zsVW6t-ribAL"c+ǶDObIcN9TYPs46LJs;0c`w^@nwUf 5V1!a5&9f6Ԍn&لnތc+Z$_-xIL|1yyuz8c}:`?GgisyǼO& ٷ=0ۃ>9g4OdNi8)<|.[O+q8O^aX\ūÚRZ#u}g\"ӿ30+FHaW MqM7-:uqSu_qp?a`ә>^h^; s͌I.1_^ }C~w=ue|>r,!݆!ytVLX1K/W[e2c̦Z^ً@'~yy_=ͫ O8v?;NDZ%n;7zImvݺ6[E֪] Bҁ( ʠ*kT.T`@`m2sQS>w{y+8!hn X_8wp@7)82}]c^e?̫ +<߅o79|NFb-COoc\#Wp9/|^u¹|<~KhM^#80lO1|e.c_ރw&2؉_3/^P'=C%i/pZO~|1!} +"zARBLy,|>Osyx4c$csyy0;G{ W=hϳ<*wnY6e6NY]adZyN8K΋eT/·df }?edNaA{)tg"֣=Sqy9 ܯ3 +OTnawj~ m+'`6@;vahO0y$>f{;{n2ŵSϰހa<_֜B~c7EE]TK2/{]B?J5hgI8Zrs̜y)u&x"<参 ǃ-hQt1"Eo2KnB>@6X=GRff}6CKc@ ZbKI~hyYE6VhF~ , )Y}E SQiT]ILkiS;,>Dj+>V%%9ib"6N&!ˀXf2ez˪cU99Ty:;QޣeJZV2V˼jQAE-\RrU!u9Eqs mohn0*]7t ԵTiU)CjꔴXSҤZnicƄYEyj0*h|JTgX>E:'?'n݅=XK۳|ý49u_;-s)(WaQV+TѦhEJgάkpnr*[yZsrW^Ru pSngN5pwoc-u@[>C )VB.1WljtU(r+RW:긂 +ܫuonkUsJ5_#9ϫ}*s:Ļv|OA?tbqo&FxL +y,j+P[ZޠMyu7)6U>(g9|'e="|Ruޜp-ĺV|e:^$Z"s~|KwF@܁rҪ d%"#O Y_%xQgTxSJ}NBw3h&hڡ{Pa\a.UF|rFBt-:'kt,=2GWY!D.5Wi8{ 5سly Cc~;7zUSr cN1d5OQy|Lj78SEc*hct U +9?ހڣس^V>7CPǵIrag+Z +eʖʚpȒp1xiE%ɔL%I$1ILY( IpB'Po+h{$qh~\r߅uc H^"sʢ!Sʩ%) +S jV `V4NaRRRl֝Iّ]hϠņd6h3//LH/S/9t5Hu i࿏{vw #A<0sqsc,1|rl7mF1f#+>i<>]s 4y%lH*P_rsN0+8t&ܧH()^b)e`.ɐц?:񣛜M'х.;q+\̎ ')'/aS} 0Pc7!g y?QK9d% .5u!}0klVO)^ތ6)`{`5=0XI}ƏuaK}u\kWr@nzcyGY<D^%-ći 5M7a&X90Os6&j>괟d+V곟Xl!y}K؟6Sϟ!s=U|R</hr뭁H(!0 XBqaC*5?+8plI>| L}_cuM;9`ͅ9x7Z}k -Ǹ(dD\,FN?#'')3q˥|xCulb3׻Z>W]yD&Qp$ U\0!Vs%q@ϓs ,QTg~)!ĻeW9IнFsƺFA}pC&k@&0@>r0u-ys% kTmr YWIe{1܏-S׀CX5ȵG6(пјwUH}r:~\eM! s?ΣijOZ{$U1#1J ]v[>_mpmu })'hЗQ* 8fy +>=w$) t9Ïa|$љY;6f_)b 5خǶ&b{gbx O)5dt;mG/Q'*;)p XUf{&wѺИPn.NVjd ~E=Kx:\?דDtO`X >-`U2K@ +\gQQH/5?z ORz^Nl߰Ǩc~g;h$i= lYnl WP:l7`{'!n-n_%sŃtAzDwm_P|ت]>`tdJa7#u:IDQR-4,MGZYmbÛV͵ǐy,G1C,E'9 'F;5:KQL䥅L>f2H6lq~+93=\ssAOǾٚ5E^8BpDJ8jnV_D*P#ĸc7|8V3pw1wG6Q)pd‘GnRx੅M؟Ld4HR1è:g(UW^1DwAýxK#>6s13:mԐ`ˀ)𔰓U W\ᚈxC v~nw]Al}`hi`, Y̹ßI)&;U^U'4$yRTRLPij݊.V8uS_T(u)G9%RQ}_ι_m>@xV˻JP=Eՙa2åL2TYrEm5WEA{kߢ^y䶝UrF8Xʸ HjFJޗO4:Vđ,Y**U" 3X|WF\~96>>`b:ɧ20ہZyVJ)7WJI*vf*RRY N@9U*!k]S˵HNZe^+쮳9ALk=YHI;cQ5WR> BK?/M< +*ϗ'';A׃AA?L pew^g;n ,$$\ T +D8V^Bq2ZN6#2Ң >q;9oyn'[鐫%g+{ ]Il% +e VP!d"Z "8<<]LpUq-@ԉhܛ_! +c> ) $Uc֥ &Sa +(Hw#)<as29C>q-JFP .~1͵~L{ MHi 4 -JG ll+@5c`*o >40E70cۭ +W/pdK,1!a7fF3bll m B&xjG؄;@ |Ro.<)> ?:cm9&'4^6O/3JκsܺbCg\o3@jiy\g6^g8 9@/}o1DNQCbwD&AzkN# -:Ncnx_`='d62!'#FW?r&eRR;㚅ͬU[uv ~&v6IZFPI`%XEnE㮥>yl>7 ,f=F5\3KTh\5!%>擤!s`1kD- ^[csX|0>CMlFY} s$A n–Z5d||X`5?b ߝ%b&&ϰo_`aM<~H{xy71RX:8{WYIň%@#_&A%WL$u8xWXQ>8B>0uUJ &^pKd|G|FwUV3]6KD_PLpoFp*%U~/N )ڇGNvSO8jQv85mjd+~Ɵ9D!Mǧ);Lr+jOU,U*.V_ZUbRmP*Q!y7^'q~&>@^gUcS;Y))*wzT0TjĞb{*tT(Ѭj-JsHOU/Zl( "Tr O%*HV^SSSI++5W0*7BMJ3:eK1VmlxRS4Na\ɑJb1ǐ' A9J@!%7=A9$e(ϗP&#}cr?8ʫ +o6$,fwI6l~vIHBH&@J$ +$AkJJJRZjŢXZdZQt:0VvږaV;0/۽=s}o +|ӷCyd}Ke+{y3݌slP⼫`M2|ey*(!Uh+ت`>9ò7RN-F 6`|KZi +|A|lr :)wr(4KP +Q~RpBpNˆ/B׏܆hDѲ!|PNc%|a#hpJF0ߕKżbxLq㤸Sn~ǐ Ѐ (%]%\a8g|JHlHOq4di X65|ՆoexhE0W!Д./n{y೎Y|< scZlŏv0 :rEE0&u٦0k@3nIk%e o!ya|HsvB!'KA#KYd>`]*Y Ճr\tuL-1GlkKx_ o8I/9kA!h.\c 2ꄯMS~w9Xeqrđ&fNjn q/X6,ao=puV?&kyGC&g3dL(9!Qjgky?ۇG>-})wžk) +!#6ko,c\ɊA(fC~yCv&ړ{OK߹F*JyW=烀% qe#3pH\΀j<9y{@&/|N:gT "bc|'ku4Jg-_-__߃?[mrrz{ҿrGQ +-@t%"}v̨N|StLF8$P3\PEM/3y^b}"3ɹ#LV92l+C +3l3ԑ)eX%x?<>j* + +-tL4&qXsh^x_xONjqJtR{L(P&˽v+p^Z!3Ne8qY Squ sa(C2M~] +{mf{Lm5:'`6?)=|w|_APŎL籟.Li$_y=Bz?"kzDZ}p_!B%}'] J)(<5kn tb#BŅ9!:NwpCȎn$|_)nSV"xfO*xlyxH%k7xxSspd;I쯇o9r8+[@ +ԁ8cҕhC|E\ +;{Tn6ٻ4wX܍*ɝyyrgEo/(3?do%3$$`BH@ Ud(Q"EED +TPM(Ȗ-Z""Kw;=4s3_sg}ߖ6(M@rAk&Ь%vJ^ ; KgM@ úZ|u9I<9v,}l+팸 O8:C_ mk<b$J.!)A-A&.~OHtOHZsѴѪŭ$NVi%M["VxsRܢx:{u>4 gqYBzt}N}.ѽzDEģgq%+ġ~ŢNy}d8/aKbrg*bNE} +A3A "M~K[4[<~M [[%VA[rNj?]༆ey|1G@(h`*]S@K!M>b Gs)4 N_(| SC,u%7$ђ4ų `6уv-dwCaآļb1a11NSaȤϋ8!5F -ESw':{U!<\>y0?*>⎤3&C̙",\&57K?GӚ +4JC96g! -dSCI23!rm3A{Z"%I cfqdZ$-*<|6xcw!^"0Å("qkNƤ5!96"M%3 Cso#:24s4/%RLZJkM[f +ESjhrٓ\gRGB %xPEWjI],-VI^T0 GFZќ^IRkA#VN,c.'ZϕW:g/\hL1$iRUkkuRG[bNڱEAΚh˺نTIn}Rɻ@S3$( 4[f\" h /\4DSGwΆ݊+yލCF3gDv6gTT a[>u 4UFhF4B@?hI| baX_MbHДt%0>BS .z$f*|ըj@8:FF0'YPyH`pΪ4M LH9:KFf T{|jHh#OS4puddq'qm*i~RRB7 ೛%ljۛ ZoIq@>CRM}jP~D&%x8(D笚|9*Ŭvh֘Ws/wQqN2,AI5F(SB)RV$M׍luxUyd./(ǘn+ДЬB݆j>׍l[ {T38؂r99<cBvOUdN[`(Y9y +6izEqe-Z^Yqխ暶U_WݱSv7٫w8vC 1r17~¤Snco5{gκos~?.x?.|EO<䩥˖xz3ϮZ k^Z~Ɨ7ymn߱s[{o{>Og𑯎~}Ϝ=ϟ.\JJ]IYWQdg+/RKrR+kAݔ7) nƃe-ʅpa2b"NLUV܉w+3~ܘP~< y G+K<Ô+/b:&e+8fMy[sP>| T}<:I?().^J>\ͻ%~__b2lvǣaO G8#1<\|LҚ7?3' t뮶;f[mq&O6}ƽfϝ7 [xe+.j[ȤƟILiLT*Nhj~o'G~83gϞ;w?]pƶehbm֮-;zW_5a3Жj꩛sӲZdOG. 54PCC 54PCC w +u%^]/9 _ԏ-~kwe ܝVOѢ'~HPG VFծ=Ͽ1}C&|ww>/n{y /Mٵzo.Ѳ{_d'װ6Rk]/޳̾{ttp|gvzffgi;fiNд2/AoF)TLP. E["-)'-߶g/yޘᎀN"_ +)Uka"a>W~ՓĢ ?J!&vezzc-̣>$CkSFkCe\ )j3J-)|ʉ鱶d(;nZs>w'C71}fDmX4 >J6 3O;F.^hW}F]υ +{E"f(/c`4Ѭ7h^!}w>85KhʙHAΡ 5.-F\ |{Gq{im=/ZtUppdK*8+#KD0 +3{717@AoPa.57d0~DK2qH9yBCOmHLU1tՔl2"8dX2, K%e( %6@du= +םj^A\ˎ*$,!2.KGv[=ZP +$A=]+#zw^ XaF*bZ*SRD &^\4h`C͏@v~'x<\վAGi]8^@(WI<}IXA-edYÍDVa( e Zg:8QCHz,I^PQ%xI-02,탡2Ad jk.s[g-HQ$Xi}yJ&E|Ne2@ZFO AF# zw/؄9Ќވ0RYeJ($9J;C`M􂜇}0 P/69&Il됼:y*LYT~?W@lͥt%SYBfaC*Pn;ӑW_ހ}]M*'(bN)%O2qbzȅ)}^KgX $'mo N_u_q y[r=P"fUdVp=!:qܴ.K``ZJ s^9z5-&i *ઑ9u(I#ObDQHnƖP VKE2jVwn '\G&ǼԲ֠m0NJ\ɰuUH Z %4Y@" 5Pog1ip`䉳ߦq/g(MEɱZ,T"V $KM2TjA})Ï9;g/(Ė>Ȝf+Z5b m @92v~ jl̆q'G%7B#~̇ސt& n磛#XB. xю?S]1a^‹w>LpP8:H"Ol@H4@D E> Eߧh x 3{ =p~@bIb)y`o%65~) +}OztGr( yĆ_ x {L|@mT+5s7*Nȁ3GR]xڅ)|9x^d\ \dyb.pPdmkkm_"8'q*)c{&B?P|5 +b} \hXL35j1|%/h`?b!4У( ^@9 Sa6r%'Pb 5A(=)|FIS|F!'AcIn#V4jBkn    {C.bE aԞ=ag*"tVP*GhHLf)sUQ͚H%PY~5[y6V!zgkhpwK +Ly}\DM3 3]WdkijJ$#d=U$yA +7B-P%P_6`ׅ3$}N+5AIL62U`#%yWFSE E[V\Ks2[nh`ԱCbx. 蕽|Đ +Cű-NV>ߊ=jVy& ޠe=ή0ۯk@G +*h\̰O^LA m(U42fwO'pxa0|YPa47(t%1 Ο;\0^vwvgԟg oB l;e^cdUd ~Pb07(PfG?^;87rzĸ~_|)8Wх5SSrITdC!b@ As/=s߼tD[gnyQAA$LN'3 c8FBm}.)u#=g4b4~noUc[Q8ܾMYBy͇fBHOI8t8:K + |H{ȠLdT}ūoݸ\7gMӫ[e`Gp=ϡnO"@EdYs@\4yjlflV/o.=ٴBj:*n%}lNmg[SeJKn/T^P@QQQ@@PA/],YinmyRؽ +e[ +C>yE@)H;a#ssEfKϖ?,?_V3|~lEfAx8䌃XH?Br_({Jh3[^^0_S 3`p^AkOot7$磐dmÐА +P+6/ +f^ Gp1~)$T1|RB.玦TMI千.YoA777SCkCɦC ?#4wPO?Nfo0qZbNYLn~(VbOKmkZeu8MB ̵Օ??#Ȏ,kNDs:#:'>V\9Oe t̚R2'L掞8Nhmנ镡1!?mb#~GPqEfV 7UO$K.R+ \f9\cLu%uMXyߪoNhkGWF 5Ak(9ԁZ$)93Ε4RfiNb]6*MZInAӉW2v -&Ġڷ6[ɐiuL Ye>3YK@e6rU/"4 SjbMCb"5b@ ЇIUBEI*w:SW.+Է[i>9Eߔ+UKr7Se@p^*6A3x!Y!,!*y-vANEM&ACӫ cuԆnvC$e^X_/.bm[rĠyb g \)ƴ&} IQRVmz;hgyn46,bhغ ;WB;b>tv/KWt:A`jNmg\}CEŴ6NΠ7Ҥ}4E111t"/,1AaO\4FٴC AJd2ȫ(8Um3R HaD ذ$[n+A? + cSΙ# ͺvUD֜Zא )F/ +(F;Lj! +14! +rfס^w Y]1|6QHUU5gvŹ=utsԚSͳe׈2xRKFEMP81H|ZOgk15zq4`.dzXFPV(3Kd&Z/asbP+KVPBCǎOf{~4]=|RPIZ-` I-|M)z圾ЂxME os?߻IocA?c)ґ]쉗RtIZbAe򺣕EHE)B)2*AW8/1/o\#=s#ǒF#l{t%/NՓ % ʸtM~aB4|}MG 5 qtl^44ra8((>' ) 1tIDIz*5+ @};XpgӻG\^km'#!_ٴ_DTXrR,-s4A8K`qqY9UX[~M6®QUA۠]tw \ԮUGg<6`**9{".#u 7 '8́j');m{k&h ho''DX_A!NW1K)LY[FfZy on: L㆚ P 5ۗ@+@t =  򆶤YIVS~AƷST*e\W;^^'cF:/n΢wt@s[\d FPEt$H3>eOsJ0)/(kh@>Ӭn fvhN1{vgo'h9_ >u6EgZcin=aqWu7h4(N~ ="l'h0SFI721MGMl/ 4d`B mȠ84'<@sgV;5Q9Ts ny*rQ(FRiھ,&#{ZsJgK|ݶ ۷6@|ftgB&'=TeH[H}U{˚6˪Zs}I/S9K 'h߷ +T7P1!O I=;scPcƪQm%WY.(IM7ـ Ad|LgGX"nJVx+9J%rS}Zd5LV%THdHm/6%^AfdP] ={}-*[[|SJј*9(#6a"[R)|Υt9Xe*\of{{8 o@eЅ փos{Ȑ選 WtEi2ȕMe*icAfa<&ZOki*QCP:gZd`"/zn߽ʯ'mytSqф̚t^s4{g%wQ5g,lmEڬJgd୛ m?gz݋NP*|3I;! RʼnC䖸 5mkey_goMi#֊NǺ_ъbZԊ"( D K! ـ!!@BB "ua(nXQHU^h?ܿʪ@-g{~o٦w7iaݯ'^ EQG TŶȟEa|1Hx]&]-E"͚`^?eܡaWtLmqu~)~?;I _)OԄi_hY=A.3+ݑq+rnW-n/^2禰gl}'=$qϛ2 &[ftC2.L?+N +z*)ShSes s +)[+,vU%8Zyps T z;v6ُ.3 -#0l&. . x D ꞏ|ces`"5^Y{JOD'"E5 2p!8 ;Bq(7n\v{>F+,Uyv.5ก +.I- +vA@Dl 8p}!:.uXb,15(`ռbYȅK2Uw ׇ _ yC7^R@qX;o;w]8|6s\FV[a4,/Aqp[k=;\ȯ f+7@!H26:C6 pm mlO +8{q\ׁD!P@rԣag +BdIR&ԉqMYϢhc.ƓޜG{a87Nso5dX݋`J#o;A ccx⇿LNHVvL7(I sU g F3LcL.SITS󞞹's1{.bXܛ|mz^y3-9D^鮒d L4T ~m8d{嘳@_h[;V'(PJIki)%F'\WTonuwWIeU'z+fjol/c}I6K m+ΨU\f\VnFeoÅwE*I> fh:֥ݰ˧֚.n&(袔[1Z(>D$h $k+{*%KGuS[o{d<5mq|셽qj??gpzB9(RF/oeV[׃ZE-M]5rI@.i' 7crk]:_Szp؜q봋sd 2hD Ӌ>n_ҧʫW\[1(\Ax*P& e~Qaўw5rr,` )||dh2e"1!>LpW1CBzmM><\( WyPzѿdf--hxomP1^R#g¦iIIVD6)/D~R:.rЬ,N/TV ֭],[y +)u߃'/RBHoƒHgyQ0 +L=ED] +DI(>݂]+Q[W_M[f1-cC\ÿDa)5:6`5\,+E$Eq$rV7z߂jZ'W ۄpN%4X`E3@D߅P Y &( Nv /Wm FI,ˣF9"/*J~ ?*\8 'C| X d}51RL܁i k|s'Z8B$1"۳ uod8:;r8 8DC+D8CN@;)/S~l ZCDݑA5"*{&#Ud8p@>3Nl ]80 {`ma CVsX@s`5Qd3c&?.n)TC*בֿ$\)/]N`,w[V6/SW1F@g JUTC4$"1 Bg $*AHP ' `jAj9H7Z $d;A, Q^jC>Ak6*8$l(|g<59jCo_j@nEDioK 0W, `{kX3_*yBJZJEZ n $/F.D>y;5?5HoCIH>,1Sd{jC<, 5ujS h5<:y)!j@Ky4@ݿ4$ig>3MM$" q:y;*CaӇ,.1ҽ?'qL.\|Q&$NzEseP ONM%O_P@<Ґֻ<eh@!Z jYu&o5C\D.S^GU$ͅ`̐n1p=pS4\:9x Va dYˤ~Hg/$'gK'd= iN%  t=n?e5&S74!i HCJ9@ї^\Z;=z4C1O# + FI%>aOЀ~9x,&i<4А͛lG68`H7K +ANmtaLs)JM + + +.QY2%Y")u"bb] +q@$8@50Eڿp0̿bv9YCu7DrV,8dI6Rr<ĵH i(ߴ׵G>trÛV/,أO[C0L]5SI =V E fKt XBkUSjOL~Wi_  鶅 ^9k>=퓝#ۃ͑QqMzZA(VIXb>Yʙ,uIr- RzV]@j +齒B  dW'`#g! ƽßo=UǹC;|{Z#"[o]MIjRE6') oTWV(VX5֝U[++h(\Aj|l>m/^7K>fs,ؐ'NYJdXʮK2_f dzh F<ߪ!zA=P4;A%r@Esw]kЙM? _p8#,-:Ym+jQYņ<YK G*hez}I$jp^ixo\GJ9t[ivO=W{y{7QUmqYd +݄/4s +b)R$Ī8V}pKsةUu_8t#Tov4t_6=sꋈw]`X ҊsdU4a|S$$E#LMaT! LdN)σM~kcK_o}vvo8~,x8<⻘4j̒byƉ_Qy"Z4`k0mz{i4hހ&ǁqܵy 9O"n|@F 'gR> ݈6fYM\d2m&Vp73pp,gnWA8vx2 5c/OOp>D962{"1>m"S^gEYQϸĐ҂z6 +v|yl@jGr j`bCo+ o=]ޜ3a93WgC3"㧉1ԸkxI!/)uJTm sE7Cj?EXzf?l^s_);xًٳa'fKf`cfKʂCgy!$a~W}g{e(]P;# Q7la +8 +` xd( yBr&gp>;~`}juBR*G!lu))6<קix A;/CԞPH i =H1= bPz\Qoh<-^(eCUM-tZ':-"%,X8.λی-~^m灴\qJw֮ udO~q|2PG( ac>lpNj2[Ke8vGEłH "BH)$${$!H*(( X.l"(3{9W?p.Y{ofF0  dI_}Rb/Wx"xW]^ <|s8ųq0}<Nb8!cVx} sw[1,b]YHLelQuҬ`ZdY-IVQ¾guDY@< ␋ +_x:[9pˣ!5Q-e'& +㤞-Pq$FDk +(ܚ|9ǻenkxn1P` #|"Ȝ̍j]w{MEP[[L('1F:רgKjJPER+.4ŧ3M7XN-Kt_>kDP"ga^c'qqK>˞^=SgsוGŜ*OJ)*JS9ZSXW +tr]:ǐ,ȣ+*,4EC>UuӜХKe"[q`/j, +IgMk@76޺|tŦڳ1ѥ|~y +M_La˭LQM-àͥXL$}9UBȹn&;t$=)Y-0epD`anj{9vZkgoaGQudC g Kv1[F?!RS +zdH4%Y0aN8u)H d9s8g[lIJŴ\u]I㚚G1hhjsP+]ռ\}xn~sDr^G{TAPI =uz:ڬ5>tk7ͤ﾿Mw/uk zDs7u#)y)6YAO9e;푷T֦P{@!AsSwZCRs?U O>Rj&;ߨ}M[Z +W}^w76MUK,Ց=8RAbyڰsԁ ֫ uTqZV"D '(`A^Iyݿ20duQ琯UPi5w["I^OjfƵ<ɕawY9*y;zZ]k^wiT4لݔxwg 10dBVI۰ՙ!NG|=xa~iw\p7?W^)m,m*m+w4XƋk/ TA!GRpn .q.qst [q3c>VFn=RRL{ e"Ibw:C/8hΎCBh$3b\PpJx֮CyK2c~q_vdrT]ptInpF"Sp},t66!l"FMl I3Ff9X!AN{H+RƍQgSwy*wlHڨTG= cpsdX"[,G\Z $7@t6KH  +/im>MKH{ +D6XkBA2]d (["O@~bN{@i 9Z F?x9 ϙaciǩF9ݬۜ{1HH@?!i&Ri#g.Qi@sTnBz!<y1v> eJ ?Vެ#Cbhw/e=(}z,~L:%|HŞcdA@6}GnRŨj=˱j*n_J2}rcf32gҎ$MIٔqQ~7לG᜻ vfOJsE]Y*HPkPNaZnZweCI~T~%c"7*kHNT$Kc͢ܧEw/ sGxvt'g@B6$7ϰ&5oT#G:i)E edY$oH I؄ld)C@+R>}/Ç}07H^0iƟRO~O$,i,K0)QN|BȢ`z@`/A\$%%!5v_k]7t 7S.lt9nF f=ް|܁Uǵ6[B!ڐgiznT[$߮sڀxoʐ8dgWos0f3iApZ@bڢ2Mq? n.a~DX+"UP"  "d&R0 w~T]sJ/)h +k0x QbF-&*E jQQDkbaĠ8?R9Q_3kỵ@` ܦO_,Z_t86]aɿe?'#a dëcV[L:@?}b#o$`W{tC +5@; A_m@%kPkj~CfeK0Jjn@A((g4:I?|kXyI*x $%Rtfffjj3Q*jS$bZ\uab'q(\} Tp0zSs( 2A hBmT‰c4탱!]~^qD:M"!#M)Xag +'d&n,D#fa$ȋO0K!G>(B'j m6s+.\I9MD*9N` (7Fx!$fツ^kx]2_K,GT-t@B{,Z_]$$)\Na8XE= Ìzs\2Nl숄^ RMVBd2ePڠfV*`u}P\\cB瀿<JmQۂG0ց~Ӯ^ 7Pz<^O$B°7yi}>Ǎhc'|>B':8<WC𼍑2M0,؀oNXׂ䣮wvkʯHH|;čWH^: 4.x#=h1, -GrR:" `q'yq~=@$= &j ڐw c!o& 0 *r4bY0CFsȒ,!M[t=NU")3`^D`0~폰I𱤡-YI|89H  #D:#eDN3afȎ\E%+!_q U7xw܎ pN}!kH) +'c-uh&W؁/1("B`p7Yke9⬐˶ETxATGLroc?KO~K RSS?{i_c#:u6ᐼJUOjoSp +'r"Rqΐ1QcM#)'XkF/%mG{~B?d%KD.vX5u3Qi`slBE|q볢0H+z/}+K~?Hcd`v1n O ?dh=L-6kq=NƙLR +=d,GJfb *`[%ƶh>U^ذl`=>ԧٝG0odg>$nޤ__I\2s)pFn[lu4v?5c5vwֿ) eG+ ˙%5oDH:2p~LξɹΧ}⏰O2t.KC 7:y, TzdVG[ʾ=VF9Pis.)/w"`8P#~}bC:1J"n!ȼ=sSbMsF])c~.SPnrn(W%3ʓNcžQ[=T ,BBxyɮht9e5hp[Ԋ˓vja̭J9jsT}vi.|oC#$ +$Z-2dAC.q {'i:&C D,{ږ^;2(+r9gJ' +*GUYs[eu-FvAdOse}N6V i(&A;}_;c䰛p­s}Vt*YIVe˯W6嵨~[TsdJiujejekq@H[2YD,%!iAGF>s:jh[i~W#J:Q#gW *J-eٍŻuMj*UgirYB?uz@Q5B];sk:~#-4)A)ԡZ I - +*HDP]@?8*~Yѳ}ssv: {:,{~'ŠBzjZƲԨ3Y i%9ՙp0W(D/D)췉y@4!M?a{g6-—KR.~4qj4h^\:Y]_^+(.*N/gd)ŒԌ 񟱒@:mCySn~c+ǯx`q_8ΩZAd +ёTaiEfFajŜ¾#E%/KiG $I;8-88 w>\/~e*=3rpAO^?۶[cK8!)LjFU^Y\Q/?//[>_LRZbٛ$8I rԫ⾶oý؁{q{nvh䘽a^k]gOhV0qјĴdL^M0TWõO5Ki1oxU+q@6\ET)I}QFˣ{*޴!ݖ17ݶa LjiO$3#>$Mr,ȅ#q)MU2Cu6d7m\N ;~8I jTDfl\o,jo: {FeDzS{“zbb#; WקjHjNgv̦_ -8$t%diF4;$ݑFSh䑎T^Ŗ86_p& ׄ!q쁊D$߁!c`V761=/{5JqP)^^ >;JBf6gdtmB᱃F՜ACƾ,ǴPOhޝ6wBš(whñԉ9mb%~cPI _}8-ۤН /οQKrk{5.T@%uR=w1щXE_R^K>KC  />/iE%FoZgaAլ HkU-ɫV"WCW9FHՔ}B[Z~Z/9})gOrveNfB82GuRbE| |#5lYwT [`wi} ӣ}x={ɏPcMeC0cy^a[I2ކP_B-7:=P&\hΆ80dWPh' bpN't/}hc{6m@]Ĭ Pet7||ϔA$י T%OX黀 >]4.#`\5ƨ LϬ_9,P-R ,?S@5"Ib-adVp'EBP>0O]QM^[TzYuQ Ɛ9! I@ @ A@(rUZPE +*Ȱw;9]笇ظm@X  o#D/a$vla,|Fk~`We0;xۜ{!p .B i+n hkrx[6?nǹQ'q-[FÖ8āh=н0 D Aq1O#pR=%~h@m 3@ ҵ@ez$|e[IZ؀ ws*CfV=zG%v?&W0e 'waO"w6x   Æ9~/ϸnl$;C8 T7[ځ|فa䮛 N!F#{4i!U!_ٔ>oA%W;9-<þ$`ODfhl<%n|TQG wN::>r~u.;Zm`.W&<AȍԄȾD WA3JJb;D|IoKIyO%^H>J3*dS +4p1:?,y7s_pyP]yW~1GyFԦЎ'XGE6&Z,L( %;Hi{I 2+F,,jB&)&:Wn*J.eW{;i4IsȒVIT|(0g-$Z.UíN2 +TU% iC +nE>rSiT՟՟y z ߾C7u27ۉZ5/[|ٲ[WjʙN[QM*jmU-M-4cR<7U>42_%di> +35LA,ty ]lrxOgwۂ_;CO=aCAֶ2fS<֨ThX%B}0/D=$קUR:U)?RHӎ1thkuNf Gkf,ںkyWS:s-=hUR*S Y#0deIU+*"0$N%ǘq5@:g6t1f_Km^t?p#`:Ἇ7R4ԲwVW e2raS(hr+F$!ˬeHP<3Sd[X15@ cbfq݉Ř]K0W4t QrFVXY_aS֗pRK qb\&-O+=JA]4_8̗-,qi O +1P= f<^g̍Kv;φڻp;iSe #ݓ &ZJMr`:/2O_^*yNi7.*a^\W<ƴH=;af~.al̽s0W0}Wu8/l_t.ҳWl:j~IH (" ;BBB@aG(Vԩ#.uSw +,0EtVEq=c= bʎϼO;~|}&GPnj'Stbo~~́ՍҴ껥? eUe>6 S9Gg?2ɃVEnvK7rk. ^H +/usYq[[ 7sbMFuaӶ9Y k +U E┆u9&NMit tIn3 .3^9w^SǰVՑ|פЭ37\X%XQ"L{~:ܮԶʬymdmC9TeZl7$chI-if+ qA3$MScCV{n Э _}#/zq|εIJŕJ*-A#HOr{kJxBrA7좑ܭt~NZn2I##rߒwYQ۞7<{7ú{cDfknNS2KR2LH.$ &zrX艨Y? +^3C \?bY>.Ԉd#hݐky˰qooLlqd)jMlp-:2{-O)zIH8!J"HX>--t?x9g#0 ee:@i5 !e96̰p\C8hՇ" `Bʂ,R)!*Bk ;[s@\#/x7){4<ǃ.fqq!cBE1"ST,B%W"=U^jo3v+w)n࿡) +>HmW] f{!և‡ T\cMPCj1de6C!>Q DHhu«)5QʤL @JM 0&jTًH CָCR6 Qrq Zلq鄏 gLQغ|AACil2} fl)2HϠY_8!e+<8!vCKľāIp1\h"G$wH +.HpAFڕLw8(XahS-^dltf',rw6&`FQ4%oL::!Az;׈Qɛo%Cҹ7I{-GK+ D4ȝP*yLa5%B )mP1oL6Ɍ=~iޫԆ&7rGQo1IyCWW/dclLg)BCBt!ő,:飔c}!"LPƷ:̚ bϱMr6s_`kcN8MEqkKXܪ {H @ b B-@"D@D +A^VPֶ^u9ߞ3)1 ŌyD>$ < {>p}n0 1[ n32+$lw} b:XۯQ)GrU`0kq&LxO1 Q f#@O[ a#O}kTm=0}ډ}Bs"oףX}i$6hWѰV0+ +s~1e9XET2K^Q9A~E6fLH@S@V@((C^ +d!o5n3aNKǪ]/wt9Q~uqXN-Q1J8yj^NB03S?*?g$z$ {s羚P?llew]O~{GI=VkȉŔe&tir:NfQJ8Y9$Q)JRR^y:m<$HX|\^ԯsv-K.O}ݪ7rV>VANfgf1ӋjMOu^>/NPJԷ$?IIeH\@\k X +:G.5yionm>KWg(Kɔ4m#%']ƍϬfeiƴ$:7<-3.#%. lb$ zob0[=\&:7;/j;E W_v_U*є(ɧB-K^P•Djω^H;)ʋ*|˔|J d!"5.7 Eg0ri={vUM-ݥ :gv_cĄr5-4uFxb$[ܒ\A?LQGl2d=iv'poٻǩѕ7w6qiD>YP|]>&Q*4qw mWB-PA;?k#t{h5h0phUw-uHy^/;,1 +0idҌ,M6ɍOhoht#$1a-0 pF;0r]m3`fouw)^lw+{/J#E] J ˒B'Q:*(v#-3>xJ÷!a m̝`,߷A ,hrO-i~%s0ɇv9-t9(ax@!p`9 l ,n0aߌ@v;(ݎ[G%];1MοÞOʾ\O%(wƁfdlfGmrޟ~n^BL Ѿ"4 I\dLCpfbc!Ń5RlOh0P¡Ej9Nh8b#MN dBBgCbrDd9CVI;hdFo۸O@p꣞Sc>k ᳐ENCP@^ᆌE gސlB@|<:S!RԨ/Do/G [1|l hf;U:A=*$(j='os背f2N/d~~C]'^OaM)^Rq|m$ y$ rȋACrr]3CS2.TkyP~@ȏA~6dJ +|:a9z[ gBƄ>c8i80 :W=79>Эvc4ۂnAPAu,lȗQ!ѿ)^E*T'* d#d@G4LX( aaV4D{%1K튢|O"Ə y%~Gnwv?DsdڐT ߳`F5}E=z&L`dcn= +'\y0.+\2lZb憘gMOsN=ɪHӞK{*+y&O\0TAtLi/vNuĂ 7ucżsJ>?.\0s}Իym9,?-wnog\?]}oD|$u0R.`ḁS.͇=/4[/[$ɿ(*n)1wƎKNw=Rv<[7E)F$z".5,uL,Xo?T48˽oҐ17*fhiHRRF酒EMs-3ԧL4'L]c;ڣϳۋd)!J~r}EoY{|_y"űo2ksk._YB|Ʋ.͊c/5K9P|>wϹ%VKedZ n)J{4/#/x +b}_`߻FܽR|~vLU k,WTy|zߜ_);qc2i@Vs%dM}Q ỐOsڷYVp3? c> tF}i1\Ci`mrkU{*7iw<狹_(o3~n9h2m:oHXնf>L0?I8XUA)ғ`B(@( =jjA@P((2눸zQ 3{f{vV|>_NnMF*1&8xxot~ |NΌъXQuTY٭9.}|gWF>UVJoO&51/'&Tſ +NZ 4D#/C ++2TVkrRtLgve뢰%1Gz ;ryr)R~1)ܿ>YƪLa&KEļus->TC"{٘p#W7 ưnA:hO6zIw VzE':J 9U*%IeQ'Nt=h/L@ +TP![ ד`]tk 5]Rwҗ]&ok7BۣKq-IM79'LWBȎr0yL, +M1e?0Y~rD#CdUV&z 0_@]=hxVr⸁pD0`ƿޟ:esZdjJh*dAC1b)VO(P T{kn~x{oeFgC5='ݼios~)wC,D쏹k5t$9ǐ27zO17ml36E|blՋ6<ȕ~WNP0- 7HP5 #(^C}lgIqLO΅sd?8{ &`V`ǘ9f32g̠촋#:JR%n+Wq gC5(~/r!Z Ɯ% Ygf~,"/|&x6dtmGUnߣCnO6p`sY9P@ -HUY.B )RyLR7*71[hP),SOeNwen6sew,~^p\O;Cde.|-{2!aYb3V]5+ꊟJJMWӌV}(>o;6kb6ە/a+~*p<@k:> }Kې_|4kC:(r:k!T +5C^pZ>}w <H[_Hh \~:L:IvMQ" +ְD{P9Jڍrw2Iu|u &9+m8)@ g)kHE vȜ` dYTg;Av5&@ $$$6!)67,E*n8RA[EQ}k=ťӊ֭Uq3_ۙx;}srpig0 bɴA$ +ZH2E1ʴ JYec'6PT9I~(“Fp [83ؘǰ' hiCzu%icj&v&ON߃Ѓ`O B Jπ_.xzǂ6p0~b8A\4uxg3O>C|x\-,@0Yï {M;H3)W;=%wÔ/x0ȃ&|<BDd,H:τ(} Xict `)$- ?&^[?i >Th\H>D,Ku YB !2m@½V"i EknٓWB_ a5W\R'H#ݘZXc!F](#,2S֛CMʆd͔dpG#vg&W߉ڏux!px.S lH`8G!ք`gr{Qv4bgمD)t01&*4 _c3fE;v7{^u~%;4 sI\I>{7s~c +RV$4ePͦ24/-!E: >&ϵ|3So"j9O=w60G=/xߑI;vM +3 " D[1`@*&D3>+ߤL_$~YT|?V.z}nFs#b{=bwUywTyߥ7 Q> v&c a$7d;3,!7#ўL {g c NrPչ_Khȹ&v߅s1D <}Ip"Xˀ|ăs8Ép'4!ιtl2K %dsobɬ&?$3K/w)|*Ι=G :Pui㟈&LUw( dx CIb̀B R2 +{ƛ*WI5GneЌeJѢDNgN˽^w05@rp_Rhhɠj_Ȁ꘴?lww}Kop[b RH~6[EFVQlƯuNV+.Y*Ns:v(@Vo?,r=K%};;RC ˷W$VCؓ/M7&wУ(#Qo̩G06NX'3ZȖgaks&%C>_GT䷫I+&gRVRZfQ6Qmv>-"{9צU[^RZ*^Q$ސA.o9wGxS#VD5\j}\?!m,DPSO;!oQzR[3~:7SohUڻR]\Qo+/]%n.Y'k*ڤXUMYW0Y?U)aDm{gՕAP_&(TLf ֦CnAlMU``|wBmMIs2<;?n-_[])XSY/^],+oWԖ~*鏬(9]VrUQ.rՕT iiQohjl5M]_M:4:-p|澮PѶ[ZT.kzlXпBаliTW}" aQ(B @ؑm( ""PYdY(.Pjg: eLm 8ȢTEgǙ3=a>~s>9 +f?pfތ=!)B3&w7- :íT~!3߷1 ײ~\btqjZVWdx,ҬdX]~at,^}w:}.();x{Br%FeTA:']Hx uE:L8˄M|j2$Z%&EŹ;itK+L)(I:Z㟘75Qp|o~ ^BD&}8jh]π4\nAG byэWYoP|&lmM95qU;U.puVnTUy?? Ȫ=us5:r%j8Of.-lHa^ttz/Z}=b]Zfx;:ƞ͒G7(=:}B|BF:V7^:{Nx +۵W:?yO^T]ׇ^+F5);ǃѽAOﺴ4BU\ۘ{>ȔF|rT^^v=#}#mJ6'jEVH]r.;hHO2pl`)0?cWh-KjэcǚD&Zn*Q<ZG+2[2Q)'9O7^F;uߑ.> g_E̟d+?Āp{߆L3Npp~; ơwRL2,X +kmb>E !T*ք!>8^LI dlT,q*+N׶>~LW4Ӡg3s'|:Dx ꩻDa5`ɿ | 9_!(_SW"y֑vuc8aڽ԰ye=c\\tm2YYpO'-Ba -|P&(Pq%HђBW=iS `0F3 1 +Ә+{XY2kY|uS}.?@O߁2``Aa1 +5ZJTP+Л^4#h=up,08XKt0Ļ75LU$;x #0%uj}4d/K P@領F)AWJ) (t" EƖF;R!"Jh`8 AhyǵHsq'*.Zoω7r,ɖXD8(g?E,A660G \ QJ\ Y ?ψrȝ/j:YB(>\E t1QB0MƲ!Y 2Pͺ(EQK  gijoĨ;Kx,(RfBҜ0SD% ݂5%;_t߸Jkx(i ZG>b9 z }z3u< +qz:O|I~`V=.h` t "7N qQTM ֆAH$2Ī3l5c ~[>`-c5Sfe~ #Ɯ}Ƃٽ0L ƋBF cJjH}1@LIJQz#r߇zVM#ٳn'AF}xiۻnzЂL0X^W6!``U8ߐ4|3-5.!q1GQkG,7[޸VNa{rq&ՐΗB;_ y4F=֧#TX`̿6M{W"H7Bd]沼5_g.}ʎ{fk"f|*k1vWZ~޴b.#" t?>1>Kg O.Qx,ŀ^ `38xػ,|+>Iq峜DKm%8opks=}2f43J>}pIʡ^~3G 4@x{;W`2@_VBkcڝ"_!y٦9zw,;XýȒp>IdY>--\!BEݔjoBWhN(Ԏ}ɂWD+,z*2m;xBa"4hR~/C);ªțfc?]Y뫊]H[@Y1BVaߩSGB:j&Z4y/#g$H;$l61ױTW*H}$B%M\(/, + nOY xb - ;jp:DUʎ0\2ckY,ͦ&k\wUEbʊ:NFQ _r*(VP~[|(B0l5g>BU t]*щvutc%߱0Nkc,l 3R5n^Ԑb]P+uHd|&3e#30x=KvHU=ki;m_0ptn.>'n_Zm}W +>H[4@oƤ`GLspf=1Ze_LRs`6`a(nQ5 S≧3JӾU_zk;qolcoLc9a91 5Rk)C:{PWX\(nvW(hdDzs۵ DQ:8[3.O08ĵ}ݶYٶG1ֻ5]ƶD\"ΡKԼ +*/= 0"&\Bֿl!:!#ԑufq:': ;#;?:kXݵ&$F$W1h|~z=}?o ? n3b{˜XFӛ̊VsBs>)Kk ~ׅBy yV?Kv@x D? ŸBG0qLd0%c Mc{ xgu7v+{u+}u+5=Jp_A_F9\w`l7@0JttTa4F1ned8 Y8ebIeI 52@cs'?25P@` EԔP\ 1l1 l01LF>ba c/jwJRC,Hn!? ۞ Fz0EYf&\TOal>/r* QD('UDE +尢O\\DEM‘0Qn ƹ28_BX=&P3] ^"°O,ߨ[VeJڤRUUG/Tgs⧪+⇪n}Sq[{jVwF@/eP`}\zi=u\Ԏc|p<˘VtS~U*~QV@do}v5uv^ͥQw5y2FkHWɕ(az{tGh.R0#3{g$inuD;nݽov%n=N7coK;bOIǶˮޖ]}$7,5ƣSk<:OQ0-mH8&dհd'dXdYIgs3e]~õ5NW7HL4\rA׭][ަm}^9U3~fݐ{S"6&d2:HF'6Q7t̓МЏyTbq~t[]͙i{iBέ\ΥM8t2&}w }q_GRJEϺ4tKGtfmL,+zwNq›NxLdnG*/ZN~I[rMuG5}qIK@H &8L 5! !!!!B-D~AumγMzvqwHEӒԫ|90;Fzx5OռQQBgC$kP|sKMgZ;> .=w$e@q<;eb6tU!ŕFQ=.Q#U"tGr VMN|D#2fo+) ;i Dokvpa2v\wz`Ys:P{qG2ˬ6d:jH343!ZHXi\:!$;D0ut [8?^|WA ,9ai;d8У;^rji&F7Q*Vnbh +CJdCJ,K!).R.>H_RT +DQ!5({AW ie$J/wwT_ZIqae-Q(w=|kXhރk}twKfWvzoUk%ƜyTe7J| EueTam5-hftd֌W RUXz͏eDL5;hϴ`!@[+{} =L۪_9w^ܧHr+pڸVwU9)ECi6P3&4l6m&lbB&fBdk*=[\?ޅ;`@Iv0ʾd Qџ\ЛvPԓ}-bnV>h0-<m[8u{\r#?Z_#3M/eȐoewbp8S-]YrNaUy,"_M|Tn'UԐZ\&MaSހׯT%=Oz{U z9gIO1!E41C׹z. 銌hԯ-iw {=k aC,CRDNئC +GgsYg./1n_nߘ7wϰ_d8"i46[4X\3 嵄 37%_A_Kn^ ,Pisc ?\tN B`hDSEoOu̐&ʡ'Q <8H1&FTȅ?G|&^(=7ت.Dv6ltvS!{lOVԆ!aP~`*ZpӄqtL:R͠LCsR|I_EuqӱKO,^&?u-Ñ#w6N/►*♺2jw*vEME4ʈhn +ҵПƃL##Z3p'S2'2h,ď,Q ;mu\ݾU{w+igaˣlnsۧ.eqhWD>F` }lAmstl6+Bx4sPzd\yʔŊBb5e Λ]7w+w_cmBKI"KPUvv4 t6L睦y 4,B8,pc7p5aFrD{̔=jG|Ŗ6\!uSiz)kN>*ve]]q+aMLtX*b72):F[\B:qP?Y@PU/bT5?F37Y-gUYv|cVtbYk25X2}ۗiӾ-K;4NҌA^6 ru КHW?w|98/#j8g> ̛fM -KeKr?R|Z9E5.fo/u$F!@n ulJA@5@XYE#.`ǕxZjUlF2Z;sng.8g>>&u)1$~*2~DV]Y$ILLl'bopk9@[D;W"!+H^૫QMmG3ӭ>ȵNSWV:u.NLVH:.K$Mr"91{b7P.8EC`{Jm]inTśXVʳmʲ Y咢LSAFK^z:kQ;kiZ2E]\PUnZV=CU`QJV$YNM.)g')v'(ds8]ٚ&3&I*`Fga4g˨}=@W:Eh[+WV(DVerی"ԒJiRqì5Mh'*o{DZŜb'z|K}jpvAH{WALl:O^UniCan4:vfGɖiY6I55تzv++ETuQOL9DU0DR/i?WS (l BԶ۠rW~>oJn +㬖u1D mRUjNaug}Pw5n|5s U3b7%zCt}v=7[*J5PjfqoEz S:BL6ψiO3ڐk6V*Y%]ֺ)s`7΁-Zòf&^l'~L !߽PvK'^e.=ӑ#Af'OЏl=R-4+Y֕mYdbkufM_Osak%[F~mf6zP(hU((H"y5E/_fm7A|oi=3zj,}{=e{g7>=f>yz LwoTy#@D rH!O.tȇe|5r B<x GhfL{'0yZLA8 c< +0~2$o򏁜a?O@ ([@z0F=7 30z ׋z9cQ- 7ö́1j5򄌑+)?W $ïK(ۍ(0` +2=1 |f Lmo08qLJ8 ƍcGg 0H YG6.Yc \#'+q/?dyTSWK¾!/@ Z*,ʾHHXHԸТH݊8mک^ENw~NrOqw¢)^/  (sHlCbh&M+_<8 xKKO>Nctf' HbGZdAC6=|Gwѽ8}|K ߑgcr?F>c!SMA\=iOcOg#SbH5|'.5ԘhZz%BSE]3QDtNSEWG-M1fO u?Ax}aM>nۓO_)S 'zRC*}I d+d[}̛~?$'7$XW<.z60s;pv*f} τp-#k +,IK`btV`Y.u˻w52U&߽6]n|ѳMF<My~ozIfC޿Ʀ76# uAmaG-Ƽ\%9Zy%FjM0=favo}OOՀ)c>׬>~?l\Ȃ aha߿~}'l_o0'([wb+`50@hlFcYqeF9 3Йۨ(֑p< u}ֽK{BVq +nn ++ogY^G-=w[k [?0^c_t-ě:aqFbe=-V؝՜vkNo o ;(61'ǖg;͑ض9 +|/=7SЅt>LCO!l/5ѓc;)մ31bGں=[n[cm\L_&X'\=* 1X쁭! Bwa0$Ѱѡ 1&T,ҨCh-Mfٚޔ$7'V94%6ҫVovZC,nKCQQ}܈.K]¸c]"0Iiq' AR :Jm(sEM^ğPи(Ƭ Ų\+gjm܂RZW#P BUQVW-Su=#*x!Tc' jLޤQ8' •\CT" RFM.P1ha, 1(6-+Id,[MQPSV +;ej׻ȵe=nRq4ew8],X\iP%28Z2 XNvQSK髂X& Z:\Q-Qg9(J+FuiWIcn(YS üIwypB{a4ܷ 7XFU7(oE42 ÍbrkS,klEV^UM*U.$W4 *\ݢXq04k 3!A4(ADqpU֩Z +X+ +8"8KlUk+.D:Zu}_Z?y}r?hL1+'Ǫ$;LZ_3~Pec^:A?iÖ8g~&h;+Wƒ&^I>7AR9{u{d*`}¬4=f15x/j\jѫ;|v G X{EwϔLmn5l%$ ݓWoy?8lJeҁ529ega:__qڋQq=C89NwnXۚ]2xuj8QgCls4و<+al܃?/ b, q=0DGcr504/"~}Ts{re.r,EvܜGD7H}zQI;q-ri9Ѩ }>mmV ⭱5^tWtSYNq̟Xbߢm_6*m管;k莿+gs' v}8 .B- 8Dz 6PF mmmmdۤ}hͮnyc!xP:շn+9 +d ;H΢l@@ѺEA0[TV%=вdKƠE4++JDuIU>%Kſ+ +T Oﳿ&3{_3[_ wE .R.uCKɗ"ۡrAWY E ] ombj/e?fSXhf? rh^U?mwfpػ>pbP٭P؋!vI/3xG@S` +j'hjXU5@¨#񥤆5kxk ^]zB/Hf,d~Љٖ@ˀO&|P:t^;5o +@] a\:$dwXNR]% +RJ:RpUu~ߜ%Hx]/dϦ{̽j  ~O9^D.Ue纍.O<Otc BTPwk`w%襸MtlVGKf#d<3#Kwa5,Ն!]jr}va2v7Ἰ}[S-.R\ +@!nӀ\!Cu~a/ZlEY`<7"{n\$n q͸Ah?J ŀ2EyuŹf)4S6b*B:Ul| 2ۚ0#Zŭ i4UT$wT9Si̴MuڴWUb*PԅJc~ 2W :b\Qq}nE%󖱢2YQjPYl:e^Ɯ>iNs8ar8.hvO820|aj|tmGF8BF"xY;ը&(n1PO|3Bq"zQt8/ǃKGhE2 jĪ}Eb'{c\jl!B!$K@$6Ibر@ 8X$vl'Y&vL=i&I:Mm433{y9^+b?uUL$L(8/~?b.JϤTJ> FI l +|,Ki #ޖ_LAYB e"dDG_ŞvQe sIZOKB/yȝK3kFJ0}n3уL̽{T +rr\9fW 9eAEEr5 z!֜l=+;ŝ\2S cr0GY)kXP!JEBlkdJ'+RRD.0ԓy 5LG aŸ!5Gh@h@DЯ$a!0\̜/񨻍(#AḦ́˚Ͱy 4iӥr)uQL6WlzUDQs=\,+ {xw/\: >ulG<>G~=<𞍄r98 z1iVIEזƌ3SZPJ\ܞx"Y|RԡZoS'&>Ij){|K !hQ< 0*A-3^d hE0cJ!MaCqG_NU{ʭ.m#۩mv4BfFԤYJh,]KNi~ɦ[ڴ߮%'7؇1wޭF0w]żcX[kN&U&VfG TF4nQZjFn5r]FЮk/'Yt6~5F_EO,g50_.|\}DW1y+Zu/iВFEtUG9*=QYhc5T4sm&Z@bbR&# +ӏJ!@xU&K>zpu6͘79o=Flc 61]ASF4[˩ +ʪnX;恸Jqʟp|&]ՇȗIz"CG/_p3u8mx 8لǚyRHG"^mQlFY]gTֵL}qq:46ZGHX#*qe_k%xa>}g6ּ::1wvw ҠMζm֖Ljn=LnhG; +-Zeijg[nAYè>_b9Qe5^Rs|^b;Gxa}x&ּ+?1s [ܮhhw~{I6W*֕IvFVvǘ:˩ +zy{-ns[ +[gDdZ$E,,siJg|XHCBr<(ds r뀯hf'07!_R:WƊ2B_}(VM* 6U M Me0?;ϋgnse@@Ӏs%`-TMP^q7W;AT(ĉff>XxkU@c^_ c?\p/0Qz:Ue@+ n:ԤnZpC͐7݀3!/o)ca؉?DZڏCKswُM>0U﵀&Ѓ)yocܤQ}E.>o9G윸x~Q`:ϞWXx}ͼ{~⦆5i`M󞬉"CFQl`.~ <_ @]Q }Fi +ͦIٴ66*TL';1E;w<;A&W E8>UQ1=H?y,NxdJ<2uQ-R.iOeEBvWjz/+/ x=K{+~rK NX2Z*L-!Kel%]ϒ%#/X +|* +})v\UlSl}Mbc#?4esZ 4tU\q/Q]}IEcdOΔﰦ)[+ZW(7[sUͪ #s5oPtU]*60>kt&T Q?wQ=F*Nm %4N)h"/_WfWdkr6hvج o"nYo̠6ABmАc̿B$Q~<)p0EaWHiCxڰܰъ_({NV ^ +]dLk$d>=H(aAha^S}ZO#=vn4ݛjfWpj/s'Ϡ?FJ׀7GbCdr#H91Pf蛤^'Ygi3lz2 h8;8R}J_#6{܎~f췏l:lvژȉ醕1aRVtYFtbaʅ&-jiّ" )+G7Niq4%CrcG ;ғ=FYcP'pFnXoEF|O v"-6Q͠hfLΈIM=ߐe41zWCR[c@a [5{砚}>)8 +|`BV `)-,5!Z>ʔULM7]?1nݗbWq\>r{c ;ғm|/#Y.h=?goÌX<5/e GAkТ!#@ Az@TBt]OZa]-3umn~L _|?~i扫t$))2k89ǹ0ՒJT2k7gk[=LڃYSL^&3iH$%QS{ Krٻ>5`:d1UKkR$iAzc~97⚣[XVu'4i^ԛ4#uNpK J?sYIjeC?14LӱظP\!?kԜsr2\ VAZwmꔌ5I^Z Iz-Y/(bkی8(bq1;¬Ay¤c> xc&;b|G:1SYQ1#:As9|ҩw X=|}鄓2v q~ x́GO4=ˠ5½ +PBEE(z<(O=޷z]ɸ-w "N! t;< Ji7N}7PHI2$9CԿp;7qBƝIS0"@!tIeKo4pe" WX0/#tpL.#?o05w1cbzx;~~ 3 'MJpT,=/^`Q|9Y0y\t$o>r|O~|F!Dϵg/PdcE]cAnArKĂܑlX Y,?`/G|b‡hEE>{F)[6SDϣ̘.c x6o>&w -C}1<%ă=&YEyCp m49q42,&$ Ud=LZțNr qO?/ +z%qx:)$D-"d% d+APg?u1q +xk%w~AE?4tN"|G҉Xy8&>y;uvQ ?uR8ۃo>?pnA+r7Fx@qnT\9C41$[1jlf4h:Ӆ/u<;HT}Pem:X5$p 1$$"B"QmCToB +~ZC j]FҊ6\lU~\_qQBYOU"1J F* !zR}/&4w|kuWa\QƗ#.hVs|يs=1|Nw'#k"uqb  $?-2zp۸%Wb7;>ŹU8ӽz4Þ8ٳ'z^m8k:CO`kO]"_ǘ٧1-O$.E&꟏Q8ÑEOg`f_BfOr2lav lpxixm71Fd7w_AB> ' ]8\Q|L|4h9'6{6`ٮf:S-ڙLv.Yݤ]nnbkwrܺ!g5CV_>T-ÈG<&w銿6ZY=[|0,Ga_pҍ6 [tyجs0t%zmu:vMl*[/m[bJ,ѝ.ݒ6x,m!J? +O?$[FM|@380J]b^q!ވ,fM\2]WʶUqKiB}YHҤ_%o5OW̸񢔐O%RkL!jy{Io$('ClH&$%IfQmNpM$2BZ +P )Ҟs=n#ڌ2tŪh1hNMaf3sRNaC1,36 +K5e0j*` +G11E٫`Sg~+Ofz^b)K29sG1sӐc 7_k*ԘҐi:A/OL_LoWUPQ*L,Ch>rp:>iBzeE6l r1M4侍>'d[PJYҪ2 ܤW(6uy8ƓuE^W(6ҜN`g!XK- 5?OY=1#?ov` UyvΟ-R%(ZBe“LUQݭqZ>8,;9,?y™'ʝQxɷTd8GڳX@~*P`ڢQ3a6=$fb+ +rٲWZPėX}5 + .ka][׫m]NVM_jUTXE gܤ:![G-^]4:u&rDiי; ^Q%k}j_ooUwj,\ub3^wY Gr`C3}Qye1LȸfnowKlE~F/zGn)\)\*ܮ6,x2Js KtNRS*4~$'j+텒x|Q䋃7q2 t7畖Kr!Yw]Q{;TiޣTďQV"_ <3:S P4vNO~%npUFQ9FXҘlRir* J$?IRF*ErVe*IXPT!*E9!{:;)`Tҝui />aB0H1șldBLf(5\ZO N$I2Cp0]<^PU T$ +QSo&7h"i4L#UOs: {\?a0G!=p:c 066)a ~nL>\yTƟ3, +ʦ0 '-Dk$F5O465"eE 8Hpj%&*.TӨ(1> +&y{E +9^٬IθI&9]hBm^]u KY+ǢVwdX'!-'Y00g#YT:Gaf)r +/lV&TƜҘXe\*T%R=PC_7f1&yeVr dia=H>}BR8Ο,$}oɽX{c?&ؾc~RĬvywR@Դ`5GQk׋WI%0PCi4K+MA/@t Cc4b嘆HG;rX/usRغv)XHk}/q ;z8x@Mi3_pz"©G3*ViDhe +B*"r8*Ǣk$T͆U[U}VRS0\$1θلyY&7Vlc<.=c6$z =08WO] Թԩy$&ߓBwp_F;~v[.vB-ӎxJd"%"SB ԩN 5j{q|˿C?N?D_/b"Od +fRg>u p6Q)\s;SU[whWp}+\D ZBӅ9 H^!M?Ө3m&SǎXC56sjnݸX|8%:Uj- @oX ^zXHo2L77Z3X Ȧ +󸮥F5*phf,Nc'Y@*o1zuAS;hvcGbl ^;CQ T6`sQl +n?Jp!! "',Y<8}hHBmcj"G:rĦ lZDB4zT픊51n(T{GUHOic{WT^o}kd4hg7Pih2X8 PbxņiuBT#'Ib9/a2a"axFq-ENcEv:Y=k=ן@|U߶^pĦXcBIXcզQ74QZek!0}$-3-rPe*S,1mU,65*N*$Pf)盅"JCơqs5>}{`%v,iȵ2j/e[&IK-Ŗ兖 y%[a)-%yjťeyޙ{D K] qDpFf`fD +5.Kq-5zXTkĜ4mz5m<96ij4Iۓd1w= |zemA6G#ulI1kLslJFɄU&3-X,VUZMI[a(wcm<+1Vl y+6"SH"?7wg:xuH?6#<MXmE%4X2EZ +S,7{2 +ۼZ[b~^*6o]BeKa?LK^Ze}%s4kahEI٦*t۲mPj+KlbͫqֵRul:lsm/ԬkCzu]˸9Dq-빮l-#QW +eʔ$#JLHY"8Xr]+~)W$/U~Q)ʅpEy'<[!܃Yż1t7|ۊQBRu&T@j:\L5IRԀXݭRYVxO^YՐLܢߗuJ@o/K} J#Pdc:9pHG#KPX&.q5َ,Gjo2;uq.,q3l>P/^0GO4l^\NGV G3 +w><\$丌X]9bCJLWcqҦ6H&gltҥ^եOuo4gH꣰+y|'{X[rzTB^i$1qO➉ŞdyRaX,|!S$.TFO&ͽNkpoRrFIII>KB^ޠgS@-H zdW BVHX+' ; <)XTeʆUVU(ebNL,n{OKqޫ)ڸʐ6'.S8\>84ʕ] \n ~OFo }HYSs >Ź͚پSX[hbBڟ8tf`5 |?` 4HnX< S1?$0o.f0fi8Ycc 1Qu@fύC PD3I&s[1efƌhLoiqڪ3fL ((@Lс<؈ =x*)`|W ~KwQ{s+=o^[6 Q1LLjۢ0m,&MSX<`*30`1FkZن;aX"FԎG=a֐Qe۩BFT'%`^ v>ۣ0} ڣ1}t'i;w,ǠF ؂ h?[?CAzdݛX'$b_f1G Dqrº+RW,] Lz?]  |d8paD8vs 0CK77[7E. ̹3_oI}^3vi=EWGA a:-Dr:0 3G_l]BG>Z{#=`7ԧ__ DQԾI@!j{r aCя =aOpaȞߓ{G{]E Ybj٬5{#|Dc1=GO>g`|C x/y=dO4 rjbE 20*;o!"\>'ug_KH2kDT} ** EZnnhYDQA@B"2bM01rRV&NRV8ff\*5qܢo~T{=缤O~ld!Hu'3enDٍ^ӉYDdd"d3AvtS"oq?xW?" ~ 1 1tKlF3`'5ڨqssg#>mj O9z<&ȿ?eg7N&qdOT@EꬤF5j8s#5P{8g;V!}i_2:2G;C5ķQĝL%_AԌ3sӨCBjF%5jH-_'QB//} Moq~$7 /DÁc 9}r]*|=c\| urQDUԨF-5>V9wd4o鋫˴wi0Z"6;ٙ0eG'\;kBq5JN&gͣp y]U +Dh9YВSG|kwqlCyeÆo$O^17x Ұ,\p9bu,ǙU85|z6S 9G#qGF^Qߠ1] sh!ȓx吻|!+ȍpy~.)DpM1lt-C[ :jtmD6toO$xm}qoc<6WL7OfRߛ70L.Ot%wW􎝆|=^ }`M.Ůk:-ScJ O9Sylv M=D+4xB y4O3 : +]&s6L*gsf2ϴaR4{bW*UY[Q?kP7S+}]s_\uS^})ZO.;v{{bs%}4h1' >VlDoj|P[*BoηUX3P>G\=X6rޏb|Y,yP<\{-]~tS\ `*aRJ=ʔXLAҎ"eBY$,W ˔br+-]--YtI#e?!,CG߈.10vƲ1-Zָ. Uc6C}PUT:(PLy!E_H^X +1cx@k[Hb[fB+:q#1&.헥{Rh2q<3I+s#kvxa>Y=DlvBP&-~,"d%ĞXVjI 5bԴc1ZiCvZ3\o1\r{y{lb>Kz 4&Vq.]#4"!RhX0&>'dӀ~M}̽5G%]3G%>4G%VhdeT>` + 38E<gTJ&;iHbR48%LSh@jT6Q}Ҧ+:mҲ+3m),)tUShShdArCc#˰ Jsz2gکOzguStV_ٱ +ώS +˙МSPE9kS+c͹,2L/RXݲ|އB}0 f8*]A +, V@a Zƨe|,3mIU7"ue<-\GacFgWA+%r:!-;klql}Q3dcMW2UP +#[yL@^RE7_W?7Hq؃R)`+5okTg/S |!adg,@PՑXuw\ xº2s/)kS +܍>iޖloaHa1~R=Ci}_CP o,^Ç<OXI-A GhFoz<^ÒsdwT2GvNI8Eag0?:Ǚg +hrM@-H| -/:'֣?<ŕldllj֟%hMFg&9GEq\#dG(+t|+e`؛=vEHrsh@:st4CjQNFi-9c֋]DNg:ЙCGaoA:N:K(gJm5b>i-mP՝ U|ǴUl';cWC(NzM=~WO2|u{7W ?w1ԄZY?T}40VEq*  zM f*7h+;8WYEYy!GsC+-)%)a_ڸŵ7+x(0fl#Yik͊P- %,@=# ^+eOiJWZxR#2Q>_ +h- ZE%Hy!@$ $BТmN!Zҭ͵{3nu;֞vNZ!~>Ͻ`͊O= S&',V iw$uLs0^5K>[R)G{Z +6g-=Xaڌ +pŸQ ?|mX +o^:"YDX\f!U<ຒX`d?|lΞH)EkӰ:;9:rLh)GCN9u]Q-\ʰQjIY̡TP/*IT80Tf?گ>8b팣E5yhV-O j Q`e<y [v&*w _4#2]Y&H4cO79rZM;렂Ʊ _39j&c6.N:tpQæ_/'EYQZ' ̆wd%["G+ ?Xu ;i& }60(Ӱ9lT4 +a+,ƕ(5`1h(.z^ɊLC2iTVh#HEc[LyB~'Z$[s8ܦIpf +bLa5eXX0QRBq*[`4(0zOd:yc/"ɴfIJG=L+s3Y&Pa0JR si:K0PTf̊ʽЗ?]yX}"z\loTBe(me-rA/{"z`-]c,Mb{,KQhO|+UA[YJ*WC][:l1pYUD+~g9 ۀ}M.G}\fN*KqC0TGB_]"hj!&y5*P9P:+ZlW3 tu WH=*gDjUO!wIBFs/QwZǀ'Ɇ5y0(Ȑ_{8CAGL V;V߈Vx2oR/#{Z$y HDGqKU(=C$ s[*e^ Oo*2}QHGZ"țRڔM*,o6`YUHj"ѿK['?m$4CB$!s;ڹ'Zg[#cR3 Hi@R$bi,i]G[Xܪ ;u Fl 11man% ¼"EB̺1q}~ux@s `3]9 ;v#%L-[Jpmcr60%&^JI$"|HL8x˥^ȩI @@ \*""^b2T@W=j>gmt]36v[NvݦsT|?D~;K NH#H3i#ϑm%1|I1G,Cy|G3y~g_2)ѐ,O"ƯgFCldbOajWL#>[_0o69aOƒ #5 &$dP/:jTι_72~w1N.~vp:kߤ0ڍ>$%qαRgrragaoj^ԓ24jZ}\ q>)tvpgp//^_ğYopjG708=]O͙xԓ3I<87+]Jjbf@FRcn)C\vV{k4Wy? C~9wyD)B8%3/ DQU^jM]c:ut='ye&I-`SGch"x^Qy1H}^Y:9?"56qj66LubTZGKB<kW)hVub]X1eG;Kf ?6I:E1g ~s7ڧmFeV +5f4`Up>V.X6!QҌ%!X50<_EugxG|Lw +d*g> Iǚl)X>#"BTGP\,SEsajTϭCFTmCyTG@Y̋ Qo +O}؂ձ!|u iKd煕Q~X=u1cQl2jPkDe qN̏,Q⟠Hn (D +•>.SL >{Hh%kS'F$ $Ơ\*C4 z$d(OB܉(L\|2dp:F87`O9Ia0x'29gIigk譞>'B>e`H![BIFlp&9H*F^r%K))w"KyUȢ(S`n:ظv``볎>VG-}+Ǣ$t(#R#O +J٪\Rݰ#+F¤QsDyUd _Ads6x:ْ>(}T"O +:X5)hĄLm6KaLAn6tۑ? HcNAF?V'.w/Zd=F.V}0,9ԋa+`ԧ!Ð +}Bk\q=LH5|Q4@A4 " F'Aj?xK1#MQ2gLdL3a Yt +ZL ԙyH!RU2d"9k^>d{|~y0Bc{?wp$(^ J5ři h!Pۢf"Ֆ UlRHqCSyRHoԾsGBl$va3#{/u+9Tq/𹼜<\z1:EPyCYH΋D# +rGd $:m:!qAEk-b]g|A#% 9i?wQ{">9*VK!G%=B$A ;y @TQ `NQ#"7#xŠO!m9B!H@+9Κ~/;9_ +4s]QQXWeueߑE=-fQ(̸ `T 0q8QU bզAlVMM`L6{bCRc4how{ xmcb-fJM`PW`ŘՔhSE(4\ldZR[_yE`oجloCkiYΧ6B}3UXO|)uF(6VvЫ +dά<Ȇ3D$ͭlJabWS2mzAWڄf_0'xni]' )vba'luPC!d|R[Yp156v)40wIwvjQ:jXG .@Z.Z}-Kbna14,ttN_tb\KK34@ o/uahj!j6pENc$\bq'-%r?= kgY,zA&Z@q.IX4iÁ=9]lix3o'3#MF{- ~FK.wuNĐv>Q@$M1p2 u1(}\4׸7qț|m‘3}ldIƿ>_{[4Ү)yWP(]%| [6]?>FC#c61qF./l~ `0)`(bJzآ~d|isY;}/\pedZ AwH0Ŵ}k1˰_}- :55u]|gu N|OCx̹7T} c ضa{.0.S0I v͏C8 Zנ"ZIJa/`߈"ih~1/Ƕ \M?Ч<~b*-a8k7刦NƠABc")}gcfcߪ^N*ȎSD2P-T+nKK_ϡ1L4ʓIg#?EhXrc;YvO^Ö}51%;JUhi#:cFg1v՜\; keҧ.]:6k 8qW:Dy{+ePvw9] ƧimZqGiV9hsV8s\ +eJ]*TNuA2_T=z6k\FXᷔaw͆SUnZ=̣U9R%3EiI *4,\JhX|~C9>5fSvVfspN_FъpuQ7N :sh@ h;3bʹvK| ]9ʎ UVd21fF ֌=Vi)=MSf)kRb5)\c7+9Zh|qݚy};`sآ#;EXojz\kj|MV*%~&unS5Qr5.at_7W=hTbF&6jdѳm/uT@T@S2 0 ]`H䲨1 `y ^K$Zf*hY)=Zֶɶv:k%ִܓ?>y}}˚ƎL}%q4bb\9\0 -_EUG$+7ª\eGڔYQ5ʌ5EJTjl,5?NܛEra #NJ + q-z +)?zrX͎1*#&U,*-ήԸjYR)JNإ2%Șx~)S'FNm[q88GE9^2LTaJ3D)Ր KB JI,Pr\%ͪiV'4˸Q2ː.;?^b911AaaV٦0QG%#dɔ$cRf%*1D ɏȐRfřE[*| w)<@75؇~gא2jSHdd8̓d4*yS~NC@SKoޖt/*zXlȤ,bI&XP,cR4QE +*(M+NTV) +M-Ճ%4CJɧ䔼K>yC6&35‹JzYQ΅Zz-X҉oya+>J+)5I0=hD{&3SV$_VqOjlyʳ<˳:8e ,\~Zʹ4\SHl2y1!P&JOոJ? HyU%ʳ*UcQe{"n[FVKիUF.wZVmhȠF΅ǩzr@LI1Z(7T:B(GFe遺 : ;лX_mQg?ߎI~%g#=Rb|J cʥUÛйM\\k1>$mIgiໝSQ;vMG'$]0P`C@uQN w+ +}|7[ًO +FwJ]#y‘PRa#> eԥ8 t4v71qzjiW|?-/҃ +܏WO1xNA^SIAN$'gR,Yhmy׵u/`ͅ35b%Ұ>Z ҅\Opn!p8>c"5ec,ýKKf+ų`ߐoO!|z-Kp\uCѫ 7RnWosܦHv;; PeP hfh(MEyEևb7:󺮢gKp>5HCax$q`\,?Yu !yaMZ`{!`{9)E h̏Qh;:.iofp^'Ѻ7/}J3G~1`9U~ YKm@k6Ӣ?ڵzGOѺѩlS$8AQn<r_ w_pYX;|r"𓈏4-"el +ֱc X:V;؎t^*ׅ5h 9$ V,a߆};plՐZ| -]ɳ|kyF;lݘ؀m@an_L b -M&kk^5SWUv6ҤjTiViӤݴnUNC}>}}.Wy%z"Y/_{Ob> +ۻ3>wiJ>EOOUE79𓣛}!\+q~F6e;K 0"WҽMyޑ`HRsxx/Yڱ8]c~9Xze TtOҢQB|c29wxz8-RLSγhqyi'Ooi=lff1s c`4!F?јop4Vc:Wy,=|`oŔ>1 D`1*u`6ƎjLza"ΏnhF0pC LÒ{_CI"%M{MlbdK II%LTc,QcI%:0,APڀ!i H;0 EldcdsI^EwUtmDG{+3wLYfV 37C;1"##) /ՆCГք6tw`z?:GϘ2іyYwz ^ω?9B x6`uKlfiq'L) (R‚@v9NgѦUGޜ!4LE4?-xo s@Ïy uQD\, Nݿ{xmtMc[:oCxhdj2q@FV Z' +ܨ-ZKg1TΡRav+(7~@N<_&-7p%~X Rud h,LGz}jTpp2Ԣ؄ +c;E즣(3@y6uX-/>K%"Y=r`wps:T:&9&*Mp a7Qn.CŅRKlm$iXga]GQ"}opKcm*q-$ RG7u2VP֊&E&.wm  li&IX9㡭BS5uv۠T!ӆ^(Fp +Ho"!R䳈=%.p$[;xuwIE덂99r(ݬ«CׂL:|Hu!7 yI$lBr ҖHlI-_Y̷۴?77s, ijڥZ Qn Y8H!ůܟd2pAф;! !3 OqF_|g|AfY㼓#VA}FK=J} io{eD$ + B + +D !L`0!J{e7#bɽ6ɼ0Xa,L|qzJ] PSSm$;8D'!b8 +-,FI> d0 1y7ȹf{5"Iq[\9 N98|_%~ / .) ._\Z!,8 ]u'0B(5wN FO3朜>dPg\Ҥ}jCtrt\\ȯkK8D??8{=<<wrx\O &5y vh}q- t=! P Fj0ؔf/TdV [=v]Ku_}K7펝ץc+ XASZQvg+tB-l7?ckncgX>Ntho+|+{n* ^k踂?t\B{lum29wtt"w71pyG\Vx塿ۏa +]x: k %^i optDoq>!;p(cv;i w|-88,~>^rlWaد@Z=ZAXf8Z_m&:-D`kwp~ >szK?"'f)X~vcN^F[4Eт&tl2!'*,42"^q6Q{rר_\1a#bP Gcᨆc2pӜ.E(>;Fvf|¤n 3a#<H#8 +-F"t)rkKwմZ)eZTmjKU}*{lVEσF]W=x2undy>уnOun5W&h{j3T힫qCU1Bc=U9^= y.RJxh~Km**qtS>TgYB*ݨ|V {Sy+0w^s6|;:rT +?Oj_J#5ߤb*ꛪa}sTw+?\Co50hv++2*3䞲B#u jv}L$7TA* 2 (F588CB)7P9!_) t21RJq%ڕP +xwۜC^_IqD7g C"De(#<[JԈ +DNRRl٣Zw}L֘Kp(щCp`<9k6d>FBr<̨~J2(-*F)QJ+):C ׀r%j"ǭٴ]&gzWq2}61dZʧyo":*1CБTCd3F*'k\T%b.TLje΁kJJ%QC|`jK0~a||}XzfXd/,`0oJfJj_5 TE +WQ%+@3T.¤Vބ;@_Kڕ|VKH F7D`aLr_hvȣ<ȣ<iFF6wh es-**12eXI3b3Ism2Q"@zcn^NVN$)I&O/\T ypײ ~ h +x Nw 8 ^o7h:9ϚQL3xm\|pZ+>V4X9np 9 +%pb]79E|Fk.=tqߣp_ ~ @z! 8d +%8b&qO, 7G;[s}F7}#8>oDX׏xobE.!}F'W\G8?#} +y 7{//x8xB/?xxd!]ʥ?8 +Jqq`2ϓ9cʚv鷈uXi<^^G~_['228}@-1/i z]@"b#v91::f)d̲%8 )=`A}`7x#vL*%x[fEA>Nlb=Ӊe2į~xVav];aA-63ڧamFnf:iyZG1cW6!~>gbE,C %F3QVXn8ױwl=>t 3mIB6wh=X)p1b8{V e5YЕ(އq#%Y/>`ݍ.F($ p< +G + 68jȣZӴ<G UJ\ #J7á{6h^b{?v[!{8v +!J$D @2-Dʂ xPW`k,@9GY?[ԟ0G^m8rК.5~a_\0A O:YT W*N.gd m$VM{Mn+rޓ+}GXo|/DA]U9fy;kfTW5-hr,lSNCݚ;d\%X mh#aǕ~Iww[~8:ZڲE7*HFyb=41\ T3f(8NYɲGNWfd2"+Y&YMj:.,/>R+цhkknтҖQ|k9T(ƛm,S/My2̣d3[n\F)%fĬШW#^wh 8ӂ%mhY y>̠NSikTF.+_l# 0 3ΰl +(0.D4Dwq;hc9&٬i&VLlkXSi&=iZcܲUt=}kPqQE! 0ԩ,+7lFSNx1WUJ3nUK)rF7r+%.^nrn-d߂Y?=N#_<&0ҧzs+&OQA1#RVg&),3]Y +*հI2dPp<(0C9?(8/<39AM֪lTi&Sy?;pMgq$*rkPHnȐP`~˿̖qHA>+#C *B ܼg9G0s%\*(EYX'btTP%b_qq-OI,WWҍ1 %gحNՠ:iV4x i|U<}/!㤀A+ Р2|ˌ)h`yr\rYX +0嘵rCƠSW.jB豉4/Ɩd%ӛ BM +0wE=\.BD."c'1!Mdb61;-s8KpG`O+yɇK*a@ȡ3$x \ \5\t5\@5д!`u+-M_M;"88Lum6{&P\ U jbibh[6ҋP@/DG=lyC2D-\X:` +XX G} 35ã9p5XᲈXĢ.$ml||<[\ +nm 5ʡ]ֱ@!H]/Y@ & +VxҏVx£%RZq.|j&UL+q4+ZOX9HfF$|6K[w+(݋ < d۹xvzю(8r965]@:r;zgAK2>Ab{婋t} A===uh $߽V3u"o%9KɓFbvI9V#= u̐ǜc@E?eb(Ea.^zCU>_Z>QA\%!Կ_p55AGy1~ [/ g?>q&8Ǣ%Yzq]9@"g 57<Ǽ=f/΀w9Q|P5Xr*.S 8yP  q-M\׸p@E~_).: x~B>G"QUmpA0ҷx̯5c=U K+<.$;?1?R>@k?eέx +^?ni 53|5ezqA#_L +^.{8 3w𗿂8#=C=:n$2y?t,Y?8VrEr?أ8G:rXD^]M2m~A馲.= ݠ&救GZq+YȑLLV8DDRKX%_"6cvv'iP6Դl_+u:~G-rE.9ϢB1į DWc Ğ2 YNy: ߰Z_j%yWx=19v-{E'{Cf$Ilq1 BjrԑdkL76`0`n&&`CbH'@B(HB[Fi.K@%Ye (mfi6AZN]5mӺ}m6MӦM۪}ؤjڥ4G.S =z?y99『w f 8$7el{W('ߡܿ$xqÖ8 1Ua#f<ߦg3q;cX5#Df= MSw)h5졅p$v1iL.x +8K)gYBDim` $]v>NK<n'2LY%u )tY='e*\v/q~J M5+ɢmIښQ{rڒ˵9%M)aES՜USj61m"Z~D XR(j ?R/1~ +b:m:r"8+GS IږVQQU6`(VBZ7֫޸Qu6Mݪ5Ri3)yNU +VUX>T3SKH'bCطEȓ&K"L3TgU\Z_5ZjUm(hک_Qgͪ,mUb{Sg;>S#{Lo&Yg{(C$;I!Qk,ekͭ*[*m媰W^2GJ[SI39OQsWޜw6 %33IcuKZ~vlF9{IW3SNʜ.*8'"WH>涩 Gyv?ʬQ5` Q)-[J&RVj0vƟ^Dw;X҃][K> 'dMSfI,kr@ ʨVz(PRB=J +)1N0uR;HfbM ~f/w_ዾKax e"'q!a$|:xĞqbMG#a{i{sp mx AY2`͐ѐb: }0q8k]A(nbL4n"LvLavL"&i0bK4A<&?åC){1ǎJw ցJ9>c;cܘɋ9.?7FҳB_Hx| +:;_ U:G;0\|Hv,bb,R(2 $y{8G^~;?oسEi㗩WH*_%p p Ǎ' 67%X,e +2X&8ҫ>_{Ŵna"r܄*_a |n]M>gVcB~PW +Iʊt9c/ggTW6\ۏ_ݛ¸oo=^I/G!R6\{tƟ6%inmzK4IIKKEZ.E\1AAȠ ás)`e2q2&sӝYiOs~/<Yz,GG>ۇ;h +{mf5*c?,ks51#ꋚ b԰>_8?@}^Gnx7u6v̀/b@2(CAw6ڦq-gҿu7g8?R<7{{BGeER?.jK?wvT=:uч踂89,C%tz gz@{཮kz _>/߈M_p귪һܷtɜGG8qyqa6WqR6K'Hz0v]_p|ܟ>ݛ,::)tGsc88#8Zܬ}d/ _R@m!B#_y \b3e'"֯MzGek=:Bt5JR=pt±nѽ(\Sݰt*O.r?b̘C"f'Q~mmIG<4vPAo ɠy#ynsmEo + 8,OUB$P]*,Od_ 2\G{?vX-s^tSsd+\x +)c:h_P +~/k$?fOyF>OqmrѺ!.sSc>;\䱧"p᪇pMdptvZf^w@dG\ȝ +-a4uAL&cjHָA9ʂ͞*P}LHuª4Z59_'`K0\RE-U$Fp+mw_ղqlI&&cqjHNQ8:CjU`b+4$JUI +$5ȗ4VH%oUqOxBwU`BSDEOƮGm%#P1i(bPɨ!Ft94y4T*Rjܤbs +*HW5r[)\ʱ\zD $#F#ϯw泥8!7#kȍߚ +YJ-*RBK +UX):Jn[rm3/T}RcxGi3-»[1nŌsJnFS'R*U`cO۞<{r%r9|I );AY㕙>MNgҝ+*{^2^5MlkH=Sl-~@ Fy24+iSө̌\P +93*=+4L={l]< K7#L_O̔zvx75RxeXՅ|vꤖz P#6(e3Ǣ49\#L*Yek.{LddHO*sLIs#>|o #c`;3 mcHrSCn|Ĥ* Y|vY +ke,K)EmJ.+x U\Qy|;rȻ chiCG3#t27^RL%VJe,u) % VRYH 2*ۡXxK^n"/˴2K-pg]9]m jF_-CF2֓b$&@>*-JLx_b}^ SH~gCcc ~cUEp>4q*=NsaXFh11+)`bA MhPi`0MA C?Vj)x6{LzӐأVBV7q7 $K%l\xa0t\x ǸcBHuhcC걓zCON0yy@0"dF\1RkRivHMdM4pġӄ&45GoLLk.Khhh k\ni)![ 9<h#;?;: 6+Xy#tp 30hs1 ; 9tG7&4nrхU]Gy,AUEpܳ:^J<a<2h6ƺ gGI'M/uE賏FG.Y'ṿ; 1pa0p{Lߐ {%W@Ca!WқO c *r1@_RqpfLtLRl`ut^o$6hVӐq -8.sfp>rFqخR+_W.0Y āt0Rοgjs;pH}A#GGs"^@ aG>|Tp!X4T |pƲ~kg88K8G<N]zS'u/ >z:=E;N*ңn<7U#` :._ORܠԍp/h=k!G!^7YJgz\hDt*bn 6^ 489x,؋h2GM>:p6Nv4#ԥY EfUR0we mXu8# teDt2!Ue/Z"\B.j(fmV]O{ jȭ7\~t \χc9)2xYŮC-Z@泳R\ ,F}9(48ĵ5xW:EiU5YJϨ.&j$ +n1 BxS(fYjC(i>'{ogG;k}+l$n9C5rxxK;\p%'/\p k4\5hr#{#PN. +idgqedY1@3zMaL$?r2C&X5>ȡ1A.%jTPFgiD a!w+'tCV:7)5C)O( 1|!OwDt.Xm)1PANO!ǁ紆*dT^Ur .eGxGRZySbdSd{< =ZQ1]!2YQ,jLN\r,rNi 9~LSeLȄ*W*,qB=9  +NUHO]pwL,Xߕ|VLl)f9#'CNPEyLxl2{2yeLJUD0(-U3Ui0v|:ɮL1v +ͥB+tr)D]᥊y +ݸ~0)\*ָɽû{Xfmհ2V|ߵ=růĔTMT۩jEZWj^vqq*B˄ΆKZ[µo5c[_U`8,G bK^2ٓ:hh5i|1/jZVXA>ך_,N7Ѧ _\[=_iu`xD@yy_2%ʹx>r؏{Թr`jf>+Te$9 `cU: I ~%ٱ/袁/h _s)qqlK3[j ML_>7\;ֲc4QkTT((kx[w +ሕKk4U@{.J1P╢4 ŗqE`ƎUn\ɼEi]l'${.yǵ1Ja} !Ϛ:mfG3m4I3]4E35q^'$;i츎[u r@ +1T<ȸѹm a-߉MKvǀz(j-|BL9~3p.Q3 xԭGn߶dN;|ܛ}6'Ѷ$3'qR<%&4S|qJ~DzR>ދx/9f |ʸ'yj= kâٱ ]0!,ڣp~ӳq0rN<Qٗc;ޥ`|<\^\e>PF<?WOcq|xiorM_a{ u| =&RK忚6W$dv}*1?X߶i{#_\Y3Nmc} 6>|d)];__/9Գ +3%OlOI' 3d,mB=E;bW8{; +,g_^U*IltBtl x( $/g :{'iv6l`gv;8hûCQO)͠s'I=. \x)9)#+yJ9ۉxs'5ۆ Tx>)3tSI/ WB)t~-vk~ƻFvNZMsEp]z>Dk;ddI8,ybi|ENbWVf{crVրco5(Xe1/sSG j+GYvꎣ7b%8pTȊ*J3LJY–ٲ_h9 +ukTz.?.7i<%oD,!`R8\)`. .jȥHB@H1%폎@TXb/&f:.cK4#1wsb=8|LfҖxxCCxt $2N(mt 5&j0T?CpmG2aEh9K(U/0q&{@AkX = =Y&zfͺ uЭ>HV^iPfPwމЋlxH9,4ٲ5f` ,x808!qM٠)]I l"10BTI##P$Ccba܍2Sc5#&F&G;Τ-gҖH#D >[3F5b( Ab${izз9&l^}p"F;b2!{asE +D&x#8j$,byb!p,dLY]ّ!1CHlp\q .U%NLH-Rdch +^@D3Hvgxq|Dp*жhcHu}67jʌ +?R#3I3< PS,> ؞Eq\=-R'6;9IAzɆٜI6|XdA,@W־+Y?[ړC iBF-(ӊ -A[(oq@j ȡ^s8j$,AE$h~?Xhڊ>ǁ-•a0|!St+R)5D@*zmahCFnlV7qm͐pnyQњ+{O#Ok R>5y]Nbs0 ;P^84~EJcil)%dtUY#Wq€rFtGz](9dj_8`]భKJ7HKwsؗ1TT..(rۮѵ}4f>z{ϟࣵLAϻsƌzfzkfL(քC ~h?j}CJ3E%/c_TVJ*pT_xEy\_^Hڨ;Wi YA"ҭ[l!Iv^يR9$Vd2nqy>=/<y;s+Nw $ ӟmWy0\*c<0gלuN@B! +G[Yu?R|^rrH/坑,~$K]Kn`l=Z5[7q|gUnr"~F8ߛ-cY đ\ೖ-K1Es)`[>zyH]PF(볫ܤ;dqFV Lk-zPߔJK{wWy~P'C8d,ߴ. :J@7 dzqF@` V" 6X ##  ZeWŔԃN~a~qfu#E".lйy.?Xϊ ;m HK=`(tu4G!gn_:^!B@zhCLZ8l$@ ++ @ e!OAx C8~ⷎNs]=/I֣3ѡM*{q6ljK~!}9Ym!!_7Hlް(Qppj`0GXs,D`+/xGF@ҚSШ +s=t##URuMT?|zq+[:sMnִ䂹33o\P7.B *OEtO1o,N4GO\ٞ~pc݌)GR0XQAl(f4 M)h@<׹L"]NJYsr,'%hݹv + ݆/U)|JnPW +x kFEQ`0|=t[ 1x}fpc3A&ŽpJ ~ 7%1,۰PRND,^HU0uf>7웻ñ]zQZVq6 S d`0XA#GVJ[(9 +RWvHo^0x3 bx +p`+gQ(^1ױ>9ږ騬*^x#qb ,Y2aHwcVMOb/f=-ȁ/} - `=瀾}k) +4`" C!)p3:mu@XoQv ngn3w:s+*qBV- M$NreO{}v R` 83JyMO4)XZGyQj{DM {_πY ̸Ӻ|)weUefᨈ.A]]dciI~\w<8/t Pg+e >*7E`S# 3\GHpχHn aKS[K 5uk;mɶcVރ iEHD_+߾U\'9GVXJ¬9M<~̨փI+qijL9%A0pcF"((`77Q#'q h[:-H,n#*Z_YXO +=Vy!pLYzY*K;x2}{"w7er"Iw:GSy\V[<6'Rչn%:溬'5mDtbZL\&$ +ܾ~vן{}߻<%E&gINDHJ"NƄdD] Q!c@ +d *>7 8PW% \ h`3^l:93cM|;egA :܂8XJ[7XI|0|N7w[{EkvcJȬi%J-Q#u|FBѵ<~ԠVTw|_JvV{J,͓ɯ)l/` R|Vxfm 96pL1c3Y0ߜ,/NP[@Qt+eKTe9ۏ-p +Ȯ|BpW$ %IHO޿y:~0?_(gD,rE}KcШ+)J_*=I,?!4l=Å[Pծ=Ğ [ }g OZO$o!xL=5dbBC) Oմ>RIr\r"#;@V2[kclzi5a#*Xm?;62.#:ĉ֙Li_8L+ +endstream endobj 5 0 obj <> endobj 17 0 obj [/View/Design] endobj 18 0 obj <>>> endobj 11 0 obj <> endobj 9 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 17.0 +%%AI8_CreatorVersion: 22.0.1 +%%For: (Terry Moore) () +%%Title: (Untitled-1) +%%CreationDate: 12/12/2017 12:23 PM +%%Canvassize: 16383 +%%BoundingBox: -242 -345 558 126 +%%HiResBoundingBox: -242 -344.27 558 125.73 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 13.0 +%AI12_BuildNumber: 249 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%CMYKProcessColor: 1 1 1 1 ([Registration]) +%AI3_Cropmarks: -242 -344.27 558 125.73 +%AI3_TemplateBox: 206.5 -109.5 206.5 -109.5 +%AI3_TileBox: -238 -415.27 554 196.73 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 2 +%AI9_ColorModel: 2 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI17_Begin_Content_if_version_gt:17 1 +%AI9_OpenToView: -431 748 0.5 2134 1587 18 0 0 89 170 0 0 0 1 1 0 1 1 0 1 +%AI17_Alternate_Content +%AI9_OpenToView: -431 748 0.5 2134 1587 18 0 0 89 170 0 0 0 1 1 0 1 1 0 1 +%AI17_End_Versioned_Content +%AI5_OpenViewLayers: 7 +%%PageOrigin:0 0 +%AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 21 0 obj <>stream +%%BoundingBox: -242 -345 558 126 +%%HiResBoundingBox: -242 -344.27 558 125.73 +%AI7_Thumbnail: 128 76 8 +%%BeginData: 17245 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C4527F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 +%F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 +%F8F8F827F8F8F827FD3FF82727FD44F805F8F8F805F8F8F805F8F8F82727 +%F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F8 +%05F8F8F805F8F8F805F8F8F805F8F8F827F8F8F805F8F8F805F8F8F805F8 +%F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F8 +%27F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805FD10F827FD2CF82727 +%FD44F827F8F8F827F8F8F827F8F8F8272727F8272727F827F827F827F827 +%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827 +%F8522727F827F827F827F827F827F827F827F827F827F827F827F827F827 +%F827F827F827F827F827F827F827F827F827F8F8F827F8F8F827F8F8F827 +%F8F8F827F8F8F827FD10F852527D527DFD5227527D527D27FD15F805F8F8 +%F805F8F8F805F8F8F805F827F827F827F805F8F8F800F827FD05F805F827 +%FD05F800FD07F800F8F8F800F8F8F800F8F8F800F8F8F800F8F8F800F8F8 +%F800F8F8F800F8F8F800F8F8F800F8F8F800F8F8F800F8F8F800FD05F827 +%F827F827F805F8F8F805F8F8F805F8F8F805F8F8F805FD12F827FD09F827 +%2727F8F8F827F82727FD0AF827FD52F827F8F8F827F8F8F827F8F8F827F8 +%270527F8F8F827F8F8F8FD0527F8525227F827F827F8F8F827F8F8F82727 +%F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F8 +%27F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8 +%27F827F8F8F827F8F8F827F8F8F827F8F8F827FD12F827FD09F8272727F8 +%FD0527F827FD09F827FD54F805F8F8F805F8F8F805F8F8F8272727F82727 +%27F827F827F8FD0927F827F827F8272727F8270505F8F8F805F8F8F805F8 +%F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F8 +%05F8F8F805F8F8F805F8F8F805F8F8F805F8F8F827F8F8F827F827F805F8 +%F8F805F8F8F805F8F8F805FD10F8275252527DFD0627F8FD0D27527D5252 +%2727FD3DF8FD0627FD11F827F8F8F827F8F8F827F8F8F82727F8F827F8F8 +%2727F8F8F827F8272727F8F8F805F8F8F827F8F8F827F8052727F8F8F827 +%F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F852F827 +%2727F827F8F8F827F8F8F827F8F8F827FD10F827FD04F8272727F8F82752 +%27F82727FD0EF827FD41F82727FD0FF805F8F8F805F8F8F805F8F8F805F8 +%27F805F8F8F8522727F8272752F8272727F805F827F805F8F8F805F8F8F8 +%2727F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8 +%F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F8 +%05F827F8FD0527F8F8F805F8F8F805F8F8F805FD12F827FD07F827FD15F8 +%27FD3DF8272752F827FD10F827F8F8F827F8F8F827F8F8F827F8F827FD05 +%5227522752272E2752277DFD0452F827F8F8F827F8F8F82727F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 +%F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F827F8FD0527 +%F8F8F827F8F8F827F8F8F827FD12F82727522752F827F827F827F827F827 +%2752275227FD0AF827FD3EF82727F85227FD11F827F8F8F827F8F8F827F8 +%F8F82727FD06F805FD0BF827FD0FF800FD0FF800F8F8F800F8F8F800FD1F +%F827FD04F85252F805F8F8F805F8F8F805F8F8F805F8F8FD042752FD0927 +%5227272752FD0627F8272727F827202727272027F8272727F8272127F827 +%F827F8272727F8270427F827F827F827F827F827F827F827F827F827F827 +%F827F827F827F827F8272727F8272027F8272127F82727272027FD07F852 +%27FD11F827F8275227F827F827F827F827270527767B7BFD05274B6F6F4B +%759F9F75A0999F999F4B6F6F4BF82E2D27F8272D516F6F27342721F8582D +%27F82D332DF8272D34F82727342727F82D0B7569510B2DF8272734516F4B +%342D6F69510B526F9A7070704CF8272727FD04F85252F827F8F8F827F8F8 +%F827F8F8F827FD06F87DFD09F82720752D3433572D584B696970696F4A6F +%6E6F6F756E6F4A4B686F20052D2704F80B2705514A2D262DF827052D05F8 +%05272D04052E042D2634272DF82D2D27276F2D585827F833042D6F510527 +%516F2D270B5269706F702727F827F827F827F85227F8F827F827F827F827 +%F827F827F82705F8F8F8275227F805F8F8F805F8F82775572DF8052D5727 +%294C4C4B707070696F696F4B6F696F446F6F27F82E512DF82D2D2D4B4B51 +%5105272E2E0B27272E052D052E0B2D27280B2E2D2D262D2D516952A8FFF8 +%F82D51276F4B0C05516F2E052D4B706F52FD0427F827F805F8F8F827F8F8 +%F805F8F8F805F8F8F827F805FD06F87DFD09F8274B11FD04F80B5222764C +%7069704B6F684B2627446F444B446F20272657262726574B454427052700 +%05272D002827340528002E055221272D51272751814B6F4A7DFF28F85157 +%756975577B456F517B5176514B27F8F8FD0527F827F827F827F827F827F8 +%27F8F8F82727F8F8F827F8F8F8275200F827F8F8F827F805277C0BF82627 +%F82D2D4D69706F9470706F7027764B4C6F6F4B6F6F4B27524B2E51766F75 +%4B522728272D264B27704B4C4C774B764C70705276767C4C4C9A9F99699A +%A0FF7E7676A06F769FA0999A75A09FA0757C4B52F8272752272727522727 +%2752272727522752275227F82752FD08F827FD09F8274B3304F8F8272D52 +%28764C7069704C706F4C4C4C456F6F7069704B284C4C517B7576454C5152 +%525226512D57204C5176514B4B9F757645754B9A6F706F766F6F2775A1FF +%4C706F706F9A6F756F766F766F7051702727F82727FD04F827F8F8F827F8 +%F8F82727FD04F82752FD05F805F82727FD04F805F8F8F805279475340B0B +%0B7C2F767576707046765776697070706F704C4D274D707046A09FA06F70 +%70776F764B6F4B2D2D272157514C27706F6F27706F4B456F6F6F696F4B33 +%4B9BFF9B69706F704B7C6F706F706F706F7070764B522752275227522752 +%5252275227F82752FD04F82752F805FD06F827FD09F82769707557515728 +%4D4B766F9F6F6F69764B7069704C70457027514B706F4C75A55770697046 +%704B76446FF8274B4B5751204B456F686F686F696F4B6F696F696F274B6F +%FFA16F697069765170697069706F706970697000040B2DFD05F82727FD04 +%F85227FD0BF827F8272727F8F8F827F8F8F82727704C9470764C70709A99 +%9A997669704C706F704C704C2928292D524C704CA07BA575704B4C4C7028 +%4B4B4C7BA57BA57B7C52756F6FF8707071527C6F706F946F706976A7FF70 +%706F706F766F706F706F7651FD0570514B272DFD04F85252F827F8F85252 +%F8F8F827F8F8F827FD06F827FD09F8276F70699FA46F6970999F69A06F99 +%75764B6F456F696F2E522829274C27284B4C4B4C27524B7069764B6F454C +%57817B9FA47C2751274B45704B4C2870FD064C4577FF7D45706F70697069 +%706F70517669704B70212D2D2DFD05F85227FD0FF805F8F8F82727F8F805 +%F8F8F805F8F820704C4C4C764B70457B7B70999A75704C706F704B70702F +%2852287669704C704B704C4C514C4B4C27524C4C2181A5827B7C4C4B446F +%4B706F7070704C4D70706F7070706FFFA8704B706F706F766F706F706F70 +%6F707028F82D04F8F805F8F85252F8F8F805F8F8F805F8F8F827FD08F827 +%FD09F8276952272D270B4B4B0B33219F6F754B4C45706F70697029282C51 +%27524C52464C4C4C4B6F4B52052D050600289E7B57814B4C45694427696F +%454C4C4C457069704B704B7576FF4C4C457069705176454C464C4B706970 +%00F80427FD05F82727FD0FF827F8F8F82727F8F827F8F8F827F8F8209451 +%18335E11764B3A114B999A755851706F704C707029292F52524B76767076 +%A070707076280C4B27275752764B5151764C275127274C284D2929294D70 +%76577669764B76FFA24C7070706F766F7027270028284D4D2804582D27F8 +%27F8F82727F8F8F827F8F8F827F8F8F827FD08F827FD09F84B6F76335251 +%3451752D524B9F759A515245706F706F70282E28524B6F4B7646706F704C +%514B4C4B4C204B5227204B270B27702652044B272928292828014C4C764B +%70697069CACF7045706F70696F4B28000400282829002D272D05FD04F827 +%27FD11F805F82727FD04F805FD04F84B9451345858117C76764B706F526F +%7028706F707070455251284B4D4B4C70706F994B52517B575175A04A7627 +%4B20752D51755129514B706F704C4C5152294D70706F766F7076FF7C7646 +%704B6F6F70282E51756F70702827342D05FD04F82727F805F8F8F805F8F8 +%F805F8F8F805FD06F827FD09F8276F510B332D11274B274B45704570694C +%4C6F45706F70284C284C27764B4B454B6E4B202D51582776524B5151FD04 +%4B759F5152504B45704B29282E274C014C457051584570A8A84B70454B45 +%6F4B290627206F6F6F2100FD07F82727FD11F8FD0527F8F8F827F8F8F827 +%2752FD054C2E054C6970704D6F9A4C706F7070704C4D4C292770274B4B4C +%6F704B704C4C28706F7026704B4B276F6F4C284C2D274B28FD042928294C +%7C7C7675A6759A6FCAFFA06F704C706F582E28277C70704B6F4B2D0527F8 +%F8F827F8F8F827F8F8F827F8272727F8F8F827F8F8FD0527FD09F827274C +%284C284D4B27284C214C287C7B76696F454C4B28216F6F7046286F6F4570 +%4B7B4B70524C45704B27444C4B4C457028282152274B21704C2922292929 +%757652764B709F81A0FFA0996F76754C2D2E222D274C4B4C212D262704FD +%06F8272727F8F8F8272727F800FD04F8272727F82727F8F805F8F8F805F8 +%F804274C706970707046706F704C706F704B524B706F944C2969706F4C27 +%704C706F767551464D294C4B29284C22FD04284C274B27284C706F704C4C +%4C4D297C7BA0524C4CA5579FA8CF75A0817C4C704B714C4C6F9370280551 +%2D27F805F8F8F80027F80527F8F8F8272727F827FD06F8270527F8F8F827 +%F827F827F82727704C7651706F704B70454C2270694B0B52467069702876 +%7552212822292829464B272822294C6F45704B5145704B4C22272D58274C +%4C4C22292828464C28294C4C284C75766FA1FF9A6F765170456F4B0C054C +%467021F80527FD0AF827FD04F82727FD05F827F8FD0427F8F82727275252 +%272827527777587C769A76774C4C4C4D4C704C524B7052774C4D4C764C4C +%2870704C6F704B514B70284C6F946F7C51946F70284D4B7C7B704C4DFD04 +%2928294C4D29294C4D28766F9976FFA17069706F7069522D2E2929297020 +%FD04F827F8F8F827F827F852F8F8F8272752F827FD04F8272727F827FD05 +%F827F8F8F8262170696F69706F704C4C274C4B2D287051764C704B4C4C6F +%69704C28456F4B6F454C4B704C29276F69706F7069706F4C226F75A04C4C +%4B7045FD044C4D46762E294C4C4B6F7575A8FF6F706F706F6F4B52287045 +%70274A20FD08F8272727F8F8F8272752FD06F8FD0627FD04F8055227F800 +%26274C706F706F766F706F704C4C2D524C7651706F704C4C4B6F4C702827 +%4B7070704B704C4C277075766F9A6F706F704C2921774C4C28706F70284C +%284D4C4D7C7C2953757C759A75A1FFC86F5851706F704C4D6F7070282093 +%4427F8F8F805F8F827272752F8F8FD0427F8F8F805FD04F827F827FD05F8 +%7D27F8F82727704570457027274B6F69704B2D2170696F2170454C454C4B +%4C45282D524B704C4C4670454D4C574B6F6F766F515170464B75A04C7046 +%4C46522D282829467C76294B5175757B9975FFA76F5151456F6F4C222928 +%29004A686FFD09F827FD05F827FD08F827F8272727F8F8F8277D52F80527 +%4C4B2D51522D52F84B6F706F76577C4B706F766F706F704C704B704C2828 +%7070704B704C70294D6F7069766F706F767070459A9F6F4B714C4D285128 +%2929717076284C527651766F70A1FF70706F706F7070714C4D4C2820754B +%27F8F8F827F8F8F827F8F8F827F8F8F827F800F827FD06F827F827F827F8 +%27F8F8F84B4C2E11121112517051524B70757B7BA0527675764C7C4B4D45 +%4C4CA0697069704B2D4B704B2946706F706F706F7069704B4B707C512769 +%706F7045704C294C704506214C46706F70699AFF9B697069706F704B706F +%700026514BFD16F805F8F8F82727F8F82EFD0427F8F827A0523451763376 +%757C516F6F9A6F704B766F706F9A7B766F9A6F9A75704C4C4B4B27514C70 +%294C697657766F706F70704C4B76755228704C70282E284C4C4C4B4C284D +%4C7069706F7651FFA1767570697657766970704B206F6920F805F8F8F805 +%F8F8F805F8F8F805F8F8F827FD08F827F8F8F827F827F8F8F8274C2E1151 +%2D1251511133217075706F75517645706F9F4C4C69704C7C284D2827F851 +%4A4C2229284C4C524C704570457021277576224C284C22522D05054C454B +%21294C4C4C764B6F4B757DFF51574B6F45764B4B45702120446FFD16F827 +%F8F8F82727F82752FD0427F8F8214D0C12113411764B12114B707D4C9A6F +%7675764B7675704CA06F7076706F4B68762D284552282906294C4C4C714C +%4D4C4D4B76765328704C4D28274B58276F4B71294D4C7657766970699AFF +%A16F706F706F704B70704C204BF805F827F8F8F827F8F8F827F8F8F827F8 +%F8F827FD08F827F8272752F827F8F8F827464C4B764B514B4B51514B7052 +%4C6F6F4B766F4C4C704B706F70454C456F206F210028515128454B456F4B +%4C224D4C70284B4C764B284B704B4C2127264B4B704C294C4C46766F706F +%7069CAA870696F6F706F6F69704B4B20FD19F805F8272700F827F8F82727 +%F8F821706994C676699A759A999F709A6F76755751706F9A6F70527C5170 +%6F7074767699754B57574B704C70456F4B4C4C70705105769976454C4C4D +%4C4C2D5145704C4C294D4C7069706F706F709BFF58706F6F686F6F6F4C28 +%04342D27F8F8F805F8F8F805F8F8F827F8F8F805F8F8F805FD06F827FD04 +%F82727F8F8F8274B7069A04C706FA075767B7652764C76515252764C4C69 +%70514B4570759F4B2F4C9F6F274B4C204B274B4B4C4B4C454C2D516F7651 +%70214B284C4551277045704C29224C4C5751706F514B70FFA7456F446F44 +%4C4C7045587551FD0BF85227FD0BF827F8272705F8FD0527F82727706F9A +%9F7C284D70706F53707029534C706F7770704B706F4C6F6F6F9F99752E76 +%9EA0275127A0767C517675A07B764C2952C775766F706F706F706F704C70 +%4C4C284D4C70517C6976517669CACB70FD056F70694C57825727F8F8F827 +%F8F8F827F8F85252F8F8F827F8F8F827FD06F827F8FD0527F8F8F8276975 +%2D3433332E4D6970286F4B29284C46704C6F696F456F216F456F6F995152 +%517526F8F8704B52262721765176224D462952766F706F70456F446F694B +%454B2106214C456F456F6970697076FF766F697069706F7000517B27FD0B +%F85227FD04F82727F8F8F805F8F8F82727F8F8FD0527F8F8209A572DF827 +%0B57285352524B762E524C5370766F9A756F6F766F706F9A4B4C21524B51 +%214C762E0B524B764B4C4C522E774C53769A7070759A75996F9975754B52 +%4B524B7675756F9A759A6FA0FFCA6F9A6F9A757627F82727F8F8F805F8F8 +%F805FD04F82752F805F8F8277DFD08F852FD05F852F8F8F8274B0BFD04F8 +%0B2E287C7B522D33757C52759F99749F6F99999F6F6F75A075706FA09970 +%4B7C52584B7C75764C4C7552274C7B7C75764C7C4C76759F75A07B5352A0 +%5276759F6E9F759975996FA7FF9975766F6F27F8F8272727F827F827F827 +%F827F827F82727FD04F82752F8F8F827F8F8F8277D27F827F8277D52F8F8 +%217C0BF82627F83451532852282F52762728286F4A516F9A6F766F9A6F70 +%4B764C4C4B524C714C524C714C524B704C4C052828292852292F52524C76 +%4C7651765277515276A0757575996976519A76FF7C7C57704B52F8272752 +%2752275227522752275227522752275227F82752FD08F87DFD05F87D27F8 +%F8274B5704F8F8273352282E272D284C272D0C27262D0B5145515151454B +%052D2729052D054C452D0B2E46520B2D2753512D2D28282D2D2E282E512E +%29290B2D274D06332D4C4B520C5168750C2E4B70FFAE2D764C4C27512727 +%27F82727F827F8F8F827F8F8F827F8F8F827F8272727FD04F805F8275227 +%F8F8F8055227F805F87675340B2D11822929054B4B52282D212D042D2727 +%0B7651705176272D262E282D26752828274B5128054B272E525827582758 +%52522D532D522D532E524B584D584B58527633522D7651522D5876585258 +%2E7776535252272727522752275227522752275227522752275227522752 +%27FD05F852FD05F82EFD05F84B51512D57292929280B2D0629053305F8F8 +%2D0B4B452D0B2D45700B0B0529062D0B29282D0B2E282E0B2D0629052D05 +%F8F82D0B28012E0B2D01280B2D0529062D0B6F452D0B51684B050B4B6926 +%0B05286970282900F8F827FD18F85227525252FD042752522752272727FD +%0452532828272727282828FD06274B52272727524B272728282827272828 +%27272728272727282828FD0727522727275228272728282827274B4C2727 +%274B2727274C4B4C272728524B2828522727F827F8F8F827F8F8F827F8F8 +%F827F8F8F827F8F8F827FD04F827F8F8F827F827F827F827F827F827F827 +%F82727FD04F827FD4BF800FD18F805F8F8F805F8F8F805F8F8F805F8F8F8 +%05F8F8F800F852F805F805055227F8F827F8F8F805F8F8F805F8F8F805F8 +%F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F8 +%05F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F827F805F8 +%F8F805F8F8F805F8F8F805F8F8F805F8F8F805FD14F827F8F8F82727FD04 +%F8272727FD62F827F8F8F827F8F8F827F8F8F827F8F8F805F827F8270552 +%F805F8F852522727F85227F8F827F8F8F827F8F8F827F8F8F827F8F8F827 +%F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F827F827F8F8F827F8F8F827 +%F8F8F827F8F8F827F8F8F827FD0CF8272727F85227522727F8F8F8FD0427 +%52FD0827FD43F827FD1CF805F8F8F805F8F8F827F82752522727F827F8F8 +%F852F8522752F8F8F827F827F805F8F8F805F8F8F805F8F8F805F8F8F805 +%F8F8F805F8F8F805F8F8F827F8F8F805F8F8F805F8F8F805F8F8F805F8F8 +%F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F827F8F8F805F8F8F805 +%F8F8F805F8F8F805F8F8F805F8F8F805FD16F82727FD23F82727F8F8F827 +%FD42F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F852F8F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 +%F8F8F8275227F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8 +%F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 +%F8F8F827F8F8F827FD3CF827F827F827FD40F805F8F8F805F8F8F805F8F8 +%F805F8F8F805F8F8F8FD042752F827F827F827F827F827F827F827F827F8 +%27F827F827F827F827F827F827F82705FD0727F827F827F827F827F827F8 +%27F827F827F827F827F827F827F827F827F827F8272752FD0427F805F8F8 +%F805F8F8F805F8F8F805F8F8F805F8F8F805FD17F82752277D52FD482752 +%7D52275227FD19F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8 +%F8F827F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F8 +%05F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8F8F805F8 +%F8F805F8F8F805F8F8F805F8F8F805F827F8F8F827F827F8F8F827F8F8F8 +%27F8F8F827F8F8F827F8F8F827FD83F827F827F827F827F827F827F827F8 +%27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 +%27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 +%27F827F827F827F827F827F827F827F827F827F827F827F827F804F827F8 +%27F827F827F827F827F827F827F827F827F827F827F827F8A8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FF +%%EndData + +endstream endobj 22 0 obj <>stream +? Я!x Yu뗌vm73ꖥ +b1b SOS#2JOwD^ f -/kjć?M$Dug'wAvj_΍|iDR( N_uKxހ>apsiل)v +{_xSϣLҶXf#,I5zl\`YxMQ>!toF!2~kx̀>x\=g 6&E I}ŏGםmi~dQsҶ#:;x?8/Dئ]5^6Qh?][vqD΋?T.xwH9V J&d5Q>x1e8xyȪoQfo[u3Vm0%>/[m2A;]o|5xh40ĺ+3J\־ذlUG8P/ o'쌛YuqpZxU_m̽hUߝ6>ZV5Y7#rBzJiyk_bA-YP VJ媋r=̶y+=؂?5ģZH%S(:X%F?K{qnFÜ ckjDINi %,o"ɉw_=ؚ~f?;OYz^lF& o?;,;VhaѿcD!"d +- +++_Mo7хu73"2c .XpEgt7*R֫cÂG6FCJcmIVFL{bXW~2o>w=J xrW-fÚ=a#esk"{ .[6.(c[A:tr^G{b uS5#͙knDalA7 +7)-ndA1Qy[~|&ڰ[6cKMy*a^u n&zB Euz> }m$^QRȡ+FZŶR 䖟6dܚ SlEʛ<ڝa:> Ѓ1*z/Ȧlx}7*Af>ض!r> -􇍘]hLnߠ !?eA6)'K68꘿궂^y=鵣wLuC^ko۰WK7K#Dd;QQN}췷Q\paAPwM=ͯwL {~rؒ4k@h7Š>b~ÞU>R04tE[{6ܞzcYnՊ̘1 &)\g5mF3[Srp n"4~ +QUljت :e2XA:f?HA i1;N'%FLOh? %%EШ9%1/ {='o|6h|쮽?pF.qK'{Ijae˫5K[:}5e36XuӚ7= 莟?2.R[ևu7fAYѣR+nZí>jH'}nHegwzl投T1| f5ECܬ@M[3mZI;MSF0v`'$fEG C6>mxƤ_3K{ +6,/dk坐 %]`9?!V b<fRsEM9?BOhOI >9)ygo}sZZg3*ԬKN\.іEZ[FR Q.ru1Y_i^D ng%^Ӣ.O)[>㳞*g[vR~X]mKRxi5gm9 j|)KZJڸnMJuȤ[GT͹S} <  h00"CKzx^n|a&]\|>ߏ-_Ԃ?<)o^"J{oFMϗM;bC~ ]R7]ϣ41 :uI2`|tgӳV34_3zl(r^zQ()$.jU|ڪYb'zjJ.OHJ,)q3*RPt/æh~==ȿ\X1R= +F5Ā9?&y0ǒ-)j̪q=w~VWrLL_;?/ₚ\1>+۰ƆoK +,"AZ +?l:UVLn|A?31>wB*_suJ_5bg)RQ +rr yǵ2`<<țI *iJx:-wʳC[*RMzZX_6D%ϛa:k81$/'"ڌeHQsJyӸP=gfxd~s+v;'/3ԞN;Rݖ>rLC.iɋՕ"5/n)cV˓;G?v0Hi/2ʢi0G{ã~AY94%7؏m%/[>c~g<:{tdRT(y )`D7b_ypqf+_;>6I!ۊWaI=.,p!ٕ%;֦aEeUㅁ{ݍONnpl cQi?ƸoA6so‹7 H~rpUo]sq&_*2kZ)A7uipO8fЖF+vT暓C؏vЈ<_Jo[SS] /4O\x>Mo%i nJaq " -a;qCi &\Μ\VXS=P/HE3*LmmcpՊQ̣5Q4Ger8{C[q LE=dh텿Qr8/>]pNU;;SbMi0+ع5vi:jM%NC?5M1a%E!a={1I3ͨfhƆoM +Fhu&WbL}HciAb# sJCrF̈́\eu])D#n9[ޖm/h?ea2SK<&]jÖLCݔ AϒC ب@iL [.aܰ\`嶮zFٞꘕA"hGi[@l՚ܼ݂֗C"xҦ?oWh)y &jT>kaBfB朻K0eb78 /d.Y `|ٺ i{BaӌP;) :BCNiE?dRV63ʨl V%bFm}bZ@ \pӚB8k+x uǠ4y8sՏgC}F6ݡ<ɗ½Y |nHɩp*M|W$6)҇-9 $.Fͪwx57;ѽ=,8$a+ࡦj{;쉿Մ1ZlUgtkCdIURPZ|{weo*dp#cR˫wRgd-77e]7jr3Z`}Ć/cކM\SlΤD`. +(̶M 7Ngs;8GO@-cD P6, 6Dog< +7pa=0|JZ7FϨ^Lj )ȅRY'LF/ꡱc/5jCjR5!U-(aI28aa&i$*gų5'yKG:Eh@%n>io/m:&Wz!9 e +|꜁XCB]>(̼м1.X#L̄Tf- 㱶Dv@W+C\ڜvB茙 F3bsC&!ty#:gɌkm s(3])6oc׾9աښvf wOis*B:B^Hk欳r7Fy;ncZbɵC4F=lW7Fvj7| b_Yv3cR:×C_w8[/6,yI #,_tX>&u|u{ƺ 6VKHIΜB~s!1*{o1Ы6j5o6uӣd.۱NPj{x #8`̴_n.>F z9 ^0'ՂCJu?7e,وKzÊPƍ=ғsl5dX-òi q$匙 +Wl^ta6R?oO9w![5u^G[S6M~RÖPvҚC:rެ Z ) +BzPXwkNiXd"},̺ hM:B([?$- bKWtW1on;ŬTK0{H ^pIDkh΂d_{l$Nnޖvx{FM^ zC؎/3W{y탽83kbh^̡=af9kŨ*;?a6&D tvg:4AٲIjcGݛ mak"ָO2?د_'-}ZѝK{ܳ>zW: w&x]!*tcٞiw&.2oeY!alSS=}Đa@'\֨@7%qmMvZVByxǛJtr{ UbkY;FA!mҀ˝:!~2oԌ0bi!^ k(m1n+\.T8ǤY:,cg)/|]iF'kuWo{~R?;c"K n%;ץ#MZ/Mзtr}+,B\嘸zn_E.-ICnzoxzի>^u*׀)hPYׂz~}*3}7tU^j)VS3}b glr%VT v)|Vzh#T{yzA{o{iEV>8yRE͙Q6.*n =^_v<\ yȐPԠV͑Xtc{\؛Kr?S]e橾v'Y16m9amwt&,2M +; |ʄ+Y=є|,a+Dm/_H1d$5͜C3۠ + r畄ΒP{rbapԁX QG*f\1ΖJF\O l #.t}Lg+ +W2.A*8ʙ))k>HMH)XNy/HkeT%`S9v+]"o0Y$eu!BM!dG D^K>7Js_ 6\t+kt}9tcfuB*ߨdwWO_h\^vH;6FB@cP kvXh|gPǩ9=>M4jV'm}aKS'%b骉!ȞvS cR(QJFe)x:|P7vd1fY_@T" 6ЊGf}.A*XBLֳ͌%وG|ﭹ'oQp']]%=i#*k,zMri!GH fR^%f|-Ԡ-ʋm`%Hc xyYe_F\{뚓%YS,$Aܙ5&JiT놥"pM4_@\!x1L7Q-dK+Jz/YqVnDR"g0qM"m+^tk5C}FW{6VŜ_J{(TVP{< ۹PG'D\{TNp`}7 Ҵi'= +X[*ytW 4[ʢ1R|ӾcQ(ZQ>ע>][i6i[&f[3P[~vٯsXm^Y-aI +Aڽ24s <%_ 02a'O=7vI݋Q*.)qN9~ŀ oJԡ5շF5G>-=_*-g5/F\&|҃oPHӾaߪr|FGwfqtf3y#6x _˕2 +d?H>rqmXܑdKw"nGb" +*6XP'J h9)ݭ=AsKl8nU +/TtAs ?*>rBL9ϣ ]+8~viGj?O}T[z:<"O)F~`2vObJk*o2E upD 8r̛S?RLʿhgV̜f+>[-M46)K"M$:5_~q^~rĽWs"RixSXWtx99 +P:@A_U :~zL8]9zdm_{]SU[(̑?WVvZ + +9t&a9dkVwVJ`ffJ1xFo ccIgnTDzPH;!㖈[TޑdG_'0NC_0x45*8@;#5-UO_6J9.kY:ꝲȈy +mhܳviMHk ,'&]!vZKkMM3EXSǢ#(&sWҏ~ >JAߖHmQ+N#8)xsb'CIM*G<G|"qHJxvw%[b+Fթ!Ө&;W`(d:[Puбwʏxq'1γgǞ%ҏ͑ˮGrnةճGuYƯ%*g\=E-@hyӘ;:"qQc퇵@CGD#nFa+|/ R2ͦ.XirWYWwG٢b$e$ 5q>v 3-GӃЈأ/>y>%3$dqN|HD.G{ѳNN'ﭓ΁18k(MF#_cǣ?w/"n=z4;^.67{rӐ2G,UZ:`Z7M> o3@.j>ziNDN!DVg)3u[x챺fTEM13NJͼ ,rf\֙%w]Eh%x>ȭS}H񛦟?$%f1*l^))/P[}{0$5@nyњдy BhoKji:!VŲn}VbľP. nqOJ( yki+ iAx +su{-~E`]]q,FS$|ZuC*XjaǯǣSP?'p~,n?XGN  bM5@.ty؉ +2IポxU?]"u(1t|9ᏆQ.Qj [?L`3VZϲ3[mú~K3]׆}*ffef2ɤ%{C#UQQ{CEDD`"bM>{Dژ&jk Bf 5oGHN˘UҖY|ia5Bp k3<2r/xFɆ SF 暖{5u$"iAcQj$)qe2֏ѓĝ%{n9sc]1/tLipPӴu;GsT,$4%'{-Mޥfb-xw^K?+ނI;mk{lKAyT +<ȱu.tw.xp0O?Y%W{\}S>]=ZIR(*Σ ^ r01\q1yv3 2Rї-6ŵQ\VܢV1.yz'讲薡Գ@) +h&1,m-3+>s`gCeW?2 ]OsFCOGCӝnZiy7W2W1'hC:~y(NFFso'4y\jh4α- +.ڻ95t/zچ+ !$% &uw oZlٟýZn +zerrűZy߹;Ty!f_EL9 +mSN154NIޝ n%kq)'9[(,9/y[XDN<4 \= qJ inU5-s, rzWIʵT펕{;1 iG:v_ŀeC &mw ч|7{Ҋׇ&!)usfRԫ_{>Vc/4d|7 Or.0C 6}wf4<18x؜wqCL/9[ink8.է\RJSLJșK#53sDaSX Y'Xg#OWj^ԧ fKp()p^:t T > qgaRJѡ]|P_5./0K.U-,8bæfcw'Z0Ex?-&ȹkoO7ی}!!ݣh`}wR\3jCڟJh.+#f}Yeδ"!sCH˝#E06l>8*H_tǖ`@6):ϩ gm4RPy~8-'NVj}p8 ~1*ģ/fSsj]eҭb@]J6޲6Z53 ȧ;C1&|ҍ|+nĝ+g;1)I߯͹v8IvIy(<zb+GdIDa:rX'ċ (4q(hC$%*%\Ups C[8oi:rot~.1_me$!r˭y-Y?L#!L% uE)th}gUc%]kk]bFk޳m&5OHL m0&v.j?&;09 yu0VBq)/%jaqe!Ζl&5Jm3b>nw&`Q`nro20Xvgzu,ÈBQXI)DŦDam{8,6~ak8( 쏣<\N dup 4$XEܟ/ws|jƯ%U$i:>-ԫV&/<ޛDYP|1h6Xe2dӻ#=r61&1oK!ƥzj +) `(.-\u3\f +-~Dk]r0ENN:}z0Z{l3:qQ + +\ +sD [ +ԿH-9t9I˿xǫaQfOG 4%J2اD%LU뿏"Rs տ+mxǥ[%{øO9|ut9WF/iҒm/sBGT< '+fe{aLԿL\RԃIlY@ȳ1@ȌGMcޜk%1!e{?'z6ީI:|;tgZwoVa=8O4|,7Rr:^HӔ~a#_۲~CFzY1hqMP\G<jc>"t[I0BWߥfBUO ȘULٙ`cYUHMjħf3e_MpnPDcUo>V]Zl8] t߭bTL]eŜWR&i + *f%1!{/A {!P:u#?R:.]yqug&p{ӄOZ:ɡ}Ѱ6(f8ᄒ#ҟ- ̚V}B^%\Ohå!ŷZ)83= jjgi+Qr2rCBL`,U!sGkѡ+U75o+~U6W\wC QR1VXVe +.ľ1[Li=Qx*bf0CDp B G,6dPWSwG/0}0~ydǎ1.CF:"iE)r/Jjc민1e^G&?%G +ڙuIapARvpSqkg)Y19=9'i'Rfu5ύ~wmk%`#šUk揇7^gYO4Miz*_Tm®ƕNB&r,D/"Ӡ޲>y̱o.yN<8p]-us539n֏\Ɨ&w0w2^? +z垃&?w4Y7( z!d#}sSZ[h+)ww-V8~Ew+7G*6XŬ΁_ =ݑoja6ǰ]Ŝ{1|쉼䦬ಜ_Z{)r9SB:#%X86#mS0wt]>>gmFE_ԵVIJCEWFCoB_5[a?w6IK-$гIؙ? oy7vFk"W2~wn+>IK#,fL $+q*PNqLҝBicMew-=J4#S6W=,{l + u)mgSrc Jc Ljz7⅊Sj[坥҇K +oj8$j\mv?'!ުG[=-ciaK }P(o.@'oFQQv[AOzѱ?619{yD]LI^ F?84C-|[]th`X2ttUKI˽e\EjUYz. +h|V̿-Ix؄qo3 6 !ū`B }UO %Owz_ȐZ}wc^5b}xyw> ,L½:lcܧ"tS,d85ɀӏV]g^[VZ$u8N0=Ѽ{C9+h:UpzS%CјK-e&x}1'96 a#:3o_ڑsQY{Н]&VQp{m=,^r`>Kܜf 9.Q^\*%d]k0ۙ"ďqsN3~ݛ$i:+v£֋ b?f4\q2£pi1B =]76z:%5q EӾqs}} nhR~ ?;Q?퍣jJ1n|J?<%f(9kӯj߁#j"ǐk֊g7b~kX02Jμ(^0t?Al0 +0ThWR7Ec7C銈˄߷x4>ιxiw1?Zr~]jιږ{!*{ޙ'sj2&R0hZQ"ȋC 2ƿ\ Qӑ\6}'WWGJ@cTTR]KJ8bԦ YhqDf_TE@Lr,3=!6v#J'ξ|e[v< =]mzk0&\ْ%xSD>Q{bR5a?Cϑ8KMn8)irq. l{)8.aU XΨշD @th;rb3j'u=мG<]y}l[)LsvL{C5;w|gM\geꛝw6zhނoc' )+.$l]kI Wq?s9c @w.,¾q[=˿m~y.i]o5Um{fw0,%B?QZD51oQW*om鹆v*y"^9W zKL#|j@齃p}o=)8um%7ooϺgRRZb}oB&J]jڒiW?ݚ@$t\QX_ԥ~20?vXOWȾNh.D煸4V{A=ׂ jH]LٰYr[Zy8`¿@H;Jsb1IRZWͭwvFA4I?k/90E^F7#űpKӸ76!>n]-$EM˸`U[ec5}g#0u瞦;<}wΒrSJ7O^M,CTO6? ^fG|聠=+4׌CUm?9ĤQu0 ;@zԭ>jz' =NY#.o&RRu%?;@yTź_ͳ !<R~xχ?r/!E:l,8s 2C++6[}HE-Gbv0)򇦉ƱpF[F!=sP7(П)I9nu5Uٻ#$ %| +X][u9'j#=N&o-j?V?L$;e)WmԒwecdNVob̯fX&+C 9We]~s{ &%]OJEy| 4vg4&Qq>5!G«v)HNkΖkagkLYHJS/p(CjM3ؘgShw%j*qk4Nl# دkbdS-bh%֙-&S?%?|e4mj +}JxHGI@G |nR3܋ + 6"sT44" $EȘ97 lUs/2,C?y ?} 5fwoδ-|bƾ ɫG3l")egvU\ߟ $!h[K,r@ES^}5G+P2jByD^:$5I>&IiBCߘƉ'''/'&*D썁Z'!"BꁐO18#6KͲL^YOu=wHI=3X%40"f|?*MsJ`O I."nhM.-TGźq;c9tSnmBF`J;GC?=r+M`V葄7񢧟,0%| R#l{F{ȤǦj.ȨrnfEԱ@.*^bSTﲟZvWi[i%畳np%܂V1lw(ؐu}CS@OJ)KҒCQꖱ!1P=]{o"z/k]9wtĊ V0,N+y8G.vȰ pi$ o6ɚ-EOϼ=^W|WĬt+.)tgJDM:M}:h:TR:RQ'L<1D(>V/|)NKw`)ix,17e(M'=`OyL?Z +fya=ڝ/Mq*u("Sȭ!cvjW}}Yvû|ZSJ{4Rm"_L@osyeX +\U:ν!ѳo S1zG M䮄wU\\o"ڔ>O 퓁E?Ťl|> qx\zhF|6l6wiX!4&zOPt''Hί#e-Sled9yo\s#6\UKtH[׷Gol ߱Nb 352xM.s-Ԟ{Qݜib!#v'e):@w-E% +[sA1B|Mvг ğ}*.jsd7_ul'Ǧ/GvJZHŪ>RP5{O?Z T,@<\EYmYm矁€: +77;8~s @GX&jŽeS1A^-:Ƨj{'-u 4ļ>#P[/ qK ,aT=5/M%=;Tc|䙖)1;O< %(|mWw@0vqeʇꮌkn}d򻛃#-0 ǦZ*_yjb[ĝi*-m-Hh'+ q &n CYʁuBmSBnᱚ +})ekgk> %fŅ⻦&hE8^kjjE?\wmbЗO34p*BV4ⲪiiOSO=옃oXN!x]`^[hֵ>doqkh %ۣUO'n-svyRCK:2ɦqRm|}ȧ5p/*{ORxw6-%lCEuHqɊ_&Ͻ&O)JUJ<C;CUU-y?9]w䜵_'i? ѿ q )RFO>i8Lp=yc%".zTJ}gՓrG4,%yW +FEB/ѳ M8]mGlߑ֧؜z8u/kj>KJU5]Q5&]xt0x_ȯ&Uԡ:.~g%뢶)ucŭ2t摪1R\s"1p +EL_L!n/`#blH}Hu $žz('&AYCJ=_69@Yq2]Qa_5r,&9O ?_IV Y('M_ CG" yCxt}o6t"U?V6e^r31!-=&y[VuscSI&lO&+A9Dذ?K;:~n:sY=N,5Lq 1~=tohll.:rmJr,%ӹsɛjN7IUf,qo +[ƒ΍uM.NͻsS +x5˭bhd}u.9ylm1Ph;2 ߷u [N-εteoSjo fa=mŞ6 cl/8Iqjڳ/G>LJP7:d[c(u(EV{+yQnLcw'QS,4)ӧ"5i'KLm +kϼ3TЇ6 AMW&ʣާ0ݵ9P˸ꙥ仅ñWihu +P \C5q梛iHt ]N1 0 +Zkm [-XugSxov?b\Kޭd!*B_I79$,,6?[e FfЉ'pWpevBEN?ƺƓM&é9ԵUt'CX@!WN?uϛ|rtGZklMX%!dj(b|8]Uss?_/ZΥhaX8嚮[bN"\ΎvI܌<L +Lc7[!"HE6ip ▙'IqB"xs?0urw y0˭vPv1>I y>BN2^ui8 + +!--!]2Fxe?MT~D +L(޻dx).91{wbKjKgC1f!LM\Ⱦ֑q*(9m#[^t{~ZP%?jw-,3 Y|yh =1yhjw JPgi7?T>tyaܛqF֏^tī=]咼 +RQ;EIpj:Ŗ[JsJyǫ "PyhG1OW?c UyY&Qih{Wp£YVA\JW-T+/>9*aOךm4$dU.?1A_; +/G,,]TV\rLKQ[ְ;S$TvɡԴc y{xjŁ!؋1%J&?m1 ǫF&t&Zfez@}LhkKz2ܭ!ToζMz&ѫMh usmjkK°I(6cs᥅F=ykI9WS+'ёڏ৆qPo--%.tMhqY9lnC͆ lK!Zѧcmk lbO[<`]ȥ#Euw ^%I4W>7T>_C>YkDBC<4-@8&[liޘbdJYs:*\_<N4Bp5ٿ~c_rh<뫩v#y~ ɥ`"a%7z%4P/W=W +X-Z͡ u0^yRVut< ~EM_zۺ?tni^ +-vsp/*rUr6 2KSU^S6W&o(TIZfi oT?^+{x9>:+tK3W^r^Ttjʚi:49!a: +3É~C!*d4gXyvv.2R4K]͸kK5_Tav5ol(U*HGw)1I+s..>Ykw)6 6p*D>_ft_Ex,a|#2^$d{Gjz +m$^״5OׄW=Yhʺ7晇U=]k[5^RpIncɾ9I~45]TxQj==~x'Y7=̓e6|*&fӍO# te[eό#W tV+d,B#`UC*.CHn5C>>`q&?/fY}EƩ:7z5L[U[`EF aa^-v]9J,fC>:ᝫOkJ5H947jһ_ 5|F,Lዽ>=6Lcf3/o~mVWL?jXpp.rRzi=NI v$ T!ۓȗ{B<$ &laíUL𩞃8 ,4!5*ppPGpV=qˑ~mpY<,>kJ6dg:OxfD%|]b>Nx{& !ǭ$1ëfJrKoѧCi0] +co{:d伀Zu16+EZ/|2<ཀྵcU=HmDgiA.uJ% " 4qṖO}̧9I@/{dYds +X-F_@Ls<= u +hiqpm2̧!m64/׳N`/1p3A"j[֡le{&{Q2\X<ͱup͵ڼ#bRi؟X7z ^qP{e[y[pT'MyNkJs)x]<_a"eXyՇzWWOv8ݟǛ&QaM %4r7՜,7:l,3$SlYDLh"K\x8du4 ?5jgk˧fC=sD-? x0 +ٟ&>j:U`EB֩Fr&AO"D +\$9q>5tL +-6dPaZ+x}Ľe朮rnUM +.mzM:ҧ"%7gKĢe* a|7. 4͖?mOtM,@8T\m_ۄW/>:Z iGjr|m ^.i)}"_}S/d䪇asmHd5̲RPJª5:v]Vة#V? "N:JrWC.jHNEmA5Ny w,bNWO[lV`Gр |ʛ̌ܦgpKxKTWS!hzK3LzQ%Gw>!݋5.@ %nA.s[&s[dإdW;E^ ((o)z`Bp<:X#Z{FMbr*h#-sJ8Pcx?"ejxGX 8XK*-@>T5{SUL@=i>Py6K -^aqόEYD4;]mi=%^;īAr:ؓ"OZ;F< saG=Udlѫ ^ZͦuN_C) hU>>ǧ$_,DzooWX%$;x/k>9*#yجsrƺEG[F< qN:յĜn~7o,br:*Pn-&kW {+FF:~WnTPW٦ѣ/;\[U>|X&i/nEWMse[\^`M(쐓 +u +X`?r9>gTء'$84۞ӵFgp +BᡔVok|n3RԥOՑ^&t^}DjsFS*ԝ1bS{4_;`<>Cw⒍KF/gw brˈ?/L^))|ѻTOJk,bBK$t=7V9UjQj_$geL3/RrOVyuF`o҇9hi +ҫ$d9?;(Bʩզf(ij-Iyc pkq`Us`_ -r2652pQ &B­3%l=AK2 Ra˼πV(P)0Jg/Cg 1ϭVF ٍə<2HI1 ~m{07N+LEʮD}ުco69/dĉn<1OdhlSԖHoٕz6MS-v'N 70yV:٨cR?fn[l=/&~:ڭ&Ѯ`VXe`.5 n=7X61X#[h+kt4Is/48);,a}Z5-fgoٷ1«Am:hQ0pt|,a#_@촒㋣zstW/EJ R.~3ls/|᳿sݿGʆ+;K촲ISJoX_≾߶Oxm[>1뽇KHN[|_|;[TedʩڄRfwpdZ!&%Vȿ&iuǚ]ܖϺlrhUfTO ö< $aю~}x`q  JO˼~Z$Alꌗ +;@5ie!SQV +Ў.|zOtzq}qqC'i\RM*% qy}r{w"sisyiBXþ?|_Yܕc[XW)+| AU)s+CsOjvӉ1Ҙ^JznEdzay۱_ǴĨa VӆzJa@>u7^ $1kze|&8bx_1H=%fm>e1]Ɯ}cduVSQIXQ;R~Z|-P\ kjLz'>ܩui]8W quU۱BD+eAD?tlmU4Y_R++#̒3I֩^"jmVZ^TU#Xl7sq3}z1 +jz%)j'9qJ숎_0kQ sș1w/FD{u$-Lx~s(&¦ aư~-we8[$ ٘GGb +SmȭJ:%a}́dw+okKʔ -O˾m>vOV"X喁W-{]*e_:I#iy05ԆI[&/aXv6YR-[8cZńU쇅g_GŽs~֭%`Yx +?l7MHx cܒS~F=6wp4dT|2Q,J"z- ¥!)?9qS嚌u)DB{o@^I2::FSF ;Z3}OӪi1up}YXH9IwV UYB~Q} ,3 +Rv5h⨎U:6BfnI /#ޚQ D 絻g}$}7uW.-2#)ƥQ׳B:~[_ސΥܦU,㳾ǥ!mwu_6p<>¸?I+;ݘCBüw lVW(k'IšcQэKz%=lR QZ)=3O-"01Ge)ni&hM5))˟zk=4+"&u}R\wquyk ϭo_.~͝qHQ&'ϽI}[AI- NR6ƙeQ>SQc޸Yұ>q}cC[{QM5q?^Z/F?mً 81S ;TV&m/_S}5 z1;-g]WQt[Rn7nv8(l@*"zn^z[6+ ij;U-7J, #ޠ[zH.?o98f99[{h*!e}wnM_@+Zo=:r`}p"f$uY>uCzxe hy~n>3i;~'"g\LrH(K35Wi98T|6E+lXxC/ TiI1•KϸصQ-h}Eؔ)F<ψ|y 9XM9go?fRqPl}هW&r>5ebj.R/n] +NFĢ]=@'LR8m?hnywP]j:~X2x{e}LTurϼynS~uw믛lkfHT:)\ _~Zk;zo$]=WC&1i1'da~)bNHލq¡r>4jbZʙ` $b`Uy"ׁ͇#'!qS~濎J qTt?mҬ3.QN5&KC3_~~(9-OAa4vjig-Q-b,nY%Oͫo/c(Y?i1CGaXKxqp ٘R1G^6DlKӲ8L8墐Iд4~p}h|^YĒYYg*`/%>BvC߶>my꧙g'M&t5~wKڸE[xI:H.\b|-- ]\YDK@ +N ZS/j[M*FM؂\@xD̔ZPbvAG!&[1L>:zz GVc1@ӗQm2*f%&i9;}+g"B!l X%!1I>"K[Nd|ۖuK9GwOB˫NX%NPG_5{i]q=(sNM&l]n}tњ=)307g]Rj$j>D'ڏCM'X"~MʡGl}eIK(]δݬGq,2([~Sg}ppJ޾:.C bV.f}2i/db2Ao If_HtU^|B<5Uq 8?]R Dx~Eҁ{PǑ׍?G9}))Kˁ+s/IASttK/˿[j3qjdmQ#)K:|\YsEa$n[qݬ2]J;8uY57F[8n}9tq(%W.@ݼ愑z Xbcdճ>)yI?Ւ$MmgB G'>]j:10+fvh/Y. 6vj7/Oڀ'6&[\"z~ :e^yi/ܿkAa-ݜּv(j^f^}xh8o|1JF~mLƴ?DڎWbF9i]+-_l}wswGʼn Ҭ*Q2.'i^ZxYхO_P~I1],fV8KQz+QևǨgSӚkiߔr1 Θd5qFis>-Rf^mL'OWh_|)%5V{.pnִ\8Ij]˯*wZnyުd*qįF +Vw#:V[x^+U.! ikVݛq GuC;s4̦7XU +/*4^;eԭ 5V} Qn1o_Vuc(MZV:YFM;dW7J_5~vwCB +kakq5JZ6nJϯʻG)KN0 +FqjR^, ?Z %6%)n;g gᨃ=ѽ{EyQ?#U|]}O,W>daM/Ycϑaspŏ?,4慎uoaӇ9fv஖]۵o۞>?< þ#Nukخ#?%rI؄ZAIhEI~MPmHX /Ǵ 3"a.9/j7SφÓR$r&jQri{$jGi_Y:!iX=&i)Z!j_ڏl2zrFznڥMI[#cQ|trSjemi?UW[BZ♵q¯ok)ŘUR#&vU!"aN1skzVIL*ZyC(X|ۺ/8L9㖦 +vB/%&VT-A>W_`ˡ_唴UXl;4v柖_6X|QmdP6)X1<ԡ)%PKR.MWiՌ Ispߌ9|:>AKĤ[3KAmXǽ0kR6v`6fE.ozK)BaOVFFEU%Bjq,ji1XG䒤CJ[1/ARr"_.hnQR.5fڥv!eFٖ^#b.uZN~k̼+095iu)E؞vIh)4Ћu I s :62i͍-xݜ_|EE!|T"Pkv;4J!г>!s8)= 1xN\ϭDRz{3Q~,7NyĤV:iU 8OJu+~'D=L.\yM>e]H9`-}jRNvUH-JY5 x=Aȭ\Xĝ0Td_2AT|&j驅Q d7 .6@M0craL*Ha2b&y(m_X|[¡dU1J;fA`߹4HDT۱ח>\ j]NE̦` +9)8'̮r:412N~s)hzRrDPCQV,6/4N:b򯴝埆(Ic_{܃?<R!z娑ue?>GDR ~t/JL)RFjX9cT%uUOYv9ZKviJt!v%?b&EO)K\-c^@Ҳ{?;]jWa5J:X^`vz> D:;^b3 Z(yVΤ]ؒSr7⬇x1(哰VAkΫwjg}=7S36‰Lp dl=ň*jf&E-ʸhmGj븍9OϵMBq3q=W1\=^ SL7">>qR6Aز2X۬\o &%֜Ӱ\N~F-R 5뻏zT +༶^fuʼ:vs'¬0dPhz&5dꬋ׌JvuiR)X !e?ԟmYǸ^YhLP2 [fYTG*,im51+S)8,T#viʂfvaVPy5~~z+<GLJYBqyog'ps1 ]ܑs9X˼Y/m'B_waPkK#"'6A_Q*A$A3b&m*sش\R̯HEj! l*5ryvZ&JCl=|](ʻ$̀yi1'Ff +*nTc(3v'由1;gMĭ)G:LI%\SԱ93=3R{\:Ui'FXYoW +xhmi(T-a槻`z{E&{? +k}Cxr:ceU=rAvVs#ldIy\8}Br^;!9NNnH9{`(C؞Ji>1g7G=7fY`T״/ kO;uiYZzR\@$Nc6VYƮ@هB2tM)+.nh +JiD 0o꫄S%%{{56Ĭ +kuܯ"VG|UؒqbK, W^^PQO=o?q?V#FQStJpuguy%yeQ0;>BR/%-ˊ_Ћ K]51#`X?9|qzorBd]#OĦS(b 3<2fxdBDO:>>|n_.}'nq18E; SԳm\O~X^Hc`슨Z:F(tM?Vw+@t +] [p\̀?$2zD(h`u'=`\xD0wO:;8 +fmݷc쒜Oӏxi754fUlℕ^r3!8 :ᒲɳ̣BR z+eNQ-?'dgܬ_&B\ .A+bj 4o* jgcf֕$N9;ԒϤb +:~ +ț +Un6/h܌\dg6gpٕiг?J@~<[{S4Ӌ[zg5w1Bzf1\] 9f^Ooq_q<9a=rUh~14=RV!X1K܏v/œJpt:1.O 1Y+iʐ zP^|b:9m[@@@G 屎#Cis2FuZAxYe\r [yrq.$hDK(Tpe_tX4\.gnnl2ZTK>l 1Zȅ\6 +'e%F.g>1#aׅMbzAbS+"Ϫ߆[&%Mʵ o`f) *yYFMYdd %mFz7m,҅aʱE~C"JD +I9 V6iRV+娓^07OX[%"êg@_Lwy{0] +zM2f6}rVĀgu4l)>*u_vOGgjFG13ri֯ +>fKޜ6 Jxk󋃒T$9r*I͗Q#GuE*@]r6SήYACmjѤj'q`MG=>κYe[MP'&ui/:a䭫 -6FgV%ǹ֥BQ࿕Ko[&YZ&&6̤bI+~])%^|Y=qY m_luuM@2J@-\}[?Fkt xx::Q]bjo8ԲmC%˹5୥[DܼWšmAqQJNzoF˫ JX[M92 a913GA̫GwK3AG0w]CD'lbs Q)8)k4!0rrӼT5u T|jRiuW']rn֌ZՇtuʦb)!yTc2 qqwwIi82vlN%[P}i 7T%wuQdG-\:)ZRKC-G60(h&~mZ<S9BݛB7bG%fEaO23=km3 +e)bCma(:u@(i NqKsήY]WCA QfVpàzgw;[.HnOFq+j 4NڔqR >t2e\M*.[AIl&PKsVPs" Dǹ״| u$Bqu*SN,b/?"sZꥬv'"_=+jD?ju RAᕠkCޡgmJb6~+sIc5H\+#%ݪ3VS!i͍7ъ;1[O_"b1C6#V11i[wf%sIihY1ݢT~Q"F=̚ (J|)R}7q׶SUL)kksxP=atkF)s&ie +a6 fAs̮HD쬈yUJ= dDr>&s[DNЖ[BKYTДshiw;{6d?3a@.qvegzeg^A+>dixuǠhۤv{#WsyZkZM7 g+O槺F5: sCƍZE!A[)hI8E_.g}Bz i[ulΪsRQ+' +f"yU fYo^6oMtl +v2zwDK+Y$]|_:1)f,ƍ)^1fYۡO_J7}jӤU6tq?>,߅a=|Ea dl2VĮH{ZƽgpO}~qs9h| Ph1#*b E,k>ݝZ-(ԘO@NA$(t*e6W() >(l902)\/{0$DW7pXwe,ly%nT. j`FX/#L +fyk0-HP:~eL Eŗ%- +Φfu? /el=[&miQkMe=p._M[5iAWAϪQ ZBτSYL?Zz\0z?]fND|piԪ,bb)O:% QS'ާpMJs=j) <@OA`7eR"*4}]:mqT\=2n&u5ͪGTRѪCt}qOoAS{=}<ۻ9>-5):μuȹ]ƈ M=Фee~,l,渖]bSAUkޯqkvAkڡ=p6m&of&9ĢeQ Оv9! tytѫ̠qj6i҈ ?6!iKL[z)e?dQə}I?5xԠ{5֙ANG]JY̬dǬja*"AWώv\~_){si|BxWs/$zw7`qM so +$@mrFm x  ?DhKӡ qmՍع KLx:Ipkf~ݺWe,EL)O5 w a=l%Ƙ L}5콆_ӗT9L8T/ʲ>J |WzifFӏg} +i ^y冉ղ#_H{ք v'棕%ʴKD.~u]MPGǼ +ͻ9:$iʸMܞt@ +;xk^ +kz&%=fS _ô3IꈒtgѨENcB<)cy||9@L'u-"n5̳ ~{\~F&ut4 (~ƩQwE!-*UGi1Тq6eQpp\6J<>aңگBo S:N7(9 =:ǯ;33dE+_ Hs3eR"+i=9+fo! Vٺ)g4nFZϿh%uyrhzcv9oj,f"isu&"bPO<0S &fQ%%Ƹt/iQ0fCShU{\ +it ~4 {~^Y vckIW'I@GDFMAy?u/tUߧ,Fޜ~ .|bpPB~G)jsO%:a1eIZ<̯Z#1- ~-hIqlVs3ᕲ.nଞ[ם`oRlU,eO׵J׆!z^󖯳 1,ao?> 7q SըE؁} faV̾,2<yVAc"Cm=Nwd|&A*@l טu+EYgoj<:a"šĤ`NfunL-IG.XFL 'M +Rc'1XEm #˙ÀK'< rjNN=72;w'0oyq[&n-twubzn(՛>!%bVyiN9) JܢHʇ-޴ԦJ[J)/N(  {;μK'O^OUb>!z,1'Sl, q).J`&xO#iB,S`OTرzx,0=eן4ϊ3\4q+HȋpG,os |'RGUb C^Am9g*< +( +.ѐg>soD-c@NbȿH(1p=_Lefߜ܂Sb Ɓsr!hSI;lc7A)kEzHc /Ÿ <~X"8Lgf>q2 8W;+>|-NG܋i!x=d6 6*qaf asT xC&Fq ĞPszYp{/ >gmsP2*t=*@n 3'gSİT3m_ѤIeǞ{6䗚>QV0R)zzB?!ji睫DST +&N`AZ`5|~*|v_*)6|A?6hJ\'l qIOQh<3!,pyMzt3"0@^pJ=2Lr UL4!j'G3s230v=5Q0_'*F7l7?y6Qz (Q.PO +Y1V7n&X)LZ?5/GOؘ~1˥G~$ls p0ALg֎sOg3KongYק^LKg؄6bEǰ7|&gj\my'3##QR̓^%cd%mzEv&%0i)YzE^t*#pF$ +LGk\e),Ps1Ui;Κ˵;arsw襅 e 9_2RXJFyg$is 6T\*8SFrYt$[dnr 'rfs|8ÔZ|ơ53*䳩̼Sef4,7 0h݃6~J +8OOm80HZkA;w`܏( g؇~ʮ8S(hI;(aϜ8i?jvg]\ť\LSC?V8g҂)FhfP6E"6<r:PBdAzh^C89bG?jAmxǯihX 0Qznr{1y=pΉr:.xc9iwoRh˝6V/h;DDm}bBkG|wkJ5!L1QA:{c"kJ*4y.7j(HG3aQ"OIu0r(3%#HF=#sE~2h +"Ɖ +b.@L9V {8eE eed'O >|Bj"22f0p@ /V 9t=uw WU)cuf|~baTT$&@.&~) +ց9u6Y}:'G*.XgrNerb6l:H?.)T[IjC/f_\*G94}+i%fyIj~}B')PY[#<‘"` +pc%i6LEMbݗk+-Z~?5t6#V`jޝarh ;A7B +яPŽrV~?3voHD 1K?́У{ >ȡ~ՃucFt:0a0o(9l)/{} THЏq  0S۟x$'E Q·iB?|>~bK¸b\ej6\>cQFhѿ?6;mu=ȓ16x?jpׇp%qM΀=x|`ȱTg1dh >i?F 9"C +bc&2z@{uy°'NhC͉\ڰ`o𨂅=a/fbH}h=B"^8ϦNA f"gړѡ;@Ko (zYV~2-s܂;Qq@?|1t xңIqo ?: 6z{8 8'3`ӳBú\f9(aF()6K-fe_+̓is(rCKۍwW~\jI~+Mhnn/3jﮩM2)zRۋ2Y׎ +I (^85VzA2Wv҃W NR mXڈB5qbj'~a;H +GI$6 ap2I9% \v;])6=(Qb05j३AYٰ&ؗՆ>(-N7uv~0ȃP}~?Nڜlx2-9de8pgR0GISB摌8W2Q6p~$g +c8qQ1]?.n0cl+ymX{&/Wp Qw(A'#Llop_цl: !O'D#Q?@= nךRi{\N/p6;I^AߙѢS?A>(= vǹ' 9Ѓy7i 0S ٰv8A.w\K +~MsGhܗQc@l.s8kM5i5 3YiJ;x~y8F[(s8>Z,Vt@p?Č3hQI?.r5L!~E 8`Yk7;g,|NZ#cm#5k$f'|*·7\[)>fHvӍV~Cu҈]k:mQZɞ]$?ey,j`=Pg oD/V%.m>`G `RDr=~t<̈? g)A=P;&Deߖ cyk26fbNSVj7hwĸVLy(d*哺2Z;LL9>Թ%d ?`YSo kJJ!XJ@ӣ8$vѝԠbf;-\7G "zow|6ZkHJ؇/8;]'-k{k)A]869R0ؐj7bg&bugMu'MFIig $i(YNWG膳Qz=v΀J":U +8@3A ou@ hh@ڬ6Yq<ġa߳ E//q?`c"'i:]V}9,2nܘ)}c&OCI9;Ԇˀ*):m>x{{p@rW&&ow< ~A|{I}2k09J?o;hA5_& b +" >)Czʼn4Yƺvs7) pRi> b^+?yvJ +r+=t$3s!ύ}I{gmK\UZk!^?$)d顇Fel.A}Q(~LRÆsX=CUt"gnbGMcީ4􃴨\ Лmxׅ$cy^!ь(;q߮a<+n3ot0CnwPo5a]nvJa<^\[lVQ2ŒrtR}W_'^iwqCaZl>xЂw:sW]Np;-}wy߾?CM抒3cEqv=[Ef5&z|%Z].[o+ +_o6Wj?櫍tfjn>b64XvhFTOr@Z( `&"ػ}ek TKXfTPZQ7`ZYNSWkv_&N j0b +k<+h"/)WۍL׻¨uL15U,f>5]}>z ö{( +rGǹ2#Yѯt0ʤo dOE1l, |0\-siYCiAP"-xϲWs?S> Qj/{;lXG J2 O`> c&s_͔`I>_ zJ>;\fفpЏBޕ'fPg*ii o[(AOZHF}ld?oa}3ΕbU>muqSuALi1`##<^gn\ TM]pSUb$QEIE.ļX,O0^#AIcmuqWۄFn>3Dv {عzWU1"84ʇ.L8o(IxkR;j[*t ~q}S]؛ }:6FD7/n;j?xZ.v۝$K-ˇ2iL@W>L>fܮ_G>nG'8}cHq1r,#,#y-:וf+Mak~V> $-6Uű/pvW>?II!A-̕cl16hBi-ZU?Ϻ—kÚ0!x4]lE_iFUO%)I6[F.Tbmaȁ5mтG$3|&^SV͗*#63uP.j6J'~7Sn}nAo;2҂\2>q~߃T:8f"n*IR6;Ku8eq+uII3NVՕ.Aг"ߓbIi9Id;wbZ^+H4- %| 4Z h'i2$Xt^idDW`]-XN0RӠ[U\6orl{ ָ~ nuw0PL {4 a{@dP +MSKp iۉ~7DbPI6_ +t=r 7gf߅ҤO#h?dqf[v1߯@= {M}J35p#& MXvH_GX) bLYO9VROQNuI闐[ ZY&PvO +EXw^e]0g=ΰ\*J0SR}rH9)k fr#if}̏4^ +DDD>^ƺs38wY>z0\/F:\-CX9deN M:yۣvFcҔaEMfD 0Z A?HfLT~v4))Cttn =PQDH1a>#Ba +z##h#} %sQs If3ol"ﵢw wz{yWm,Q㿴 BMK>و `3Rs%!Dw9-ětv:i$4O +i Cq$Tq{7[8/Y)̮U| +j[Kwy0Sq9v!zL a?%YdEJR,f+R-DAZw:0;r mzo:kA_n;/7cgz>n<޼7/٥hՕFZJ'wa{n0#BR5D^~+"sS'r*t'y5TϪZseY!1yp|'%h2a۔InvXaFR#RӀ]i.cjS,3%q?ჵ=hʼnYhi$"WmVj0s%柫r]!^%xV*H(J) +~/ {7_]C9t3^IwȵVTQ&N +*᝹(iP+]? {$Ai[]x*tekXlZ'%hKe5Mьmt[܀Vb;j3,eZqJePNweTv[i"youmܠE˕VzJ+w_6y6xNXQvF|y9|8V7#d@Vnav7Ɗ & "^(?.4Q}ћfVm,|bc~ +}%ځ{Y0Ev'Ĭ}o5I_4uk=88 DrUہzkjmLr#_xht?՛-T ?fȾDXA ++C}U?/5=7H=.B(T![[ib1t򢖍~|8]2)Ͷ"M I]:]n.,0BfO5@2svӘ>JO^mb,1UTןC\̷J$~l7WBTI^knP#@um\V'; mr6{aM$וFR3?'dZzf#'vYbgU}V:ӉzPvLؗ2V{|( 瓙ydņdFz9p?@,x3_ Y.MpC /?%y?hc?*Bz$9<$=Kh{HSWZ3U/]a&zkX[:PC +@ n5'ۭ7) eOɑYWv\jl @^n.#48Ͻ KjK4Q&[.Րcub dFZmgE33Bb#fj>n4ao ?[bďAg.N)<_A|_oG bv// {n7y:rP{KLVi oxǛ!Z[=^P0u)¬uc@{o:tk৬ֳj@6%1(N\ 6]KԏdrJ 52]eAweJd?QN1g9r.K?uȥҴ ."(hZ"MXk&,c7[ }{q?ÆN%KžK%u$uEFL!ES)r˅A`Mt̫ZtI k1-um7Wl=HetSyuSy#xQɟpKHE1\znVrj&GvpR<0[m*kU@&Eݩܚ!)mfG Rw 4 Dׅ6_YmnTa"Mf=KhfF'rt9^nF/3~L*S2>i3UӐd*-Hyp?dij r /i;7m97WLqYdff+)I+ $ ݞ]磌ws3 5 'b4b#m.y +??LSxɟЏQeV:(xd&~=Ur^NLuT:%2ߊ̏y#/}ރ;KSk]"t:^Pya,qø@U}WY!~vR4V jɢ|*ܦG^A{9_B:@!/!_A>?ܻvr+7nAqjh0r'}%j=],'ٵcV2r[+N/! ߃>ys.kW.s_}G_ZHЎJnzLc{s9 +b!Ǘ/!ށ߃sb1Mȕ@ jDy#5K6r{nx_C,qbz)3א~^#ȭA>=l'ƿE~ߗ *-Knȫ9Bz,{G/>xrk`. pBq{ڍTvJuķ, 63vٗK܃<}!ӗvWaw p׽TGezkmbdsfJenlc!%26R˧rb5@LB|~en-~!Y 񣪎0^AޟC2˶w!_`g)ם,!f۷!As}^gF;!^_ h=Rە^^J?:]wR%}^aDۅ C<\u6dS Hӧ !w!?X^z%HYJXf-4ɮROx]%LG@=/@--!>_, fϞBr ǐo&xqaRfAV'uO.NI;2!eko!SfcS\l yj_@>qypoc0O^@^>|1} +'9h(f?S+Xn/JRV1|&*Y4fLo!C^@>~][Pw_?1}|9G&BX%ޑq R|w}i07g# $a|37b%?!Oo ~ 0.1#fa"OF~D?m60[V-9iUti)&x‡"n'{ć1x~9ը2Bbwuxͱա̙ b_(XRZdarħ?H"SvY_=o-JDrpNhrU]/Ƭup׺3h+=”^h1?әC)ƄpVע=\FpnOWb⌂D^eASIwB`Φ[HQpO")ЇqRHNl_6C>$ꒇ$xś'`'$I>Y]vqabQ|L[EQ-X5K1*hpҟrBS!s+ї_I#.uCYMYI<ʐ#D,Qf{Ř>1FٙMj(=ELuKXQ;]?+[{̽3e@ۤi 6hq(۲e̲ebF[YdJis?;#xli^8g1+x:HW])q$S%@x%{˃h|;$)za510PQ=RwvŖI۽ezl]5*&zSC,I__|Q9W&/e1yОnپN*ݙ?r ѥYg[5ڋ塮JHe?~yb75Mc?PPS }-UӐZ.;LxCss)zbS+]GeHG)Ymm,n(ygɿ1LK G8n"?2"Aato)d'MtT]A^ek0* MnbOj[m <͆ߒOM@6ƺr4r shor[9\.0j>S:4z_E\'s +`eD<= f!S6*٘%Hr㸅>1Pײ2fǦC6U1KFiIvm)$o Et92BeHMDLTyŌ&שC2|2$@[GVZ ![[2TGAi 제q*X1 tSjշ+u/aovYGC]"V8{<Uq)^IXWߩqTrl Р( .ž¨a SAMO[*)uO2b|c 1sb'{z 2B3n䰂-.߯fr6>dg4cљ}̾pvE=05FĦF+QѧtTp `~:&QXo:5A)I{:1o.r:kvS*'VmI &8bHԭXYC^ .j[ Ih.Z .0jjG@Fl3z唎olg4Zw}gbXDdPMkx9v[4՘?SBr"Tա3/!TT HDA8a e_i}zP{6Qgͅݩ=$`E-)9V <~HKٹɺ.y=ތ;-KvI1Zf7j`ivF~ٜd2}v`~=1sXA 1Bjty@* @OZqG|r! hCjJk@+v +1ٛí(93g.3*>dkXXw{'| !'6er31q o&Q6'CbFT6nˆdR +r{8l3JŅJ4iGD'CqXr}k1gV:+l$kM覘?7.g-FT`.T߈H+4$Ż-(Ɍgma$*-7{:~ǵ5)w0W¾2%%&P/mU?T" Zzb$"12ty-GBNw{WRlW,8 6:*\{|{'r=3 x-'ÔFk6=^sOv(o^=b [g<-Ôx7ړƇ.8_NNQ'5ǚS_:wf[*_Z8WI.*#Bf.?/܀e<΄oc=DuT90w–؊p(eϷy{26,Ap̵? + +Yj&/# TYp湁 +Pj[+! hyࢋy"#g}JtO+`.Q{Sؐ䑡KEY +endstream endobj 23 0 obj <>stream +kM[u=R:ػ+q ;fjn:Vk2X$8-Mh)>%ֹ{# i.Y㭮sqlBם0'Fhw~@Jm/Tt}9^V@|Yz刼DDnSҢ+cZB멱gX[/'$Dزc3:%.9$;$&ĭ&lӱ9apRr,nki0.u,|^K#:LV}ϟX Byn%ԣDnΥdBZ6 c|g%T܌k:B[H ~Y 3Jv:_n!R֠iG7&fcZ|c2 cӱ x mTk=Qk츎Ch9Ԉyg=k[Qc<0o39]]]28#w(^ ߿Sq{h]?Rvyx=*"c[3b͉peOu(OE>i JϿH hIw;" \RT~@*jЕa9.u89JJ|+t<2ſ<" *~p ;^ĪLupXǕ5F}HLo XȽk&q L:"%&G,:9H~ wͧZx~Tٚu !+4Lɹ/^­XryX݉g,jp~%$ M00O;V.(KM22v{OTA\Ay%x{X{b:^LgU x@*9\x]Bl"&7]: 8u廋ͩQ3ɩ W?>`F [sUdZTM150A%0U ֘?)?T-ʤt7f=blu睉 +Yw:Bg㕹[ %3Tӝ)J@%D,tNԌ& n ȿ> !#SܡtDLt왍8bPZ\646$=&y(jxwuТ3A|-bh(q-ջґUк#s5Q[""\iH=hb1/[|5IYp>1h3iom4i$vv{N6YL o@-5=^,H; ŬK잇`MKooͼ/܋-}r8 7|"rW@TΈ̖t>\@N +dAD ͎k!!eGRӽa z@ NԘʨPʂ*LG̲"Cu? 1PmyXAw a @Ę 5#%5ġ^!QijxWb(kKJF:kf>"4z芠*|%xa Hut[X';n8'Fﻖ!>1jg'ԟ8;4jԄnjnyK&*ޫ"X[$G-]@۟c{ +iY M57z:#jN BkQS~n#XޞlcKK2(' b:6>$&:&'NJ:o+⸄x0|96QwGR]yp[m{dx"ȳǀ|MrЕS@z \4*4C+9X3z^9ұk/%+i_yU~_Gu5w\;ŐvIIl:Lɏލek觐 {"ZhsdE-'{7#R[؞%%1i +T4 yIwml0i?\Fݛhs4UOоP\c=@(Ɏn[mDd ஀ 5L<]ƌ )/RBmYCfW:_ηTr~6U\JȵA@D ]Q3zmVw4b[}ى ܹ;ʵܞ3S{k_+3sjuN6D_3#%Q"Gķ +IoPӻRJKXerXrp&|: +! |b[fא9k⠆ {D:(2̷>i~vdxԽ,IY5ÚDͱDcL;T#0pDZ/ h052)T +bx^@X],= OG';ݳM +ƫ8K7 4Pgxf5Au_Pńژp~ ,sw掖t#H,81> pmBS]2[vοyWOͽ%;?@@ώ۞ĵlWǥTSgV[sWhk9Xs_{)lOK/Ф{+oԂO/;mt yp0BpZPm +JΐwZZnDdB 5'c%=PM8!5 5\/@_| +6Ͼ98>u%׍!{aOD枖'5Xʭ^O_<z]m}YiN>ӓ ̢[[KXiF+jN:167g +nG/ۦ0GY`Z5jo*9Gp ~qk;-  W EW- nЛ=Rw8,R*" Ьw&"Hlʉ8WK c%–[n}_z>Q~%.D ,"ͱ Omr<,乾m[F'bf;Mp"uY?"Rh :R⒩_~]~ygP 9L@blk#6Yum =1wa %2<"ReLA3@TY=\Z&gl{=W@)|ԆT?w9ɔ# uf~3 +zM WSy߽\2\uM@9/:6;6 .͖–d55tFO쭹!a1Yl=sFht8Ir.34TTXN +W2V9乘XYTuQ.:X}hRpJ*{qO.版}0_qҝs&3_:_|vd̀mmw~&d} ?T~T6Os^\):Wa?tObRFʅ RC*m{nd3W޵\'#]TVK#JJd#𢚖 +,vbJO;{T@Ϻn}ubbN>53Y'&s6^+ilߊȐr ؼ +D?,@K VbA'݋+ߣkQҡjNhX間BZ^׾=6pY~x1%[ָV0ٶ[nU + w^~ ee^4ͱpH{K;5ﶉw6dQ{ +ψhh!4sΆEDLBE< ~>c?Z0RnVLp{>!8UWvE6ZܰN5>oz3 譸]XG:IJ,=Xa 2^xH^\COiԛ@Sg;8 ?7z,4p {*~mʉb,<zB[?=_}?:10HgVnwP ht 8QY8\hPmM |H_#Kf@ɻuwͽWw'+ܺwܫ*\WyGӰUWRp_ElVHJjVs2>7`ίv(_so$4W@ +᙮EX>2 nk5^,nӺ +l\/槾uxQT-JYMY=v\K5f s.ݳ/?81ѩA Z' p@JixS߼Otoމ +;h.8$cdBYŵ'W]-ko; Vx-pXehɏAMGqDm:X1M^%w%jMX@Clx2O,։>ѱA -Dqpϵ>+)m.}XC<@S< DUk7H JmH@՘ZԵ&#',:5SP^$Ho+)ǺQ MOu )ҋ*FW:N ,&IhƏ5&2Tg !.c?kO[MbS*#u,HD߸w,Y\Oh! (BEN"W&d',̴=pEbZRUw{ +!َɶ*nɷz+›{C  @4ef޻-睍pžp߾ع PɌ[R@'&nwfgH-<:-"խڦ2XdFj# ?_?a)ت7%7?(m':Nq*?TL}ZN/n^mI{qnc_&2%Dd؊tcty@YKܖ|bbmN~i[扁 v_{0Nm{jWHahz?Yw#"ֻ +ܟ{"|W& =H;@7g ~ȨHe\jcy :x +1u QޙaUg'':٢N7-j&w:$Vb ݕ7r|zеo>Udhwʟ\ l?𜰞?<],Se@EjGDM ]0q!2mݳ@Om /j"lwyQૼB[4iobGp+nM qkOVPe?چaj*! 4& `)/z6/#y֫lOd~{5 ]7~Gv<;x3`YTńkQna滾5+*[HʆhZo. ?n|Ɣ nlӯ3`Pb2li`-ykIDQ~XED(:m(FWYm=}rdY-[Oρ_{7P%~P5XG|]ƍvPUύ70opA611iH>ͭ&_Og QksA@ +h= iRLxUG5\s nj5RmRMW勏S.@81MQ sDuxUT~iw,feE䪐q}Z?el^rRzi 8b9!Mn4$i蹟:s5Tv}\@33Ӊ&dͩR\zdɹlvP2Q.?5 &*&6oz_}. 3wu[Ĉ +[k#y ^-r+ -%%O'%l˃kG?`E{8ƟI^)j_Cf;֐Yv!,)T +cy`tl(0&%rtяV 5cl^@rl^-[xˌ\;lwc|C rWJm ++y@\%e;בʼn^!-T1`a Z,ѫBp Ɵw`N1p #[i.=9u|J~M`͝IL'Rf*:[{X@R (j@W`iGBtαCMBÇV kn^%ηNo)Ǻq1[`{w.́? N^Df?mzpgZ&>8HBl#uWՕ?=8\ ք>60})DdϷ>Ik88S;$:^BIypY +:roY(\t[ ʩA;/bbk*óAkiM>4wo}pD.|G:"ho ]+ߒ˯8obxR:'K~pb$F6`?_bٍAs6LOCQ3,u[|lP݂$k 'B]{!,8M}3'{}~no_9 dRzbCt +P5|N`̴/6KLEtgWx`R`k,ֳ-(nz+f.wnXM;bs2̯jW]UE|\`ÿW c88Cx{` akFtGxCLtz8\26)VT@^mA[YS3qsԱ3ѱAܽќhIU pX$e^AT4ё}qGco>hNt_C@H*nx8roWtaPUг-jÊ:"%{FR{ȀAƯb*2"bbT}9qhU6Fb<ȂMٙ%Kx?s_|?8\Fgڦב9 дڻ+M=xXDkY-vN"uFg:$ݹޙf@10q3哣K+~3P>|y_I wao:[@GZ;J&pX۔|ͽytQD q,AuM䌏'!~fkz2Ȼ/fIÆd %wt}?2+Sy=bX^.[6cJ}v (Mٝ|b,|XuLҩAJOZ [i!]HHx1c/f:@#"Ty@s _V/pڗ5p[)7R}5h-2z鶔3 soV03g ԹoaٴwY-!@Ԕ$*$eYBgΧrj3⁵в֧!)0&9Rl퉮Rr>Ybe+*p`Kw1A]:5bEu'mNyxQ'&>fҢF.+c`O\Mj/9dΣ^KW_me9rw{5; + |U +EXʡz(= *;wp?Xg?0 tΙ7ul[ZaW<*B=0֘EϢs9VDv%"*#9)#?X˿ۛk{ 7ʻknT2q~W4[Q`˔CǒǑ-L. ث vn3X}JZc= N +i$6ydݹ<`ݩZ{UªiCX TƠs+\QΗ;`LnOFq g:-v8Č6Q-*L e<[p$Pu~_ -}ٵS 7uKӭ-"QUPK}bc;k_γ̢3<*|kXV_do6tcHڞj~[#UD̖9WO̼>klvlbd7Ysy0ip߹?HҲ/i9FO>aWz^v#m߳/B\O. uA oF7#a=uja<⨚|Aiٸ# չmߛn0~nxY~)iwLCmUW }_ZG~_%[:8:R4o [*J/g$eO +^Wg[d[MyeLU@Ή뱠o;IxlRRMB?RDs1H,1pJN} o@.˷Nu>gN/xl45[WՔ38ӠQKp|} Aa;5wo751<͢ʇ)DŸj,el[. Nbdd㝭;GR33Qca,da +:#c㢳mnPGn + +M؎aK< z$d@9h[Jl:beڭXNl (r'n=C^m.|v?3uΝURѶQD)Jω>mM6*DCԑ3[5LCCr:Խt.@O7de~.}?lխƫ{kuyihaou0x׾T{ۿ|g [7KbUrbɥ  7j0 Ȍ'ƞ,*"g{LUuOηs_?ZƾV@H~g)0=s /ÅڸZRwf ~u5>':'VՄ?-$ؿ/aH`tŭ\7̯Wɟ>[r +Z"fMfom6G[wfO,ByĤ:^䖀ךVzB + +"5EUIgWA-4|Hhwʠـzg叟[q8 dE|)UT4}Xr xSa+ڒgi.~Lym_6QJI1xر :"bg;*Qo/K;g;ݳn9 9@ytB[oQ)^],_AX)eDCD(ݛ'dꍾG쪧>UwoPMmpޣ$|X9O!ߪG`IusgR+$ߘPc҇?jm Tf7䘉Cs%v/ƴ +99HZyxQ+o4bcw:I ࠚiY& +i>5r.mF'<:(l :a +\aY胐GSs{#77~ߜ~ۜvqw+ yW% xӍ_֗ZgHk 6RΙIi20bew- RBw쯓fZ2k^}9Q˨ <>: +b#!7]R[n8 ACCO%=igTݯN<)δŖڟ7e*}@5b뙞83^),ǹzUaBRd& +w`k3@]P*NSm-]EߊY_aЬ6[`?ݱ4ao+ Ѓ_†G~ ~{@`2 ?`\R56Z^g4-Ჶn Eg?ޑO!< ܰ)X'}L й12;7=b +Sشy|u2(6+ z| XDCtxטsqwPZvF~:<jT|ŭ^]1_1؞~9'Jdз;˔ofYoM6ܶ sWe7>Z#fo#~4蚭馇oTԎQXg,Sgg5%4 F!/7G~n#/.b2:2 {lC&O2S>W>g ﭹm.! fƐ Sr&{c믹H\sQŶG.#؂nkBIȘjd.({ĺpUCweS'ٚyx5O?h<:H] %9L؎-sKԖnA 2^XP /[=^+͈϶8[UbRc4_c%Wb<$nµ yc![`*6PLiX?ۤaarM15h w'<~@MWxo!푦䨜>X)*nH8IUe6%^)+8@8ަc[dLhAq)PX]RǛYl?2=O6 pUTG9FȚܩ5NsZ~0Nղ*/"3SR>7z4~5tWU}/o5>=VwsktǓZxTzB4Dffbqw&fue3;2.vo;ycU{ +Yh8?y?YeT'1Y~SSuxgXCR;)o+4 [WRB8HI5q.AVzE*P뽣9dűo%R|3K!Hg6\]FzoCR_X6Zh;R]okd(@`jUo/>08\㈂ +{CԽEd9/As=DVLh]Ri'zXNm o`*7ŷ,e~Z7ͦEDV@DQ)$ %tV+狃ִSxS+bۻ-XDCČ ¡R[ ;Z*jW*EU͋W<=+y4UH- x*%!Vu=Z)uUH,ǚ۽kE~q,(d C>nKE[ kMY +Aa.EZ۟ل@ٿK阵o_z~ȝ<&"Or.< /5w`d}1גz4S?QkDDa,lN-?_sUBtC"]3MӍxk YL{nxwqd:&q6-q_=㐢*rsHL(+MLwŌ{cm凳ϵΔSi_ 9^9 Pw:B[JU#DVT}Wڣ.zD6پvA @,@RtL\L"8׈5QhUR>M[ZQzyRrks6C7]U'3Д쯆Z{H[Mw>i XG{ HwGև^S uMFXe'fHޱ:@RE҇{#k-B7AB^{+_?0(/qkk_]NRRaG1/t=I_L5)n0@s4eO9lPm*EUo+R}O ++9=tn熴dCb=vbGd\s۰5fľK:^ ڜefEjFg_""=̾o$S2=$I$&&1{+  +";*6{-;^^\Z>W[}iq ,wJp޹ x߂*O5"igi)*WfoܳK7Ƈ^vٍQ~eyeR1~jc12׬;Ȁ7 7m5"cEHUXq_ԑշANsXv,.b\2)c)#̨ՂѺp ]cҮ%Lَɋj8t +M+cˁ.ikvne=lz푓 {:Nm!13Sj.vw<^Qk&{hipkOJ_4ވ,)mJrfe`i[&h ][+N\3e x$Aއn??ƒ-=[ Zxٗ9bÁZTu&Geд<Ŭ>VݶQd<:UZ u.NvKNvFQ8& y5`h{˓)Ie/3猗wmьQ|umkzd/̊{k^?a+6JkqS8Կ療O!xY[wNV~eّe#pajM0h0p׋[!V4w!Q6WGhS㒆w;|fL ԾTV1ܺ5c]6QczK <犍1>omF.OW>91섿Vw9W􃟎[1UH/NV_˿u&' ^^?J|76 # wK:li^ݭm:B .jPPЇx rώ.]c%%6)_3jdgӊYYsʤ1qA +y{EUkwAۨX0֋, YkjPyBHmn]('"1( y@u!@B/x LI*n -1 $oImy:LV|=qY J\2 |ؒQf`uY )/i1-97Egٸy9=㼉Rcٽnyb\7 Zɛ>BA͚S@b9АU-xqXw֕m˫5 "7b•Ƙl͊SCN`_#uoFU@ɫFP%D(}-7pw9 0!HCe; H1_MHqͻ.\AXxeύrJi~!$a^A(w=>\T7?[oOݍY5!)jz^Vt<[7C)uà5CvtagPu(yҕ"4U2ΪNԖuEm67D)S! Ύ;l̎P²j!kob 49ԙ=\LX^C,Ě; 2'o=폃=' >vMpf +s@%Y{]i wK1B9VŻļ7Pώ!ӟBS0 pV~U|ZV'*g#6 uˮjVoYw'd2V G:^L +tR2*Sj|YsӣㄶXYW;_4?քLqN-"5/zlmhEٞg663wO9Io8 +S +qgJgy腗mYsl̴KT_3wørX)X`q+cρZ@b E.4Yieh +W% aBi0%ZD_kǔS#۞{ ~i0$AK:XNH +t7]R0/߱ߏYԀx(uQ}\&@/&Dm4-vC|39Yd۠Y[NtՇ *sgW:ddP>_=βz>xpaH󽹁 \[PA*ZԐ<ꚁѴL n*W^\R"Jbh ͖ > q ϭv<]' ƥw $}dĆ+o3Kh]}uMOSa &^eP+?Tn2b|ViLW\^\P6opn*pvb5_,ZW,肈d5٪}?}qĚu R-E eLͪp ߺ` 3JrY@F7swt؎kl#bZL_wxݙQn5%*wߎmJ̈H5V6d*$:Q)"poZmZD4,ܞQy_o ~G*hίZЍ[yY͍rpʩߩ?OCjReGq.';dKYZ`O#8|wh5.f4==NND`5K.+N +J9Yt7=W{)yOO| G ^B,gf齓{ǦUʧo2 _R;vfWTNpr_K){LM9{xY_/sdZf䌉oi#:D>͹G+w-mo5VuF*n>8-~L=i@~5}DȶVioIE,u;yiV5WuM+[`Q^PVЬ/WӰjC;VZsxnr4!au /yW3}-OW,v@sa)H}SI{TԼ\=!-عEW_Ϥ\vv<ڎ[kAc]]xF8xB&ly3G.שޢ[^iD*ow=m/K 2EhxoݝdNj lؙ-vŵM랏ڈiY#,s'Ȥ#ETY=,{ю.'ݔ1IW|TlXIzf,i&9Бw~R:vX^{P9D-e 8kf4E-q&Sӽ?o_ucch[T[8o#,)u#TR5Vm䦘hXґ+B2pZ[Y;k:dA@Xۈ9烙B^ !vo]8"ɨStڢU0e/1VJ˚\r0/ =[U<M6ܚ쫿w<.(r2,I=kVYw#+C-Cⶇ3C/dI):kAl55 st|-S5ܟTz/ (e&._sg݊=+eO Y +[ؖz9Vݼ=aÎ*[+"9Q%8T8,2Eš3԰ꄨ0Žow]d0po;2ǃ1Ρ(c;(7 屹O8S&ReW5%Ua+/؀G^ +nNY3#E;YI_.ikUˆq'sJc`Edԕ1gՏPnvf: |o?+n/ѐsz˔Xc7Ǚun͋Xrfm kHƾ^AΚ3&T[X=w$7oZȀM 3JǸ1ƥN)gwKq8}LaD)<0nt!qM1~) VM`V,,\bxKypyY#m.YiQ @'yr!I+6t+}ۉ,|PºX4Q"2\ւP<jJ嚏O˽"5Kf *TƼw4&&i/1]wpAi˄̚Rᥴoz`/\.QUkT߰bVFMӁk>w73r䫷l'|:wwOhA'G$%wgUף=p:0رufmY6lSDZZ\ k`+V zdֹ|r'pJm\0sl%VKVm,Т0a)傁P1"z*pg<1McK,XiEy'?e#ⶀZF *EszVs+yI*ز6qH:j9؏-Y6KRww֟3섺ak~:lAUkNnXw We;4ou1к. +NlirFk;׼D3^wg׽ct%)1:jG,ZEC-aj ݀!qãk?(MM/%0ܙlO6x@xIHZpxë ;$Q0&U5~VS >Hᶷ#c /?v|Iie#"}߾GUm{5w&UfTN C7\)M]v;}/'#e'q+: < InB Wu \ΧBX5"rR1k ]wgvʬ +F 6f{lj͸ '`QOv3Nno#\۷ 81뢶/i CMڋrxɪKX+7cܰ%+}+(ژ|ڶH'}AxՋo +飞~L?͗yGa';rG@x];fE<ȖW(<--yjpJx2!LSArY.uogLhE >)9:mJX2Q6qk"KsIړk_<ޖwh'QxaƆnR*]\쌞\wq^LH5 V?L `"-ClEX1Q3zLΚ 薇ޟ1Fmc +Loau'CԬYtUۓZqУrRXsfN'̎t&. ɡao/VLc<[vb9j Ϫ fd$IwBFmX ~قK20k!2xE$Y#sF=mc &{F'z ^Zל[%3q \!CsG̎7嵺e<`Q`KQkVJўle>QGA'\8k!'ݠĴ ^0*^ XkOs%쌱<:1 j.Y KfZ˪Rq& Ġ֑Yœs2"Vǘ:%Q/wːEuz$xQkm/\6:GX~=ΨP#Wo)m{~b˶߼ZRѾdږܲf 6|L^vRۦ‰o{#$AܘU/ DM8ejzA Np#ژnjEWoW4^ v<= wKw&6,ҊXZmySWڑP8%9kk\=Bs#Gk=v) +b[m=|z}Sz.vsJڌHu/ң2)5[=mos)SDNw !q=[ CbAӟI܃}v6j+ wC)n(wio/ݞy3ly2ZÔ,)-lbxeX!&b{/ٟ;{ͫlyˍX .l%ۿc~͇CTȨIf+,NEfbNW䨕 Т<2]ǸݙngwױW}"a@9ж~ L-SُCaȜJFrm'>n̈uF5ZsEO !l!tUTA HƓP4R銾{+1fNsX.j +gBU&=6=5ϐaB'dspsDԫVm1hFi6b +^P75;}ԧpi .K +d', NݧhYhu3d_2Wcq =V~pZn2yS7rA.y@5k| x?;%gLI2e8TΤ@?RYcJU~M3!u;eR=<Ϻ):sэ1b+WBvp1#pn1g6Mq\8|%)?S-&^KBjb?Tb@M$(E=o;jiBn,x!kȺ4+bhZ< +T ji& u>z\ܛRoDO,u۵.ԃ# S{Mu|#Q?m(#|;u~ٱK 7D%XX ^bUt}Lh{ +iT1IO~t}~%f2<ܪ`;)?(Ybl1A&-_ۺ'zd~ʭ2qKQ +| +p$M[U_; KD`F:`VOY6 +zYTKBgvFuk0ff3Lҡ>SXl0@m^%Kpˆ2ղ<܌3%{<$oos}-zd~ćx2qČqlKs$4uإXS/8]\@퍛)q9YUI9@uI2PVljԌ9O7RTՒ^URgSXkF4u&>+B-])>io{GOTrzjW_Uk1.ٛt0ٻbT}5ЯRWrz)zPhGt(_ Ҭ#aZ8&oa0ɕ>Fl.y(H~:{I.T[%ou*?2zQo~H'FN +aPi$B*ef4\Ƃ q]ƫȎ-M5U?k+-|w+6굢4 X02e^dwC|9Gqshp\"8v_jT?9Wc_.pa+9ACWY5ecmZHS%z4g~D.S YhUM潆rr1zDžQykCՌ lMN0p}C%h1/KVFy%݌e;=CM:9^4dm +gV|Va|<7v]~Tn*{J wza\v&$xik}0ЮRb +WrhO;[0ٌҞoǽ)~*w91ؽ({ w7}sطu 32\v8hTJ8WXSjdbWjiKJ{ +k]qYc ^QN>ObUʓ̺z"N.gs)Ts +_=]B*Dj=| +<.mJ/y7n'"Tsk/Hax/bջl S|OU Iw[:hK͈KmˍmK%'ߤ3OfN$F;Wfܽx@͇ qg/{t.%o *H?/kٽR$omOjg%aN{8;Y?{v]쵄b_j`e6N4{~"_5n@ҸD6?juóxRjys&hn7);En%F|k܍G-q`NCcMh sV*/ǦyG ;O.ˏT?fT'ظZm:T\|&/jڿ u/O=j7Jpkdy;+&捫5XTvAo&F;/j͇qeS5 '%cKΤ,CKz/& 6lj3Y2io^N(GmOD#0}Iui/ھ}=V<]UrKW;|Lc=x'E흹ugG/ Jk$fPi=Pw=?=Kzu~(]]4ui:#ƒָ?c5Q:/.shI*ggBOAOF*ʈ чg-6/^-p*cPܥ[ q] +z,B#~UuR"Ɇ,497R:^yƎ@JV]ۉR 2J8'*{d7kRybw[7'Xԝ1ֺx"eDjH KQvHW>oUʯ빖SI^PE +_o4K"Jk . xT/w2g Ē93$i^^_^?lgՉ.j}Z4t))O͗t[ +o5CW G/w:i8gSu;~c[Ed$$t ͿlB_:,}wQ۱ϊ& ['fnG +w>qۅDmhA'MBZzŮ*БGXP8]R} ,YMl^)Jc&U8AY7b|CPs= &F)k0?UI&ԴI.䵝\c0e$FaKbCud6j'uNDтhD[a4 +6"WHuU>k%ODB›0V˓ +%oDJѣ㢖!▗? R+nXɜهfݰͬi#]}}=)LzCzḓUwMDVYS'3ߦr>VKav7~` vm=*N ^vjXvAtCXݣDݟE<\w_~jJ%u{ 7,&_$õSjBbي >"`+%#f 'GMB[(O㉵9d|\G{R] 9]1_)-1昼 =a&nYC֎ T}9ެ\鈱Q0hb@ ~M).lE.9X]>ȳ%y}C]1gӦ$ ERr1+FVw)n{*g|kRDr]6LjNPն?٭؟{5nO˕2p P, J+*jd=8I}j)ec*tN"J|}mɆ(qaY=|ܺrl֞^1%X1a gԸY1lAj(v>fK5auVm.qj: Ҡ22۾Cy8ےMof]o#jȼO1-ܣeUri2OW?`k Q7Uo(sD*)*zrۺ\2>u|jzl9Ai=c+A((VCގ19ڌ A&4f h jH).(ƽɨf^K4̥IYc+ѭ?.bEcv)o؝ F{>iτɭN|Pn4&5?o!+ND4h}|0J}k䆰.jjM^r&Tߡ7=bՋq3AsFV8Wn1nd4ioؿ7鋺h`YSJ'bp2HuD 3Y#Z+E0ajԮՌpkS~ɉ_⪷s'Vj߁DX6攐㽕C} SR$mD!;9Hm[j53$uӆʟ# +C$w׉,q"KpuqAk^ >4l,+Wo7Qlݾp0FlkZ *k"|6+QJ} ~ ۸B5>֜ԶE=vZΛ*4 i )UXI4 uoǸU<%;e+!Í.^/]ߵv)&OG>S>Pޢծ8c3 ֺXp`gB|.y\|Hoٰ1:׭x׃j|P~OaV,IM :X긨%sѰ?Fl9S-OC OY!2|7XA86@oV,wB$e]?0еs3K@NSpfBYHM*<Y7N joxYeW#ȲcE3`=oosGUBW#ƴY?t8aJ2/|d.^O] ^*?Lb@8#;d"8ʯ3 jAU^k|L:=Ke茨P!uL 4`7=MzXڤ[:;LȢ޾l&cnDͧ1Tݗesa%Bh_gQJd+yF|n%ofBv4تU0+xkB} +{%n^~nJN0h_umŝő ;=ĬYscj5CݳE-h!o>l㞋Pie-(ZM_R_oSí{k,a32`ֆ&L ++_wk.v͢iIqAsȜ =<{aن3ڷA3GNVrXm3~ް3}];p wl摏Ps榶9 [7'~|CEu.$f[&(q:*j]т2m배͞ Y]iC-Fh(sڶ#6Ggh(o9؃룀\5s{ʆfu +쐬53킺~8=-zA[;v&Y/3 ^TrYߕbq 6te@fP梢-aY Nٴ+w]4?!ჳ+wNRt{EVd~._ 5 $;?`F!eÝ&@s4mLW KS!*}nHVޮ{Џ|Շ&g'D'팑x mχm k`w۟ኖG^-O倇G>:άPy;MCgJh괅PBm5mزPۃ;+*e :wBhJer(71FO܂65]v2pJZQ_QT;S>N&Gbo'WtM KkvxށUg/vmÑ;Qv^U\6 + [US!NTiMכ u)n10({;i׌)v,+9}_'Ww%ugb7L)5U %ي=YKɻ0J<iwfSO'?p><};aMڲӡcu²?ƇwԄ]3 cjLXU4ć{K.G "_tܾ kmk$+o ﶜG{$eA|ԙ7yPuKUWk}CWͯV OM /?MoFFomX@_B,ήS[S6?ⷌɟFLpؘ,+uMCHM1񓯫nYU83X}(x'ޏùĨtѕ{8M"Zcu4w,iڒ$巶-cI:49h<Lٿ +_:.[^,(oA):; !ag ݨCpTUenz+9?.4?lK/]@[ZDں66^v͹!ˏ.*xEJڷM=$_ӛOގ%=$yv'U9z˷6v}g2v:B)@HB -qCfZog[?4$:c%zC٘?$2Y/I?;QK+;^ qewW_eښ{zH\~Su`ۏNDuB,w?433bv!*;)?W }ewT0\xs 8}2B[5Y )k G3nͻXdV&~;Ia^C/vCE\\ac``Ӥqv?~wfwIV1DCNsXl2t$ہ{ /-w]4Vo.aV58Êd;[-]}%54D~1^OiZW:̲DeE0$:R~5:1 J>xD j-ǥ]ʼ`- hHX}yEZe*M#_f^„4]U ;ܿƣ,0*MÕO3C{ !7>@ŽWXϛ5N1%'[.Nm/Z`Fph1bM"$:Mx< ͹ԖrslWŨ-N%  [YW#L'.9elm,'ƑUkz wŶ˪e-uolwQ}he}.](>2+؞Bk/>P*8م۽44!`2^z)E@B)&fC CAؽ[}VΙnLܷ]p42IKG=eQ^]!s/o"V ,6|k8#qPL2 babPY~΀u3?a"^g+Rήw>w^]y}P0_l'ko!35A,uG]c5|>9䧽3U)5הЧa1FԔВnkoC/ x +5_?%\f dw?\pk#VH8I˚h (>)> J9;FX4?02.ƥiT +=gih«sij.9psʛߍÕap<&eo +CH^δqE7!7:['l@yT!ۜmG+t} +u/P6f_8\|ϔo3~/Ykl"XPe ^?cVeH: \ȓ?RO_}Km61%g಴&懎ɚot,,NHOEr [N-'$m_RD}ۅz1O[žf~?+;DK J.KJy,_Z7l"r98@xĀ 7"߽Vp`p =4VҙzJźSwb>cѾ?5[voUF/*~7ӂKWW%5i^\T\a$}OU#6tBXps{j}y8&:FLM1Mt4,zrQ1znTVo%wLC!ҚHm=Mѥlܯ}ȧ?.6$D+%^\߃}n\$xueⱮ=R{^qF2KƆR0X C_o'(Z}0gV:*xy?ZWmŷ//_޴JF7һ*]0O+|m=zd5$ic9~i@?6 a"wY[,uCvb1!z?i +>vÞL_cq n߀ s +ACgy I)٧䂴Sn)W.J 8̑dωAٔDy4h\2vfgCÇ$t/s>RVJZfPE֫g7L"n!4\3KZ=)FOƨaEYA>El)p3ͅF`tR%5nPكo!nsTtMB !4M`r:tzwᵕwOy>B .='l-L1"]D4kKQߙRsiCom8"ůⓕ=Oېq? K5q2f䊾bWQ+JET%^;u|,gk~.#٤[\MdvK=4 pR0(>-iI9\ǁ^v{!beQrSd~04n3GLIŖN0-#b #WU\\W&Vǟ`=j;C_RqBe;#}q\E‡)ZJ /Co" 11fmQ|[^t9Gͳ鹆QLhQ@!N VB#AbV?B[w$U? +?v]vuY]ox ?y%$OKQӼ z(yc5euſHi9ba VzTc:⥞BLȑ(@C#Q瘫AwaemUD/7ޑ@FKo (y +cLuZ/4t}'ȵ>99'독_^}r>I HITW- JJCOԴg]0M=/m.h=7W܂_nx# f_Yn6[}Wz&];d2@xqκ2rG>#0y9KDIBK*&ћ}Oniu[IRED%2IN1^):h8Zfc  qFOJ.j(3#ځXS:vHU7aunJ,θa>K}pD!a77zӴhn92f#gy.iyNMƅm ,3d<+[&QQ+.ϻghYn}9?T) +fM ;񻍷#u*&&p-D]9>O/Y=tt. "nx?ib&}Q7LK[B$ uH)V:-L0cB3HJ x(}Tli;eo$&32i?5O3#O>MQr5_lg@n?R|c,wD,._!Z2攡v+D< =-Ⱥl Zp%g%)^fޗ4]u^ke>p]፝^xWƻ*kxyO僷ؘEKin &LVpy.۲{ Ox,y!jʽ$i+uH;ȝ5'ѿ.ʺz!r(iAjUK6 bvz/D-i8Y~e$bV{Izoc3)!ӭbVt%)ע"VX$^X`n TŬpD;Tl+7VԸ[,Z.iS)ӁeuU W=]~{`3xlJYמ~fW8T|qnoW?4״%"KA*L*pAh +3r]"f1I949u- qZ1Y\𫾳.G+,|ۃz1 +; \,&2|l?镑2!B$/ ׵me}m1aDkg&&=3~X|wIcWaȇ+ۚւ>%bo=J t)Q}Q3,jy>L{|]R琶6Սϛ55>18 {e&Mwyb>X JGЍf򚈝1 eSXw!} !ODaLaր*@~p8•.ȭAxǭZkC~9&C_{}%WLzg]?̲͂Eg>.XE ioWԑW7ֻ/l^Z)jzhGJ4xY- Di[O)'QaU$32dm_"~S/wN!bl7:Vgҋ,qZr[K{Wpe:+=mjҧTd:zwRGie]s,IҶ[+=,5)d +tp3cPG3FȎSaLq/a*D QwBaܚsħn/Prs40ςiPCm< 33\.ᒀܶ'yB\euu0uT{ewsc-u2j?yY/Xf>[|qM*m7Mf *i)S U1Y\q9sH)u! )Jʣ`Ye>kڇ + R.v(Y65 n_I|YWe{nzr] + XiEѿа jewK iһ BYƯ1~YE1|k7L m~(h9+O +r_-$Soh~ҋOu%4:NL_z[qD]dza{sO #qQ[a9\r:ܿX ]yz-q j#=2RK*7 \"Va}Wŵ)7&_@D;U˵<dd"'+ )~(?VdGI. +8$pj>azOݔqJݐ>zG%SV{dy5O8EYPcGYJe_!xY Op# i\f_э ʰU! degA ZE:,%).(N9xҡ YRNp`K]4@bR>ag 'ak<:Õ)JzsqQvGސ~?'(Q?n 6ّq%-Wߣ4\Gͥ\[EYR}# +CQpubv~1ފ_23y8p [-Cv['}k|YN-ܝviC+;2}F.7L"wGn-rUӰrfS|j|uS#4&qiNUp&fitw]T̯d5J >p|}m +_ɿܑyc1//<VCJ/ ki̲;@Ui1̾iٙb5`wFiIk]; -٧gJxIg|Bpw< Zn2 #!J֔{(8ٿ1)5og5BϲIjPKUg_-濔8\-*q}0\nSr=;Fgޤ9Pt)ldnG0FI1r[A.2Z6"r&0X6#4-קi2Jgc8VbR6xO ػfs؍ V[ 66sG 1nֲOӨ1.ݥd@zaJC$bL`_P +=Rrcog/ъꦆygR/5a= ZN6j}@GGGyY>p+KD MF*XVM}`Ͳ)9V%إ0_K h=7Ʃ ˜WZ` j2C,"J;WՆ~0(9U6< +׶e[nK`.syȍaVF*'ra zw_M$[9 zaUfth??5ň?zˮ`z񑃬Ud_+d\5li3l^L|SRlbL_":DS,X' . Ȼl͹h?3csEpY#S 1M3_ea-閲˽*.pouG+ۜ@vЅ;61X`[?o4uP|R7HKIvء6G nxp+~u9qZÍ퀖V߆u6ѯW|>1~% dm +<Լ\Yz^`c=,SM),Q̿&;6$CEZ&~]s]2hIdjO>o7g,$ZD1r$):||p0Zv]oqY$er+;$T)[}y7zӮf!9zKʯKj= Xac,)R~"EYX.%e&%y\MDJL"AZxp2Uv-#gx a )"pSJWM>(uؘfr]\b^2+=J:tc~ăzkjhe.ѫgXgpiǫ<֏kukCxiK]G p1)Llm'p\}'k2]k*B?lr'<9ˀ]o:=a(ksa;(+}JP5q#gRI@-_Q=kMWGp>-yFk +?%ǣbU|5elF/Jgѥ7,"|CN%{B @Hper +ԡa@/p7F.u2Եq>X֡ H)t(@yx\r,bK*+[euكʂs)F#kM9a7ğLVq0#Fwp%l S}\Fy?FMŶy..&g9OE}߇xuj`AP3&89Z$>Wq;O^LiI>ɸqkŷH <ё> ).&,:ԑr6xw`.#E96xp^UvGč9KjzILLt34YXlby`1<ƽ1s q睳5ÅZ8eOvX:b/cI4`֦~z5uՍ 1Pq0ݝޜK3.;1.x4MJ֟}ħgKoS硼 VEm{k6s,sDSӰQxc#;35f>ޣbr&%!eiH':*n_Ąt@g7usV!]76CxTHQ&lsrVQ#ukR*(_| ]~W|<ʰY4>$hW_jlv0gB#ҏ5_ Mfhm4j_}^Ufa{ g4 TxisOza>oj,^iem"XML5^ا NKJu8H_eUX̂R -~3,r*6rchvcwu0)&ŭuរq(ذ%xT pic&{ð`zh*됢=:r*] +02zes+7qfoa#q\ǥMF+G&H[K2O;0\_J-8`3t,5㲀nW?̀5a=)d4`*xU+pqI.qui-!ZOޡ†A/YENQU VO٭ciÿ dXiKZ*6H=-kɾUrgvX2Cq@,1PA%*RZKbSXfhdi$:ײNa!ۓBUuUN<]`i6O)ϵ׺ DœΔW +gMțxQ; ^ fyU\Esu9d7& a>E`ʛ-CijO'`t5>p,2P)5JC[u*ih,2үFe,7ص͍)Z_{',4{ڙ0&u\.g}Y +z*wGJAvL?Z!UcʰUF_Q.2 *$.M-ծ-2|o3RXxge q3 B0*#g]1N̓B 8H7M^-9ϩ`w!Raꏒ5N03Vr+=s؝aꐽ1"P(ɡ3\ۗu<k +/ybuV2!Fy?s8 +Vϱ0E:H:>D^c +RE<Ғ + t OUvy#>9^l4sy\¡Yu%^+kL/rP_vjMs\key{,WKe_wBX2+/Ugqڟ?+QrFC.zÂ:UlI/Ь2`[fA'ٹ[Ôd$9`Y9D|9Pb9UBSC%JT4Ze4*<;X﮸{ ~8J +R|KBwQ@ y4A˾㶀JG0 8Q}rc= CF?sJ:^a5=ب{vWdC-h]usjCC@'Iz?AI0S 5ƒgC«N).&%9ܓ%틾g}Cs@ǩv+Š +\FEHIU 1GLqJY@,"jڑZeW6y-)o bj5zhE%\{YCBJ|^j & UmWqə .m-$dIv9P<lgڏ|1y8 r=?xR +N]*S3-rMJņYi|BoϹh+e?E\Vk8Sj?^& wO@#l+JjQp-2vI«)I8kzP%RaYfb/ 8qbC|m:rZzi7I;dV̼{LeW"}JL??jc ֊a%Љ)C[`#R^ucAEϿ;\vpqTy=኿nr('`L[ƭpHyf1&Y+<|"cl/oF% WFy VIGId>ag `^]x_r9xp_)ZB?P\u'8){d37i=a,+0[UAҠqtU€U5XJCqU9{n[wUjr!=. =h-֣ͳ%QL-x-lU`3lW΅T*7?^>O<2 |('.CʟjhYbE_vEEug @txåƆv)XђEeЛ.۟$[g%@5L}oU14#{X2X&*6Hǟ,sp_6/[၄^5HJ}~1Nb#JZ[|Z󁇵8(V]i~{XBTLo K GkM9Z6h`eG.`Rl" "B@z_cc>nxpv6} d}^O/ܝ/S+*~->n8s-Ww'*鈯_4OB\ cn/#cZj鑎ZUa %7ld [d/ie6*2[yVyON +x6uu]Rv:}xMhW!@ )fS΂X~@WyFZduR|F{,y)nW-Zy׏53qiibU-ߓ 6'k \GƂYfoo B:559j&ma&LU7mt^31z{|4/8#-@Aͱ܀1"F[.$D\Vǥ!"PO gn)W + qbFGM*0.*"3F[Tga7zs84Q`!~\SgņbKR+kS[-DjacVi~5&>N\^2F>?EIo= rpGEGYl[uy6-ߥc{"Jq EͣJb[A/pG/'כ^^o&Z3 +n_Am +9\э0:å;|RǔgMu'k /\ZFwѵYV5g/RnڠRPvy,)kgԝu{")Z|c!gSo Ac?9E*9Zt_Y|5ɿk +|ߊhܩyd]p ~5< aDE 19G=YPC)%g:X+{̒C Uhq+%^ jQ +}*> 2CnN`k:K:Y':4P]8\$xnUu\ fLMByH*i+y*x qNe=ݣ%zT|[r-`sgJKẃ >\5َ긝8z?i l4geJcg!'a| +!ɣjyaFay^\>ٯC쳼WSշtȩՖ+u.q1^m=^RWFxU;A?lzұPs뚛]j6)!g9Sҡ)Z-9WSmdl ^44T=9%.1# PR5q19@ly/:< 'w1v&M"bUM9YyϠoWZ/y՜j%N^zZxQXe:+B;^eS:6<ֵ8|| 2E3M#fȥ>C*4C$ɱnmkMFl+?qq]"YLaQʃѭ Gmb v]m uVR?R*/F.a{nUH哀y! +VhȺYXKpK11~%HF@=5>dE@kT2Kr!ڥld曨*a@2FW( hو#κ<~o]c)4(9UacYy RKF&0yzp"r*`W8XOn%ȧe;%OM}tÅV)p7@},ޚ>~/Vkt9][*BGʧTR61#ZdWi%7'~k$'KoMƙĝxty{Wl"Ep۷Rr^eIr+fiپ:;A\k~9:V bzS^KEk +&UYsKNV5} hkPvx :4INr(Axu  ~q&aE)y-jh; |Z! |B]G}M_v.n%VMui( fdU&ꋞEJ{#w踯[m.xsV1x $uG- %|d}V׆|z@}:ѿHڟB" N#ֻs/i_ǟ)>Zed?ӏ:.JJCS,[^Wlv]/kM˖hU|~9 g@rrsq `6OE_7쯛?{o%G +3{vy 3t Rak!BjUWWwzVuu$lc{̀gЊ6$H}_|Ž7"32jjQAȸ7n=n^y~Dmp>k]zx.·ruX-7;aż~sO?|\so]&<'/g˦q^,/_iL;s웏=yr~xG;ï}M_Z. 6>\8e7Lk(0{z3 Ėb.ɺ_+sn{iC =r78R䖡{lYwձvZ~zhځg{oOsvc#>s<tsi;^ܶ_;5}| 9؆Gm>}wOȆC'n^,;>yatϬ߿C/-1NXS>s)!{ο7ix΁}ݟ/ˬcfqq>;v}]_ɇ +[Ėg~u/g e㹓ƺ x/ +/0yϋjÚd]}!d襷O>omO^-/<)a{dS㓻ѡ-+r٧/܉M}u g;yd~wG-Ԧy9C#:z'/h=CĬ?z<tm/sa׊G6.s˽{^yL|8wNo{ÛWY_.~z}g 9"얏BoGҽ[/3Cu^gEMݷ^?ȟß|#o>2ۧ;V% ӻWm)-hm>bB݃/]m7 tBGvɻЍo}\tMnڲzg{fl׽|Ƿ7=S ?o 3 +O(uwZ:s Kow'ڟ;GQ#l{rkߜc;Jwm>xg/w>ڮ|Pzoyd}k<}e٭#x+_rŅevv4yhýhЧtfϿ?[Ů Ǘ_ē'֏?aԆg֯_~q߼񍅻/\|l] 7q?҂μwŝ+kG|'G| x[2xǑM+HIȥ3ǻn7zpgwbs~sBث|=ڪ'?c㧶>/-l oxc.+ٶbۿ^f\;_߿yӹ[΀qF͇_)t1;u|k>&|'}#Cw~=6諥G_~3O.5m^x|O.uNm;qgfg_--c]_u?'6qn^zsE޺sr/zS;?#wM'^>Kz?y҂})䖮[7㵋M}}a{i`7yO{Ϲݥ!ayiuvS'v3Q<+Kn*̽<.lŇ/ZvE7 so>'\|>CB0kC^:{|_=dܸb[-_ؼsa9.:|>%+}J:[?G`+#N6u;3sOGf~ミ-^~1wl:y;;:ZcWX?&܏ۛ|Ѵ/¹1! %y.ߵr䦾;>/͏{yMϱ _yAO>!|k/8޿]>'ZY}~Ki:?yVuלQ\trwF>؆s;_oOo=kV}G.svpg |\Big܃/ }\B~uFm{|{w7+O;!<ԎW_}w48×ޥ}i; ޜ˅SVNy釾}rX~- y9]wV/w y1x-/v_{~̎e?yl['U~Go[Kxoc{#wxz +\3>7}[_u?X;Ղ9k|?ԉ} 74h6srhw o,~N_ظn_=}_`yw\:ч.,-9m;8#w׻o>~wύ?k.z{hƿg .l]_oyqՌ?=JF\ sкR#dךS®>־_v[?ɭK}w{h}7|*UY9|վY^_{iv?2vhˊw?֓p|{{WeGJ؏N9sw|ܱWz/1~?6Wtms.(Տ_k6|klm0]>KŻwf_RvXSb +\7ṽ}.ث[?,-ݳv?-|r/?{W!rB~kvzt _vޟGkf,0bke'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~RD'E~n{2-Ӧ63+[^{͢;fڌ;yܗ-K^su8TUȕ liU&x_>;Έ2sJ°egngȘhյ״[6=?X3\s fe)k<0϶ I>5OV;}\ql57]p]T=b5^s,]]Z,͖C +wS[* }mm>3/X7Ӡqmn ~%q{=y=ZWV l,Q\6l\a'X;?ʫFhկ3]qp!'}'dzcUW~r8;D58B+Gheqg׌lY5bd(C©ؽ<+, 9,NjijW/Rp6X?_/Wz)(M>~Cuo4;\W.sQg_ᑗjdžZPߩri7xHvgG-Ws5kB, O"bX55#R\,UYF7XGqh8Z(״ɠ[cџs5ڒo fOuVk +#\~(?\ijVlfd2_ȯUE5&:*e $²#PW QFDv$:lկxXZ/p>[fkިՋO ` .b{^(%UmOO\ןP t.װlEީUk \놻UkHwuS}#SCDzNRv0T᫚H4l%*r[TjMiʥZ֦IUTTC_-]jmڥ\.ՀZ֨]jA=W 5MRukYv%KkڥvIt$KiRs[TjMHR- $]ʵnR 5 +RhXU.t-(tyJ{5|ibwv¶S]5ZkKi?i?q44C*ȥѪ`qU,m9[klQY}d;6( {Qxn~Oja+y5?y^XJrf5S"F^j5LorQ4Xi"Jta0始.Rgz,ϘӘcsLc5 00SMՅ$GDz^T\~PJW +w}Bb>,uJ{70pZ%d&vuĘFG9ac2 q-uCz&]{(8f󧑨4FB܉"Q2,4FHTJ#Qi$*D}HT,zgǯ<鴵Bm*f=1<5G)3]z6&iq-ZlD^[Фrᱡ9rvwꅢ^}Khp?Z0-e)j8CWiȚַҶY&kVfWSX-[ell5(Öv54 -jgiS +ni4UK[} cA!ћ:Z$A}ӝ }Ocqrbł5 4隤B%F+ ւUU.x?/jq?U6MZ5ӚV*OS4OS4O~z(OSKO0Ҽi86{y::ڰjr.jM(5zWְS=Tj-]Ujk_xTÖ\ܛi|"V#6n1ii89XLJk{LԖ+K7ufs3*dsj W L׋5xadͼj\&_YV'ljm MDbV-qD-!f'hB/~}88T8*g koF^'$g=wdžs5jw3F&ۛT$ +#l!Ϡ+RvxrpwLɦjqҠO}h O>WX2,18(h"K_r2 0ݪ7mƝupOp8)`ײ6 + r586UCAKB_q5FfwFkU5h.rؙٙKLxo}^XvYoz! į&~1mXjEpdfs w2F8&s@lv״gvn icF;6a0Lgq3m]Cb_v S@v  z~>괛gڬvմ-vX. dd|szM j:0eY0+n-1Z$6^f:$7mHi)[1v_]A!e.tl!|PLӑ0!F $2X @0ngvKm+@0uuʈ?LAb\YݰMbt7gDoAL]mbxk;&|E4HN8[. 9n>,m  oyX; ];0eX.< uT3@C*Sm|@[/ 0˴/ l&JLE,/ Me{( f 1| yuEK,`[}eh/v6$fOYq-  hv1Auܶ2VT/X4- #h1ӢkM$ϰ 4 1G2LH0 qڛ/1?&vIˀ8G=u Wa){DIp=)46M1@ +8lB 8 C̵F *6"-( #6E$؂y6C qz.K)vPؒA296~-@20|l/mtLF ǯ l*^et3 6%/q'ڎP`" HoPn"c`&`DpG4̌!1a_Zwcӿ2|3F\&8g v;,O& +JO/6F|WhwQ*a'#%$ۺ l-cn-,B "0V#A.I$1c4;wlӅ= dsȎ #/@ +rEA`'$b/i,T2\p<4*|ZBQ Dl]59 BpZ!maW2 &CY&czZ{aM:ha@+>X@XO=ak؈e?@ƞ ;* 81 /7VQReI /gt%L7r4O#N X.G+ Z$Ah/ + KK,(y(&PȘ#+[2 c0G Qc ([2*Q7eŵ hh1||D.oYhS"WWX͈P&i!AaaF܃wDŽ$)C,lGR0F\fs`)Wտ'ŷ|_|4bp3{X.xJ0 Y@ "# ]JzYV mEho +aVbp?$KI(9L>*8^JW~f_Q, V +20EhZ1C )*`6R E# lA è|\OV b2n8i@`ZY\~@ESX +ym'Wa1Ld~mG&d0/3\P>Ï?]T#Сo_olAiћ8bȑQA&r_ jXX_A !欌NWU9[8)v9Q@䋿3EO c <$-AI 8u( Ծ { c+=B;&Zd+*`; a!ZA!Q[ ;40M$dˊF+d Ϡ6c1;v>w \@< 7²<WZE!F5 S 5@~7@ .D/ht *hY'lD< c?[\4the33Ɛi@X@@˄H&ÔDw!J\yExf{<"67ÿ*_ {SF`rP MML:ڬ aXkXgi^!gۀMXv(M+ĉ} 2P.XvQh8]AJQ^ ECCmz+㎰?Bx%Q!940dÂ@`4~,6F8pD0"ń(ctIP.Aʒx>Џs( dS*fY|``2w;3 aa#qe=ěWK` DA"e#D(CWPq@ Q0\C_K+ OA #Rq+!<q`s ^ &\e38>';-yZ,Y %'9d$ +XP /h8mX<TODI1t s@"ULq%.̉;+G!h$A;z;6uAqYir0ʥOF(N(Poj>eh0yD ]БHeg(F}lK113|1 :~B6LOE!86B\"6̤p%3ݠp ->D0!XBS 8(dZap;8>Z'93C [h ¤a2BbSh%/YD%AɥpRCs0*D*H2 4mEL b=*)NxdtP"ļ`hX%SiMz)OųKTGD'$S* \ij$H|CEWro`G%*+XCmm1B|YQdT_<X!V1Ѝ#N^ +̻XR(& l?C*qEJ&ɻX,'rZBR0@5 +d!speij #q-jm)Ŋ&-T_q*P߀9H$(%{DTeUќ8p 1fSG^=c~-ݩ8>R G9 b0 ?Y5Qz`t cJJn[bad[ yF8 dU#48rĊ(U)e]ŕn'3ce C@ANae=M1)q%uRr BN&гD{#[?J DZȞb$W5MqS].E!'~5|Jj~J$Vg284cLIђ>1y ,Z~K RÎaQb`Ƞ`PlfbC2g +-i \FBȬr$R&I8SIjXrTenU6혨peK0XBAj'@Bi^/,O&'A PE~]) LlR8LTI@+E$ /jj*w/9p$y$ƈ@ #PKTL19A +ԓ#M>G[ +,1qJkUD`X#XrIň[|ack/1P BĊX͒6%Jbk9v6$.\)6FnN0CD"+X7ptHE92)U^hLPܦQ5m썛Qk !!XG]Ga"e  tjAXn;NOQOԘʡZ'33c: P80ϨpУp)֓X`¨Lb1ppdBbfN6Q%cUVq<^Ю*⋧/뤸y;OSV ܐ+Hu'IO,Pz` ,С كld;QQ-tjՒN <)ۡjђ0}SE\>S/K#i6v:E@z +l@R- сB31LI.:!CcѰ1i5Cró6lt\*K鸢B'[4\ k˥E,D*3ri yLha*21Py`Й: gfr#G<̢.h@uڟZPˤP5*x ՑRMAȸtaiH汰{52%R 4aS^G0a u ܳ!Ēr EWhh05[vH)Kn=5L@plKY4L)iP T4&+<ؑ*8Vw:L6ڃUEdh0GځsG:+}x#l3`SP3| +0w-,KÓ/xE6be #†x 3JuQ;+|ΣI'0;઄E= >tԱ#@:+@΍Et`5ڴ`tOBXKOV::ۑՃ3ݩ +ƹ,j6eOĔ!NA&3{3 +G_`Jgf%ՏCAFFtRF%VfI +FwPo@iܤ3i% R6ffK:.=Yr:p(ݡ칏بN9K%8=0XSY9{l4^ +uj)6*q+8C{a>`&7JS{\5OmSSR]u`J'(aɍHV 6%Bڋ)+ /y0ju +z }HʅihsUڃLֹWL&2k0bn\J_,4_Yfi]7(MuA;.[IcCl9ߓS/ׯ76͹R_ӈ5BHZ6T\+8?k 8Ya$-n[ OF+KXb n0X=p9c@>n733h?&65Pej,b6!Y4LHrl8'4\f7he9NF;cg I騏Ϭ.2Bb;z`8# $F&8pt"?S֙p9nm#@Xd{4# Uf:V lƆ^j^@t*f}k,ȝ64訦}xl0g4bxSRA8֊dw_⯷UH[b.mj؈7T{uHOʀd&/!# +7SI nA:bNdc\=ӑJ| t yP&^;5 k[]h"-rmӺ*R WAi]Sxf:L"%u 1 C_T Қ-VKi]Fƙ`aNB4hR1ܗϕ:8jLyFM| jT :P^,Zs)6j {tm&4ЁW9u $$<X|$@Ԙᐮfӥ +?Ą36gWpxX^1޳ W= 2?|FN0p-[Lj]455䆴K 8 v\2OB?{Q#%9_zV:Tv͗˫N $e_,3/>3VZ చM=al`+}BJ8bIr,q0E_^;p_+ +0,ŋ$ q5x! <[gxobQ~l1 Rz@e@< myz .8  lNdA 6 =jgP 9Gr`pFjO%1coLe. ,Bt8I4gBYH'R*\d>stream +OvBFxÀRTM,km-tzeVO㍽xLoe{Z/59ދurp@ZBrAOJ C!%8.is<#:Bö1^@)Crw9=BHP@@s.W{KR)gAslxL&&<5450N1Iԡ lf9)Li3_>@>/wY3sWE} PSʰW?!BZLDxXNmw%o-7nA;sQ_eh9K Q.ENjmlƇzro!d<Ճ=^(˅CPO-3^p]jo21 5Lx +4D0Т93(WUt O/*M˔֥lEԩhea +{$! IĊUclYi!ʺc1Klɋ\6w85B +Vv <^=R}ѷ鼹:%%"WHŤO $Ħ1UG˦t:]o3XBaOh3]FM>nkǠ$L|58ݬ/XXMg4'4C%ȖBmMաZf+ZQ^%#tQkۖZ)-$Cb7us))7 Z8dг[a,ybgq/A i}yԅnD)"C:0)G aa1vYɗJ.lp\Kl r 2sĥ-Il )"A3-y؊8{ٗ.Zc/arjgbu#$>$(]x4+lل ++ +2|yutH@B 5'Į ]W6Wԉçv 0fv˅%3 d$}DF#`m,1 R~>Dg;(aR0Y]m eֆkD!C֮!x] %k(lj0;4 "2KbsWo3rdP=XKjgR SQY2#A}#(jszFC&z +N0]LR +NYjmUg L J@ 7T]hȜ꫶\ it$IQƵ]3t|#'8/ͅN%hس@A,j0'%b"+ i"Sr@#;Gх dDӥՐI!!0r,>k3{APƃDoQy ˎ<} :I96;( +tttpttB派f̠Q;hfLCS53le\FPυ !b@m Dۄ R1_K(\K]8 +b=2 +7P5)"m@P9m%60~ P@. CJhl`1Hʠ@ T"@bB,KTaҬ6ܸ,!(Tc]P%") B5mEC nK8e-l3x[LFMۍh2Pu +u:FK)Bn13]TX_DBS^ojǭԟ2nLr?btꦉ%4;G6_`9nh) 6yh|;-}h%άg +J 0F77HUgлRû$ @ S 3AϢg?N bRظGrdB~#t_l2v;]ݤږ2BMQƫbeD6U> i](2p0t\[ahO8p6C(PPP +h@H T\pM8A +P_=D,玁tuB79$bBDMbr~0%1W\0U$ R")j °9D%ݳ+Qt҄ЀX>49%#s%xsE +SeLFKإbQ&w#W`1۰Xtנ3\/Bqh jWDErYx5֐P`"({)G:H}$CN1鎭ȗcFҜ6m+Oڇq``%Z%L-٪5lAaj Pا +5*`KPBACMOU :3elAzI& jU DK3L@eɦq!Be >f1 0ͤcUi& A3 +.t$Uɖ7+2A%FLi~*PNY2AKQY&Hfv(Vbݔi&hc+ G*@.t ~r@̭% i +ĎZOL4 OO4iPh"S[g%Uy&CLqrP.2:be\T 70@p"Z[4c{AɖA2aV*HS"H !(̐=~W> p$Bd! /MB%Xizzh h9Q/+.%-G7-m$dH4Ry؃dp8 +kw--@- R p$;Qa4LFi7 +Pur &rdp;*O1=8cq~}RL_I BpJU9m4!Jij &g`a!ś1 MQf={݃{#Fm8 9 p 8-V+RWL5Rg\"+=.C ѽ1dg q("A) r#oK> =0B +qBWɝgZF6#8!i"-ْPBN +Q`Q?a]*X&@UAE +ICuX2ܩh+lv=g(l-A3 0N͑-#"M äˡ A: ChK,#,=Ԋzd,BV cE('1hg%oFfSR 8E#îP +CkܥB@ԫjMO-db)%\ "aaЈA~%VjiG-ȲSz6zj+W_aLׇ#@O;g^Ȁ70AC\79)Xoƅ'_ }s.%i)Z"(%AXy8̘٫&cܒJߜK:O*B\fZTRBȠdr(p \#dV~jFrǮ4ZpCo؃$*NJm]*y{= E$oYs/= E_BC|PdvG=QhFS/IOrKR(S#'6C/ŭ@Xy  ȿ]Rboc!$J8Z&orT5t?PrAꥇ0_K|I C&rKb;͡cBFL^ܔ Y)&BVԑw!y''(9ȒQt]x(ս &r&Bc^<c~a 9Nx&-9^j$]Px=/Y%0+yYk+̐*v -͏zxUo.R2Sbar&p=!Gx&ǛFB`*}H(Nn4:D 5P,bjʓB,:`:F38\!S1# QG$=FOr`pgIdFD"7ᡒ(a na)`L'Bo1:vH֙vbp#A*ƪhJX~PwF䧾jPKMG=W#pDߢO32#>ŒǀdLD5(! !$3$~ lhUT@C 3&qh|3N1qQ /G2CÌU'He=bU-fUe l9F8c_p}ֿoF7f5PϘЏ; ߄!FatԅDEYeEWt+0U%d#FcW'댍4# ehU9bӫ#VA- .VyD_:gy6ZJ0GlD0JOCU۰9)=o(j\n1`HegEEE-.=o 8}|G$G? ~txSJ9=sWv橠ӫ3ӳq}W-ߢ o z 7Atx~p^Ë7+w_[o|ؿT{uTu;o[M}|[=L߮WLW?]op̆07D7Y$Yg[m9-PUeTaX^GcRTcdtF-}^%?wٯ{< ?-'l~CjٯڼmpWm1Xͣ`=s^mvY4*~Hj7ZNK~L7I !,"G l5,4O%Eٿra,x5~_)*×#c0E]6NBE14TϨ/4mԛD_BUOCM*Oxa|eZ"b N 'I?t82KJW-C%T,6=!-ܔ$r%㧸,)8\LJi}pk BGbh206_QBKTKy /o4*A$RPWCQXࢉ%Na G L֣KA}2q5`'(q[iǽ8|Q"mBX Qp=2 $!$H[.'GdH8ex#+n%o)D IV,A$q:*A" +mD}/.aE uG\LiO+IM%EjGDC%LXxQFrD=>1$߼(@ex :$H`VrH4} +Dz& I)DU]$ϔ y`h\C o :2+co8Gj8(!q*yAE8!"0 @IH~% $E$=8 ݪ[%]q.tq04s84?%F@U`.B*!BxgAF\: D 85W"%:B7z܈'F[38СB07RqG!z#AHxW,aE:y%DFo`(uA +a6c'a$UAY-ȗ: +: y:%J,u:)Y%ZMF%AI]*(<]%}< Eg)`KVD,T`Hauh Gcж0t#3DCGHE28V˘lY 8 }; +x#DEƳCtX8*a,92q%}QZKN@B"=@'< .A3f ñn…!q_d9T HH,~IB!ȸ^+ +:~$rݱT MS*KVF(=%L7QX+pgfer<9鍨'#fh2v!Jb1"QD* +HhF*M6F$ i@Q +AbIw.ZQO"O"L=X*pcc4H% Q$9)I@Aͫ2`|TF V"RٗJKCb- U#V*@5G(Tu??yx;$*X4.BLox@⠑[l*{)4{ + =5y. +^1%vX@@,IU*=I+@"<fLTG$XZ$>"«{P9ar)qP B*x)!"42 Uqt9 H)B@HCRy*)ABlYS(6W"88K"_*V9b#W?"&o{XT#yd]-)͞L_ůGY|Ck#$*~*'>kb#5xa^?p#P4  f4U2A0硐X\% yQҍb)܊UX04vQ+ŏXɌ(nQ`0Y0^I}:054YWT0a]lp/D|z (],o_Yc#݂y nEI^Ԁ6~I_bĄWRmC ăt bq&)9ZғNZE4[0KVdB6:Qe"[$!|FY2  *$ +ƌzL_qA-#ԶeV!p$I"lA[[iߥ @ ʆYY ȅ +~<ps63`R_q@d@YMbS"+ v@S'DP +-K +hβB87{ f*=g8VmGoخf Nٳyz0/׏LT L-@\V9]+CJ28"VN" ^7`h҃"܀#rv,8n$Q=Z>a+X$)45TLv +`VbYC!ў s,w0`anJ,T p mxzEkS(" (\]*ض`pv@v;T!נkZxf /,&D1]Q6tLFIH JHK#& "5$7O4xi"N3[Ԟ +F24qN!spBGD,rBDET,` )ƖBH`DA! X>scp#(|`$D<80y"aEF V$'ϰH<50y0\*ׂo$!LBVs.%نS"a8xja)b@ގsn<p*6߀B!>'8,؅TMPAʬ%! /"I#I+eϒSA + Q2TxYa[>ĺҨ:kɚLXZhF, WBB < +>*\*0Â7+gH{čThBuK +< +B¢@CG"no R(cHL[l݁7mJ^3" ) \k@b0Iȩb c1QTDH1dlC?upFP T~+@ zH76caGBtǂS= IB8KHQ ^;*lDy 983j^#v_kj; !8&,)F!O^C;ߗW=yՋ[- r&vrlorkM! +=g~jYBvj7#m죅| d_OgCZV鵈c)Vx_uZЌ-؏W-kj?*yIߤ/{?QIo?D?=&npƨ4ozZCQδla)kiPlAv+ލ,LDД_d⿐c,d7ADbᶙnj]@ Q7_Kzk`9d k.6H^_D8 +bs`X/,~?2[^S$P} {2Y"_N`)h¼y>YƟQ|@c.SI>MFi߂ +{U0g518:~0>oo2: ,pӃA}dlW3_6a02GuK`8/ܴd~+0xj +㢒oPw͵O񦗫_rx7N8u!/O?Jt o?/MWmɐ%F +{b,qIKL,BS`.2Oe&Afi}t%04}TIC$2ſ[2WCBx)o?hLTW7ZoG1 @%aé0.~l%>D1{_M`gQB0oQ*p_LHU>H*Xd*va"%lᤔ;C=-w[Z~$;Y-ip67epB0cUII*'tV<%i-_}DgBx,5m55f\bKv I|Cp9oB9r<|9t7ǽ~𸟟 a^2l ރ(Z -sS*xz!cFmyxl͋2،#X=w~ဪ0* Ǖ +\R]QhlQxh@9וth}4 yQisb\H\yc.iuII\_i+xD!QM`zmfk a;p\O|IbH_N,߾БWR׉~!Dn&K`_dNmFe,:Yr7߰T,!˱sUӚ2(oTNkJЧFM5ߚJ{Nkr-?ɾ&bW5wF;&ӝiOMb\@_ &#oQ"|pGMllLAfB ar% QH8\W?v8+95l`!I6PnT;DU@^5 .>[|-;sHX.KT6~g$7,I h-i-=;dtؗ\>b2|ؾS i=:yP fKد@ +r&_wjԏ:_ їW31 3ș7pݭ!Y.VmW u Rm`187eȎ}u_=-Hw/mW_'p.ׯaw$Af>]TKҫ~kSl~4ZX2x̐&%nH+ +oU7˟VV KIC3S7sG4̩HٯqND3SẎնSKixIh9jh>i |=L9'Zۭ7(hZ*Ы l0/5ƄX4WIf- +HiO%jl;E? c'0=y& :fLl:ur-HkA(8kd~6kCCE࿸Xj@鏆3 l|f8USԯLБu|4Q >v%} 'uBܢ-v. Jz?ߚhGxސTBsM:Pq$yxFfԣJU̐-ck Ua? !8Y?"%C\fbj0k^m@0R4/A~`b蔧+ ~Hϖeɉ$D0 mYx{nf5ρ6ר:gaL="#|,M!HeAΰ#&)p;$üX[^vab]xoE|db'{Dh #jr$`}IrWuqt1Fj?\'|x~J_TSX^a\NNjۆ ;lK_@_֋O'^H\;3\ARքQL fE?65d]ǞVC*:vJo!2nwChEHN=8(c솫%2oc&ae-G+nFy~'Crf`#砿hݸ)"]B_| f@AuvI4mwsJ; +F4h8ͭ+>63ļ>Ń́ E1Foe;iZroJTk@@;D+Bxu`_{[s| +α-!_kf&FڌVPQo#2o>o>;;: dM 37w"o:ձkohr8TĈD=;[^ĝsּkT>|-Tem0?_^;q M~G9;s"ggWֳ3Gʊ4>dm?bE]k&x6g3(ōcF|Di䧤uD_[s'j[O4LH>xnx7owr=} k+}Ee/2]ƴccwAZO +cՔ2XO\"U^&N8F"`\>Dt5ic[p 4_^ƭ%F_\٧~a>2d]x*j^ketMru1dݾ.ٴp\zaֈcVCϮGнUԀo\*&.1Aظ:ڊbt6Qs1nf!~x_@sšanoۍt[<'# :3qp84B!v$}}LR}3.* +%D#yUA*vӶ\ nbwfV-h9^ #p~ +(ŧqSJ>LeG+;UrHӵ{ {]%4L}~skzr6R9CD+Ck7ݣxgND۸fW=qMe_?v#%:]Cn.0$\ hZ^ $׼nMgi,(Ϭ,0+-O@1\=R;=d u{>':H꾱,92&0Kq_U 嗵̺nikN6pjzi(]a{iKM<7'.;/<.;8ڞ*̦.1])]16D8yYWj2wн֮hS񱧝yZѻ-; V+7}N2mv) ͭ谕@:{G^ +ʣlr(+TvA^}ZJ|/M%}ͿˆrYֹt/^Wσ](r&~/Jllθ"- Yrmkńm<. +}Pv.U]t&5nW)*:$"nv׹6!YQk""h=fE <ƂM)tQDY-;[cޫY6]=FETIKD]_$&ҏSQӞf3͇UQ2Zbl3z)Y&|PMo&?QܧR;YpߴӷՄdp}3R7&00B67RE1()}*OH`VpO݃?s^%y睒Yd#n,^켬:GsDü eq~mù3^O6nFOy]G^vX'r}FrהJXuh̩e.gWaϫ݅Ubg^=>sf]'x&sYrSSvК}ۄ_^!͈.kq.v/>)a @]fW0-Gw6l}խq#/g` أZ2*Jv~џߥR+19޷&}JA4LaJQ.SnWy~AlϞQw1Ma?4y&YLj<^nsϞ2l= ْW<-0ܝo?B)buٔ4r [[ y~^O>]jZP:k{iXᆰO1qL_͇ymVd\~i_v7֩>TWv;N"ٍ+{s^oJMwS7I2<.=erSH8̊)h;yh~NW狗di_tu-^[O^pcohgn6LzO]FA}R)%{d6uvޔU[6ˑ 'FW/zQp9*ۋw&wxfHU-lNe+USrEZ*GTsS&żLr˔WH'\ ,2S_P\ Lv;竳&U 3]`QΛ%۲5P[LgoBx$^J];xGА5/J=.Qﻉ/++WY +3ω>{g®.%q伊ނ[;/ЙmË-&bCq5P"|E6ؗJmKdytm{UjY$!>UQk}VD!W#hmT]6Zl8G~{A½|ܫL|\=la/Om_M7lo_ ]lXN94Ln[:2DŽPv)e :{W,* b?x^/M[egbh^ Mr\&TrJ7`kInq^c $owt) &7N>^͇ {^WZU2'-I:YxNBv饯ٳt ?xLZphnvJW5}JsuZ;‰/N_ׂxgy&]RU8֝@|=׫{q5PAplo\hj~_ sRӨ5u."]9iXqorFZiΆaVkRh#Y7~ +3]vVfm<Dfzt6H55>[븵*G3P{ܧ:Y5˺s0ˣaRf~x>y;EtTJr{-Sq6|qx!3qOr4X;J->UAv7@̔D׶imk][zjaҼHw܎=מ2ov]vVl `Y3b,rs'Q +Xm7t_ˬts<U5~e778/.d9Q 3\wnݍxuBr R>,SU,=y걒*׭ʉ_O_GY^HsU.O0ȩ'.?AzZ`(HԖgZ&3^ϴVnKRT+e d6RYoƦť= Wq^;\18oK-ӏaL"bvl|ppfBF*'z +]ܖUMJms*z"rzDx n^i&fsO-ۼf3[V, hk.i{*΋ԱQ%hsU\=­ +MB!ܕ :U]y~YӲL:TUVވ21.<SSs9GSYoo# -Q*\{8sw +M|Q|;9%[;y-֝R .먻h+}I \-lmiZ v6Td/JvuCu0v\C]KٻU*}j[W}N9hb剗7w i_czYQu^|{opsŻXn[}!~fϕ{"ߝe[8{h]fضVpL]JUμ4Wȏ7b&H݅A\s>* +E]N/cEy`uYL=e;$\r8+$o]..|IўH'eEcW>H$Q㐮..r,rTdPKU#v m?}wv eJx7KwҦHzt7.Z/*FΚ{YVX&XHWi擗bzUKBb^3Bp~^L!W֓鼫(T(39? 政 #5̈́F3(\2R$V; u:µ;N>>1zq5zϢpBMߴj{^d<+bOJ'㾽cU; Ƶx1zb rOy]n&?-j2bFetrT{X)4l6S/뗭#'j330WYnp3nTYXu=5f|fO)&fF1RRWyh!=dki]7T_-8oӉT땵ܵ @+}{^Ĺͱm%)}˖K4{_`K<'R?v~ή*g݆efu5.v3~4kźhc WY!s:]bzVS4%#1囫ֳ#R++GReOz/.u4v;O˩(dG(GkMwgbӕ fGKLJ<0ڀϳkwD- T> +#{!UOS.KklhZxެܶCw@3L:]I[ K1m{z%[Bj۪+.l=9۽;?6o id)Rp7sX缗jeårJvȬYL6ANUS{u.j=tB;Pֈ{[E˪>^lneǟ%3mvsoYVHr'ݘƧk.ZrjDmif2ϣk]hHBwE*$Kf^1iv7.nի] _ ;t2]r?fzO4z7M.(\!\A:=黶d>Ky8urU!z#Y6mVnɮ[֖*@;?{j3^/ectZV"iD7:v˕_ã6ʹ?Kl{U7^dIfdX{m/f\]_KnF0_<,mH.w7IF;W4͞Sj6ޥ#ۋXM2ΒT9w&J#Wy|FͫݬzJ7Jg!4&*Z}1UOdjsLo;J9}E]Ż2dXc.ҷשVdԻ+9\*@"q{S:2u>bdʳNT{>~'JfB}T qc%aeOnAP'<e-|-(FTDL919{G1Ŭ9WB*AKx\HCpWO#v[][>*'%ibR%sVkmK 4xT{l"hđwL(>=a)aA{$DlT?g@pݴ^ +f").Mu)_{ӪVq +W{Q~{`ϖDZN{A.a"/w{AU2VC\a&V"ޝp?J?4hx6;BBUƵQc~H6٫h| }esʾDžCm4^b +0fӶJHB.r o@As%`) G =%:}LgD`XX}sD5F{ߡTѓp5=C+[c<(5Äl6$\YSy-T\lbCQh.*v!kK[4n+;)rPm`%Jft*PbB]Lq#>?3*ۦAp`BZ ^J! + -mWM޺dwfLW&d u'We`&k,<=[M]lGRDX|)"]j+ח9JX+ Cծ5ۗ7,=Kq 9#$g7<ܥJ7ˏ&JI'c"W!"y]!+[U .eJzp*|4lf_5Ѷ~~0V=~h~> fd;ީ.eA/jֿ͋(<&KJ9*}(Q6#ji:6|xPf)QM_{K#f!d!:n?ә;/60Š^Fs6Trkx&$f*?BޥOdni"#37<{.Ň_7w\VۂH; [InȶU0'\_UM?ΡD$֒\BMyk vGhuN;)hT'N-Q;Gz/q UEW/}qa*R7KP. @VTkMgaMWƝd/y8!<&^X{DW:Vi/=R2k(kOI|)PT<+Q0g$( S啥90ex|2IET#pߴxT/Y;?]s+E_[`MQ?Lhʭ]-hnBZ:y*(gf?ԭ(H̭LGECܨZ |o\m8^O7|qGw]`êH ̈́x,̽ 𮃨?feac˘3(x4YB帙9"cNJE -y?M\j3+tw^S蕔O`J[哐V!f04},w|nfCdF +?bgpX6my/GZeuDˆaΩ+HCkӮoV$K(ڥm~Oz{sج)ۻ ! 0Iqf^ֺ:9׻S*$k!t͂U׈ۚB`Ξ8ݒ" +%fee1\ޮAB+}ȮSQJ\iPv lwwN%x6:ҽ:G۲VύWnᐺFw6H-(LA]N}!PһyNVzT[nJ.[^݅rWuyUi&5KQ+[/^S?< 3j?$PBJIE9f0M92`Be?/Mz T\}QP%/ref~htֻKjq Y#kJ:xY7lXF.7'HG2;a{_֌ϴ۾DTO;s.JY6j̗% ݯ=N0ժY\ t2bw]uR&L;l~$G2&CRU 3aͱW&ιYzi$K0M'hg?QJ|f9cRER(rٚ +֝Ac-ېy맄#\DjzX(c :WVzxq{KGm#lĥ_!//̈gaAnoQic'7^v Zb79:`w6 +TTc>Jm݃v.?sʫ}D0 m6`w3`'Vc_h9a ЙKp'D=ϴi,dDN+j 'UtceW6|]v~rAnL5#,]Rϯ~wwuXXW+TnuZpgсEzMuu2e`s +7-ȇ}bWA 7hPI.?Go 1CBb~هn'̸b2: E6euȍH jwz mr,S9S]F5&"ٗ +qSc44r0?;ݵ%2>:\&~y'/0 ch"hwP_:r=٭9#镬7wԣ P7 0"8GCF)-|ܨ?WքU{[hsp3i56Wr[hDV.>Im1QE.U4Q#rc2iuñV +b2XKscw|eA 0FfeZDr&muЋe`-#t說j/g6&Z_!㞐3id JuV3o\h\tמXס[MWƜbBnڭ.k k^4qn%Z6ҁy{zWj:ɼW!۩k3PQ Llst##$aSso(tt|m-y| ޖǿaYL.W {5qx{g#lfnkvfr.. +cWXP F5Q})|SJtR3P-BHpzU,/yL8O2;'eySAwbyYi➟i,eZ㙯7n;+asÒx@t@lavrZ5D`@7aKxg/㍥ +.^Tysܚ0 mżq~~R0?n٘}eVX8>z]ѽO$g\Glw\Z~rͯoUK#9oV pwn@ޕ_T!] :3r; xIDXf`Z?%Oinŋmm]T~ertr]?r kϕ%EYV6B'v*'՟ae8./ʮxˆܜD8'mg>j 7GΗiPMᤍ~0pfO nP[_IlI -zv-xVfU[Vrk^+*=*J,'"+ܐji3RZWa05@:zZBIX8FWizvkX<틍#[VrWCt(IFWWDzT(\LݳTX_XR#R +ui|<u775J?;{'/B/prD08k(*_ne|rBp 2CLN54g8S|QrEН9u2#ZO)M.GR1g8cI%lhuX`O3ŖCI.-Zr 篁>ƅ;ݚVS+%dٞ}D }Nd+?b>3V֌SsI" ږKX.{a/zZDhUN5t +CɪU.Zzn3} N]}62mlNor% TzHU? n$kn08N,R{oɪVyDnT}Վ2 5嚂.֒@ $?axmoF[|& +8ȞW؝jyHg?# 3Nv2WH99WDX>Z{ w“.Ă9]\`ayRGE?mb3 v<qwSيnڗ)c ++ /9[h(MZhAr;c&#^\Oɥa~crLZ̦E[f}|r奲)] G{ĂbD횲/'҈|.3,Oko6(`klk-Ls+,l +'Vԛfi|Dq *yTu4Eՠ=ӿAK<6f#fҿ8ؿ~֜*Lazu!QgV0lqq0Xlt+{5sX+H̦fbv)\=znbb7%^P19MXIӚ+㲳,W7_ 5GXj5;oT>D_Gw?*QtrGr#Vs ooz +>לa1MduƝNSf^ׅ:uޟ& u9|h}lY ӟIh 7yNϣeUP8ـ )-Ro< J%===i`5(&;_̰gs.+XtZrzz|+56*#?ٞ7B8#wEj"kTn9f"WoEk ^[XRGԍrݫ"/bP0:oG3*,Wjͭ@%EvJb)bcِ)!fX|ӤR=5k٧kF|m1Q) 65yI2EG(Wh5JZK/Lڼ`&i +O?t8%k +][{ڑL{sj3o\v<:=Pnzې_`|+G)jErcCwrL|Ӆln`&pѢWqíZ]в bWѮL :'^#h׵ei΀[(jUQFN9vV:C&}i=yq}Ln;f<Rѳc9);Re\Hc&[ ps+= ƢEJ:*FY˫~"i8ݽ^!]*7_-fR|_?&y޴ kvjaǛ69SZ@Mx%\+3IydfRKqe_+HM>YbvR6PM7zN\<|jF{hJɐIJbR>0'덵 +yYp8TD6&nÈI, SC5쁇Hԣ6^%~k$zps6/68&ZW^0 B0 }z]󒦀1vvnkT8wć+:}!f]1B'Nl{` +MC#|kc8Mq+x8hs]T <>T,; +(]Ũ$enm饞0.bf/N3t2NګPII U/ F80VHY)<< +^IT.Ma%7|; @ ,]Ω\/)x%;?mĶAQ Bp8輥Fvu8:_8&̜ߕ;P#@f1,?K ȟ3lŨ{y3?Y$BhǪTHwT +K0{30 oKY E; a($8o,rbJ{)Y95lꗂJYȈ]+N!*ʅ6SxwF9沞iI#^ Yߔt~OlImbFy~\ly2RHzjWCkJԊT;ҽ+iJ;]LSo7<^}Θ/Wi[K6}?RmԽu#5^9oxEM麾d!On"^ "m \Xεb$HI +z:F̦4{2 W8Nu{^Mp}:RWn[^ *9#.D!1 'W͓4wY-Zzg<mTh;y݉5џ>2`.d]a{#Z-=-*zznQ'[,$\alXd?,;su4d]+Ў6C`C:ἔ:ȷѡ̨٬ DH.j s=m*@W]xWdN ˾P3k({<֮*iczf:0q[MGFHVn ]oomKU|Q棠/|{Xƌ~vT)Acs@ >NCg]N ;0O:`m1,i6e}K.JQGF"1|;RR;/RV|ݝ=ڑ܇:s%}]cj̞é%;dϬQ$6z+mݢ՛ՔLD[> ;3]שU})VsōG7`"tͨT2xܭbI.Z٢?ٮ0l<aA2XBD81x῕?sc&H#a0Fc;N;IЩFEN*7_5yeES^=/xt- j )t[eLiN>$lq, S?>9-c (}\X 4 zo7m7ss/L-Ё*rccmnd 9ߪ_5^_?ezsq#`? ww\9ynYxii +iKy +O~+86ºqFswAN ?.<.|yH:y?yu`^UyȆ:ϱ~Y{vaۮ%¹!ȂFDuVc5.ؤ:f-~;MwUZ+z$Ӿ┋kϯ8+?iCqɺs1ԙ@$_ӪEq=#_.?0F_*n㺯E./ֲ;L7_zU/ns98:j,+ڕxXGPJ-kR竼Y$ĤXU"/ :]PJRmkfӡSGOeZ%p:&ꏆPgB͇0b.^]}Kߊ;Y$H1Y;4AE}0?qJ9q p[ur@k+o3[NK14]J]l&8qIyAw0ߕ##1ߣ HTi0|\dߜ'cHw׷oܭӴC;^} +r)0ԳzE"i!xDLAYXfq` \k}l_Zj\?$Tx ; &{35`z{U9t(zGRױ?-t/\AT FyGd,s{ \9phu/Ώv}y.ՠ&d[!ve7ddq\+ȓXP#(_+Z=ΙSU` h#>.vێuYVRگ׿066-n6)+%:p{ô5>~> +-7 Uh$~ 5ڱ{Y|ŒNhp)v5?t[c";DtN>qP27viMOaOZOeeOoOIJkδE/@KI_8C˨]BX8¤?/P,u'ӆ@pW7­ftJdИ}gsqu[;Y 1s09w9gl[*J꣠c|!fԐ?BPLH;i?X#]r_DۚfRZ|1oTg)yT>z䜷enbWZ 'k#_#y7r@Iӱ՛{^6m<ۚk+}ȝ!4{ͫp< 5.~~ᠯPՊIpA kNAGedHi}& vpkϋ4R)G•70gÔv$>0niW ]F3tŃfqN9Eʻ.um~"`*5-y:Hָ6ِ[0-꩘Vv}D+\*\[Y45>Z﵏x a;Ave,z#<)eͬq\7,|L>jdUuPm\4&\φ'CR0S)#J>eT?)o!qx)z3MµF8u7XX^D)蘵9 +s8-2E mxk7тۜi?[uČǣR|u)WAb{lRs Zf1YNȈz[^z Ak~c(ҼpBã3{"4R':FIN:F1+/16 +&oB' HoF;FȣfzY^(QӒ;Jץ oޗ˂t,㖹?~avrl֐L-\p` 8$re4ebQmϒN/,#[EyĥlhW+|~D{OޜvmpQIUy|o}:v?w#Ϡڸ(kTEk|VlV?-^SӻgI1IºsH8ˉ{+ySÝیԝ״mt:s-u{nZXˡOZۇOIGFۉ)EDŽw +K˷1s-vb|/1NQc}8nؘvJ^bimԕF忖YYZ.^RXQIM +ВXӕzL Qba1*uV"xzef;͂w'=ŸynrtGܭ_bp(+x>J7^._;TmCf48z1:Y&SDsz-an1jib2κrNj%w±?XF㾔Ywa_^ڳ{ԃf^EL ,[]}q%3{.>nz-Koz!K 8 +\coGOʛX{{r['۽ч(/de40K[}'эzhg\_E*m?K8/y6m@zGM3G Σ >ZL=*Fp(Jj ˌm{ѻH vrQin:zLD%]Xj~hϹ#\/Ҽw㨧UdsS%1bK+SoG#Cgn:24/9 c"WK[A_!&9/,$0#S>Χm7 ztIl*J4n٣z["Tc2&=nv,ߘR~))jr -9m:l8Ǯͽ`tev+גex"R%گlҚ+]go].Lw, ge>e +t38~Gub!W- /K<,q)KnJ{H \U]ޏj7ՒG~؝NXKkN/yv-~,N$Xp +SI^+.`9z!BWjK&nQM%ŅIG{Ja%bM FI<%p@7A&%ˁ+BЭ ;  +bP We.k/JaY(2xcG4V[{qu0MԲxP N۸!Whp~8& =6KQFXfP#uԈǦ^hC%65<;P!bϘB2vy-!ݡMYVr^[m:z_ln!\M-սߓ')i\ e΋U_#:/ud 3l+UI[w٥w^\Vޛ,B\D%LGe)TW ^5@O5,;͎ +B[<$sb_椥Qopfd<8˾sTh +X!ᓮXoRglOWҝ}-/1L]ԡx!:Z.l\ZR7=2F}%{+^Q +6<8YTx綳h3V}~Vke1zD /1x.jPfZ>Ky肺|`TRuDrPpuk*j*A%"?D ^kV,cq1YgT{W-!Y1-?g}]K&93*[&VAsz@'gs0fQp>22;=[3H4a}%.P0Gwy{z Dͷ>HFޤ{⺞v{(R=eUuFZti'6.ǰ>Cfkpx ֲ"qΣ I1#=+q92fX +94C}W%u'otpCX2YǟP b9Aeov1vU艏Mo(?*?~+e\uبIGjAaRKϱ&ۧ]P:KMzotdX`6N$a WjU|w0~]B)O#}SZ|Pk#.O6Zz1gr ozO?C[Go\9Qo9IϘwcT[rV]ޭZ25Nb>lTpb'[fe\'f3Xwۛ.I~pd=`uRäs+%7^+,y5#Pclq |CHGRX{= +Z23-ZO!8mv|Bwq-.##?~t~5Ӻ_>>;bp(Rgn/ 6,jkT{H ZeI2u~)wSIy u&sH7j}I|D`ơL*NTV(Ȗ3pٴX?0|]v4i@߯y=0Uz6#zd/ځuzfԐ|M_)őjK@S?fH(\zM!W6BZ,JKQlcst>ag_Dsf=)a>:MMN6Y֥/lӣ-2x/'jxRcpS^\>Iw1GBl/ihq[""r~"uNHo٢T}0dT hlߞ]enՋ|Q6Fvm)EUPMޥWwA'LsQ!aetEz.%0G;ќ!vq|Z +KT$!8gujLRK#ݛ=ǵQ~k[/?գHJW +JFl̐jLν.ְ 0I74IjוFG;Jу]LbTR@PP77Mm ְ/+٨Zg˜}WPm@oZRGy{$7Fk9P9_a7 QQTGp^A{gTfb[h8oŞ>@:jo/l4EsQ#d >47ds4.. X`F e 7Ra[ 6.x۟°$SILAfh>f;{r萃w $'oG8b.8>UJƲ9qrd%e./"5wč0˄ +^w2WçoɝS Cd8BvBEn5j+-\=Rm,VeZƪ"[mhLQ}vQ jȉM^1NG(yXU (yLG6UK~0XgT!&ʔn[(/SEoCZņ$R D"hd2%c/PW9\ E6J=tTNR&e*&Jb%l4[REQXLMyjSY6L(<*H3?Ġn$2CB\;l=D +V6U +[xeaqa$= +$Z\(Fwn,._yʧ\Nvd WiWN5Ê#xУX9I$2^t*čԻퟰTRgАVQNB%ԸUy%˨] ̳#SHa}c,K%@GH$o^j4W֘nA]~˻jQCN)܁&! ґr3sa)ȔOnkz܁揙z\ +Tm E5-edbQ+qjs:V׏C/'8l <5h10ݽtemgDk SLێ6N̲ÄRESKrǩ:*")jlP}Sֵ~͗g}߭rζN++xGi@"޵s "AMN'3f7P\cӿ}vzdPS>7<>uV )DYv^g j0Q |jNf,-c:qR ۶"T/vjؗ4fne_Z+Q/lfVF7k4%G)iuȨ`/o5FbzFEWh-UNm={=PY=;@*lOHI־(:;̋|1unje9K:kP= [sS9Q@‹u絏uSE=xiV{Vr:X=UG9N4Au*!;dRHUs )OiYCHvꁀ_Y0f|; %sSN8[bcsr܃Qaed9Ar1\\E[M/SQ'~kfTNs^7 bX'Q!Ng:sDpk(;Ƹ"mE87J[96:9㞔\i95U!;yx՝q6e'>lkj9YhuDP%"@JYX > hOV]jE>&$\ΉKPj`PvI"=C[K:]6#x4x횎%z#Uvx%7! тæP( +[H v+͌4]0`{랻r(M4QY6VJ0_ܫf8mvDB\{'oD +EP`2i:z`<ݰX麽kUSyEc4U /~Ob /wk ,)IO [k`dS*̾yt >z0&u~ &a8KV_ւcۏop(ҐfUP?zɹ&BΤk.ԖgLb.8Xv?0>qyp ,=5v@z|Le {ak ] Վwp|7({\<]"2H+=xkT~X<0f!'~)-4.9Șf%] \9˒O#NO&*ˍf/H -Jh\FmjL )Ǩj Ε4b/ f˓^2L(5|bĐJS c*uǽRi=?TCk׻~%FsUVyI E|;?TI}&GG6[qk6ϣ"qg>7CD1΂j48iN8-U??D%vev{kV\e3G<3n-BB5A\Zljn +m|Tӡ1t6 +@_b;%Ujbq&(i}Q->1PoǦ8 +[xYZA6/0_[ +OtYF<ԬXkkr7;!{x/q6# vn1t&)[Z3;DN s6[enr{%{ݸ7G[F!>oׂ1JKˡbdT&٣M"FP0Y'E8>\ +g\ˑmK8f["Y p/r2A/l=X̠;(uj>.B,7ЖBDA6f 1hvjL^.^̦𨋇u}+~63->Cp7\*$+x 'Z@?BZP8@E<ȣ`(TtZ<əxWZR钡NCJ]pyC-X(Yi2LȱS+Ԟ%f+3^nǪ6T;\x fo_J}< +~0M :VeUe᫭!}vOC98J +Fd~WsǪ=kf0ԕPՙV"W]y-G%jlJmM=јG +VK[?zvO1EΨ@Hj .i\oS:pR\-x}Uĺb4"KN}DbW7AF{ؙ;3wW5~^x-`a¯4 G0?KD=>[ʰ9:Ie/lɄB8(Qh_ʎ^vtHQ4qY HۀZaٚw]jp5 ,EZfBjE}M\"Ah̯Iú ϐ> $|4DZW- + |3LyI +B{HT晤bq)ڐsfyN򸂬aJC~dخT'4U'%'6ɭ-e!upO7^5]J8P 9>Ls)3< B_y*ջ֪;SW9zK]۳?KJ4V,'W|q?KVQȥ#^22t~X^-/F4I +7JRjv,_ԉguR.Y;)}67P?f9.+te,5T+%4~e)%2ДlJ3֘#z(9 f3bt(0ú4[ O[-wG,JT}q|kҤՄo#oiD{1N3c9[L)tB$pR7\ !:R]3e=:{=m dWs 2E,ˏ%H,Q~TBUZ mss0}8,:;xv|507 ZjC`gAqG&tN;?wm=PRdzffjkETu 1+Tdu6HP[E1g#cnv!(@b6W%n# +/ ;ښm捜R0T+Lk"SqU&ܤ3>=ܤ^.a3DEe9$rX.JB4Xa +ć)( +'5Qŋ?lk"ɠ̶`7_5xYb}ER +-R@8cK0u+bq;O̢A90"9?2X:~gc߭{%P^}5g@w0J屃?W_i0}zݲܓl~8yҳșױbe<UCL`<Gʢb_Y9oM; IEC.m)ڗRAy~bsC+Zm5{䖁hh]~_iFxSvޝm0! 'jTgv< h#S3e^GaP s׬Ω/"^g`?!Ƥ}!MsVcg1>NZmiaUqH'Q 2S*._|+ύNX)B^gM?4V%x]4_)!18cvBnyRMNow3kUL4֌,wM{)r䑍p)r6h{^p.ed jݺ;-鋹kI˝J0yk}e#(tO{YXn}t`Jw,(+sa( ZA9h5)ȘĿ)~ȱDdpSaٴ[ZW5Ie]:UY@S5ݣ/$ksYд]+iAdVŵ'Eb2ܭbO X/A0yF$̮l2WTz=:#AnJ&g`fz՚ +¨iWGǽN^U!W,L;Zu&gr6mF%@ +vOhPՄ޵*¤KjU&Btʨ&,eUNsold?cr&}rtMN ,W>+`I*Y)H5S5}> 5qUJbX崻UbODAQDam~UeC97rw.(YAPn?/~ T7'R߼TY~g^ XW +ʾnҳzջ2U]w0?7 Nuf;ZZ7C|їm_8Tz4̂sNK׶ˁ]bՉDQhú Z7ӽvjYQym;O&SӠ/91)޺KùÃgQeM!?)3K~71W7OEvx +/ Sc>Sӭ噵_Xww7?Vh)fЬ$^ω; @V"=O(Xl#0LRCpv$T`]8FtM-u/{՚*9תM'd)_?o]tȺV1 Z ݸMQ~Ox=OFAa`׳`PzU< +,i-ӂuz$>zNN鰎h&rMf$ɞ +#8Cd`"./FaI&us`ơs92]y..Ā)^SV*mƷC]!˥g5"IFl;7ߍkl|Ҡom3w7Xl}wQ7uy{@&܁auPi=Ƹr -Ev^gj _}k'k'Y`7, Tυ Yաx޳}^, buM##ٷ@KGɃoMOZIR尛Mht`Zk' +u, mQ XYbm|S9J{|=g*Ic>ʬ7n*$O=^qɯNOt'SW6 t撫rSrP!MUzdeOT3,|<ϭ5>VR3V +3/*ykɹ~6^#.7kF.!KniOZh˂&>wݳqXL\mZ+)ztу!ut҈8P׭г,f}XfN /fC;qVf{c~ݼFIKWoX^!֋l m!)=szH+ni Vu*:}+mK~rz^!s,}#N3Lx`GVKǛctƶU'|7AD5 E,~:)!(<iȄʵ8 !0z#WL0L>yxh\[8]5V?p=:AZc34ki' Xvdr|HhԌkrE`y])8p`7xX5uRb[a= I<rMUHhUKۯA JP Z^`lkxs6ɯW#sed)?Ijr0A6hX`Yn YgѮê%><,ٷOJ6)mA.P-v5`d+^^r՞uEܫ5:'P96DFJ \;a; ťv* +*VH(ezu-iX_ NHw\m&F:#+7Dbޔ.rPwNPb"iAu#Z&;9>({d,Gk w +ƾ I,*= Zevl|ѽspWPx"Xuۻlžisg^^"0ZKӏެp4C^nE2FauYK67Ӗ-5aeZVk B>[3O+eVؖ> oҪ-G[_7VS|T +㖦zs>-s!QiS-'һhM0jHNFf@< +l hѵXޖgMpP#4;NC/J`kz5UfmX0'ArYsYw/cwt_ZT`.Lʪ9:}InJ}cQb#k<œޚ-í)ޡU_^]k?4K`m?l+,IhDVaA2*)-[jy}ب< c?OE/+xY|GL82zdRҫݰJ89vvL|ҥeR..vG'0385)U7EpFf%ITЗw_Igtk,%D'@<:ZM%v_ks.?pG秓mNLfڢoW\=Hf]ge2ʢ5 ҁxXQS@>UZԾcS-,wr3{Py׊$ ƤZڬYZbHv1(J;`Zʱ"HN\or.Q:7{(k/ay@[W]E+FOΛOem!8҇F :ƽٱsЂ^'ӦU[[oWƘkJ +eĸ),v F0|keR*vkLסq~њއ#t[Ax1Ӵ#]UP d}H{sI:-;grJ_3QCeA!'~R=1!k>N Kbq.eS#2=jkb^skvl̆ep|a8EA99d؜ :*P+_WFLrm*5b!t 4{v)iDM!pkfוJq>WMUX뇍O?D;N:[[1v@vW׈-ca0;oՖʕK"Nac1 &,G=]GarO)aUo#^жC ?V3wzw: yDlvxCWw5uӕA\0`kḝJfAh#mגK3֊ MO% +:v@5{8X$fCijrKAWMYS~JK9=˪lCpg?d|L|ZCInNL^D_(s$ Zr+=+3{yhڽ?PѹesgLCWדx?o;wM`Dr^s#;3CEԑWN,[,Zd4\<"I=JտT _Io?wfo)5[HUtWۨu&&ˋV>SO"],8OYu"Lj_Yo̸ V<*NPZ6^,Q`O1Xdd/}P4KUx0[gXU-9#}gsE\s ]CdLl&Yoax)u(mOzzouy=qk^6f>1/Iqw$nR~Qk#2߃y0_vtQvq]zxQwS02Gz^jV:n˄飴lMtMӫAQ@i+8OV'u~BnlhV&l/*8d=r?yɅrݩ8Z=^K[j2kI=)?EP̛/ڶ #!,J`#I4 o8t䷱+(>{JPWJ$&TûMNt +k׵aq GYJ+_ x2@SU¢ᾶ87Zkuc,>{slLV` W5|.GMWк.w']i֍flќϝP泵4107Ҥk?7M͞"JHbM*/1# |=9}ȩV`\<-5GJ'DkCgE5k&e` 9Nzf|z^mX[V{ u{IڠDY2 +N-zdUeZ5RX@k^/ksi@{ɧ3P{cGZkR;5_Q%t5 B0 +׾U 0zdMf|!uS~rf0 kw :eM^ TSYw%/̣KUAoN>i^-s*W{'ňyFW%\Vy[>xm+y_HۿV8KT2Aa q_9)߉a+6j9s^qe8ʴ`#0ܲG7P?kWMEʗc*%X&Vwkgb,;X&]¯ 5g.J +,yۤPA9L9sȄ >_wT|7{)kHwÃDH,֩AWlPW4U~K:6G>73هȒǽCHtm +7#73}2L_ڨN w+GۿG4{bF%i/=Nn@]bnu𽬘vyYzS7 v + p;?k e[ɍgQu gV_j݂jō17 o[s(oÉ\%nҡc@f?$:auDŽ>Fu}ϝ<)]nMgϡ*muauC|ZWJ.>6B,rg:wY}쒚*~(*ؙpf瘙\z}.ȕ70& m‚i4ӭ>[ΰ.6%ۧU|Ǡ VA3?rl u3TT^M!Y6<^RR\: ,/)mU(y{;<ǃ Z9UɬDвjS6,U4g`ao=@vygҹov) &!^X6OPCx ؖ2/VfnRh#4MTHDȖCZ)s9u.!DpΗȁn68lH'mw /y[wmZ\iZV .]>w!b}Fԣ2nr)aIᅙ߲ǭ*geSˀXRO@p+5^N=F3p(w lCӔ2ݳ +Ǘ_+s=F n/2VFEgAJ/K9Y\Ay@;^ǣS8\"Dr~w!phȚ3E1Z"Miy؍%P [F։ss.{XfOƐUYX㵉)g(]FH\T|yAJiiP-1Hk3tgaJϠɾk!jm["w7Ɣ|(!V!ۈFi%TXK"7zܨ5Vu1tdms1%LHh;Ud><~HMJk}]8}{S[VpW[n3h)ݙ޶zn¢]7wݼg2>bsG9w_IiǮuK1& t8 6nsGk &" 6N~Gcz]¾ dя Pդ/'|ON5Z;]q[ U|j;#_srZNlշ_-$Xǿ/*֗❺5}@O</Dsh/s;E8^f%~HmVDӎٖ>LҒVghU6bYAnX6z; +n'*UOlAx@3PHb=dn] WwRܕZ);VWQRD:V;u^Fw^Vnռ"jO]|Q-$.GhvQF5f+iݭUK[R2QWѬoI7oq1qiDF70)ߜ[֬†%o}UGo:Dr{FuO?uqP+o@z uwLx6)SHl|^ovFWHyc[FlXà rl3EKkϜC$>J󖯤 kdOZ F>F٪fw-EjlL$<]b+1uِ;EăhہϴnQųDx?€yQ$_Yh B!u1x x]mO_R_InOXU;Si;Gtֳk, +v+"z$iF% Qb LҪfFj('uV 0g8i nPKW'CAOE;Lw:3ڱm5f?BC.IUúaVx% LC^ʱCtV36n?`mM$ +*NI[6 XỳҀHQiaȻ=)b9l~3<.S`~M4# +endstream endobj 25 0 obj <>stream +dD%smǯHО.iO[dQ>MAwFbT}n 4R鍠җ2?7IdzeG[gļ+=W7G|{i=ۃan:f<#r!kZIzswJҏ^rJȨz5i6J,^ʺ3!)\5=LMμLkT_\*TO_x5,ɺ{^nxO7n-Z#?hZx,fNy}wМ3 EקVHo~R]ۢ=0FnYb8dgD;=eQ!>]< *NyD&FJIq(;=TڍlSI2Uj]UXՑ\*oҊٱ5#’.qN!vG˹"kͬZ淐g +|4>Ìz^K +k>wݨӹ !).ͩ$MVrr"= =~{Tߵ~^P~\ ddžؙ_\hh践8 5~W ـڍɄjO} &OpzζcD[ǫ/ȭiXX_Ih=Nrlk[´V'u{PuuN*3D̥qG_  ZaY$L$zI'j)CxW|Q7A{<"^4]XfWɵH٫%pd[ڀpAT?5`[4}. 䢩}΀NW(2^ϟ+?MmX۽Yw> d;}n>eԞKybƗ_\UwUH[Wezq90A꺺e  C7ajy2LiJ X4 ;x#9YUUۤC ?d>VCcWyMۧWE'F_>yLut6VpvrөXZtʂmg +[Bt+1nv|G=8ܗ +zKl5*]<V$'2y.uGQ'Z +wYcEtm0 + GUb/Aq鎻aݛnw P;CӜ PfR?zuj3+ED^?LJm6UϦ{q6HvWY >ީӱՖӸˋ>ϗaXnqޏm1+kcdc7&ǂ8K"zr 4ml;'_Aaܦ_/< n>MӠ|j:'WLXci,{[ƈ4;aHf`CZM,J-W8gV +9=d8MDboa.Vjٰ=Z bLm(l# ?7{BvM~rt1E͘7U99voqN0.s@" `i|޹o(#U9TJ=\WybN`3-$ts˻K7ժТd|Lvٖp_K ?]\êPv3i[k>n@M8u=,br  $מdm(Mgl>_jku2}f7grjL3hU7dsmQk>5BغD }v-}qkQBtsX4˫M?abgfd c朜FsW{c5f RL|p~ii~k%7.) Nf`AMsas;>zjR*-~g7 +.ws{`m&mU4KW:wRCNG^nJ6Q ̚O`$r6;$K'hǸtY%-sӒHlڄC) Ɲ0=YlefiGI5˽"羋lYȒu,W}o=PbĘCV u)w*i +N&,8gA^V0{%Q6zous!N&F Wv`#8):|(gcOf.ۦ]q^c]ݗyoLmw ]7$]r46EPp0w+Y-9Y:(Ew]b5nq\kTp黵SGR^r8bױ5(!9 $xDsӹ9~3 :Gs6=G >s4` +զ{H8ɝltfÉYT~\| +IauD`87]>=޵WͭjgުW:)¤پN~T2ZHm5yc1K2|7_.z!ppc J`CrŻef"Ux~o"mD~ Ce&U^vH7w%,V5#ٱm/VhV12uiXDZ xy`5ȤD^8YG܋=pRn@F[Eg}tapd+ъQN`„Z×QJpA ;ED'vcfUK+Xwß]=j:O -J-l>]< :q1ؗaIfݫZ4Cy|CY?cEo$Üp=\|H觠ؠ„aYas8~: Dƽ lvù_]^OQ7HdKF\Īa:'Fev1;G-6ne +˝]c6S4֧kUh6Ej ?mgC>Z=)VXf{캭$UMiʍ@x= G_fːB0kh*A /Ie<ح"`{=z=>mZƒΙ0m[f귽4Vk:_Jąԟ 9oo]̥e{}/!އiWi!Zctw+qڀ^?0AyiO/FU<Z]ZX[pEhZm8>ǍAfs3,g]1>6sb]` +i y3L[Ӹ֣Ոh^v̓ +q43ѧ-u}_R? ]GEZY2V'59}%5iA%0BMnģd;hz4csM +r{?&5\Ղ6, 嵝v'Ǵgnj8ՒsZPW/܇;Ŋ-`Ik]`aMԘ6g_6v ;6mQ+^UgE?񝿪NA涱_6?%g_o7 tM{ V{f;h~(t$J[nMӻ_Gthe!#mI` U.v_-y^G[8SF'l/Em6K&D2dd ȭu>e#mr=Qi|3 TLF0=kYCxnů߶V,wRv ^f)Whg;,CI2~/%2x$ƲOuПLݴQ~VYaU2j6@lWٌZsrM MfP+:{֋c :cOS@r7Z/eNRZLȘԸ˼&%khPw~$7~7>OW x5q6aa棥 itz [y[73<]wI![ 娮&ޙ,FRkoŻoޚ;в硡㇙p4mhIٱٗ,K9S޼1nPVR%*|a/ѳ8Uk?]&@'t}pFFEMuuf,b#)8F:d_$yu^iGsdN7\8Yrd1\n {l Pj{{tZ1ܶbtus^@Z8'ƽ#Ac펎-KEȾŴm⩑5?9)ɔnSB&ahr]QY#&0ԙ,9[l<4XVj8p>pDAG xwlizגC* +F䯭iW@qtjAYl,BD%DXElzfgΙ8.~\,W: 3 +-m&c^^a_шΪ*EQ 5XeW(xJqeTߋ6 B|gڑ^O # |FDiċzGءh^ܿOQ#CІ&/ -_k22MӶ- FBCE5|4'(fB5J,M20]u6Йp gZ219 1Кs;L|z +tM|1uctsm@>EcILџ'IPݺy-b]{~+:~ڋkGsMP6v"8zJ6V V67e!}˒0K Z ɞs6ښɶyK~F韅7*g-#>#G8繷hO]aO*Ќ,u^{(r9M6.,i@E«F/(Hӓ/; MjfcM4 [X;o1Q+`~ψ/+{ s$vL<}}; +m]85NS5Azb<v4kЉBMݢG`LԡűR\CvAUm@,r/3\-lu=9/{v!ŭ:l_̀_GHs6cO>!fgMyFwՆuN6(q<EK*3Tv̡E26[U?nC@X#3#yc3N8¦1VH8 +3^(֤R\N˨(G5}wxyHM6HUO[7q9=-KՊ!gl0n ^F+b'%tq) kqV᱾LOXe9L>pnO4o>;EjfB_2U ߟրG>BÇsϜOO-MkuDw9#Zc5lTʕs5́Y \HcȞ3:q;-;fn\ vEA;8{>Y߿_ /PQ`uf'S-"[T;D$mX-θ|[I];ێ<wF czXlk6Xfo.`k[jsO1S5ĂA&ay`,WqZpX,Zk x'"2['f['UZ?pkH%:W#k!4dv3§jUOm:W]m_+ˇW+3\͟pۅq?p+o2#MMjʼnנqn*p|4 i_Ֆy,Y;Rr~8=`d`DFy0z˿\|- ԃ5(Lpizaגy^'f+ 8ԜF0aY^8_km~^ڤ)zd& +F+ 'F_[Wc%}0s/Ȳh ?h`OX-Druz^eI +Wҩ`NPK x{mbZtI$9Qj|JŨ}?6KVw:&jҍA<ެ7uJA.v?˩q"85ӕj[ p'23?n\Ɇ$ܥK{٦>!ebtײ'ti27ke)էZJtУʐJa/ Q{ /x'x;qˍ? 9cӧjH/8մDf&/Zq5Yxg_jeԂ6SPޒnuMqYfjG[1 ].t;bѥeq^V₩ԤƟyo"L~ +.wWGK_7[JlH|]ѱmGEDcV)Wǀ6ܨLöfh^FUc:Ykh& ~2 UfX6lC3%֮K2:T7&ScjݸOqђՇElpTsdϔ[ BIn+ۈw̶Q#ħ6 VF+ o<=*,y>z3;Ly4uѬ,p؄l)5YOc;Ͷ'Za-+60>q 2ֱb]?ĺn&hbJ\lGYU悯Xe:I{OWyCjC"lBD4SkK +򤶣&d ,Ӊїtv.m?$+w9~kaz-t[/O:9yvuml )gemA {KL;ERrw!ա=.smlM_|BmSyNxF?;ʾ'3>"܋Cy .? vjg迥ViѾA i,^Bg ̒a9ԝ 6X".2p/>4+Ah~mzb8H4z >嚮7dJ?x&Y)_9jYŁ]"{3V +ʰ10JMN6"8Vhn*8bը!ǜ3o0s{XBF5|efZ?pVg}ի{U[BgvDeiYIC+{CYz`%n&ZcZȥkǝaE*@ȥf-P,֡-ZE's&c.7ĽV-4YM-%G➆ -Ɵ)Ok] M7HuCY} >Dm7)%,4-6xFAih2h/QS5$n)Z3dԈ}OC* ,߾7P}|-պu˧5U/Uwpp.) +Πb\?Q?' 6a筮&ōwGPnˈ™mvǸ16,[ʑBp-j(g^1ۜ4׷s#nrpʾڦV.M5Vi'``wǍ6`ߘeW=mZI,)bmqûv? IQAzlowX{XK׸C"_r)n&+Rqe;7,U#2cFՕG. 7[_+TY]SY;˚A*Ɠ4[_WI; rĚp_91#i¸ S9ZYZ60թ^Yt5,'pzFOe(GKRtK*pyZr2ؤ-@ݯB/Fm=i|NZcy _lɬC!*GLI[ #f|Ξ&em(Q]3F~Ӟs!> ?};fTގ{x/M-R8k.m8"+#9(]]R [w8?6)^?یk;wOwh!:\W~zH!>h za戹TӍ )^c0(Lug"wSWlI%VM'{jۻ4?Xv~33IM3>%TEOYƭ埈gOy* ߄ͨ}ˎ.CJjAվ*! 5.Ӡ .:8YLUN=:%5rmeaKO;3 / GgŶPP2p`tZ{{zyQR" }w8J7_-w,뱳tgJW^zb/lNe<.Ŭ©-cikBFqcZqھ cd^ۛZ%6u8*paVŹs}7M {MakkxAR7/3 0-aP-=g&K8^WBR,tZ1\YAF߈ljbp anmo/a +ޭo0>—g#7 il ,IdigNW*W_ބ)(PihgLgӘ ιYcAiBSl8##//`}l XU/G.SYoc^^Z+KƕXRd4p)H62uOR@rZk\>.*5wJ5؍n|ax5>ҊK($냼TY}PnT) +XIGιPEsxV^mz5ti,׭ݒV|-1!jS䗼7s+bK%PPyp6dPW@|l7Kobu|^B' Cw,A~X>r_ǛcBcS\EuU܅tH~(o4-`g~t\ЬZ4Vh'L&eck%& (oN0p[il\ Q݀[l:\bVc9%НAni&d}ZO9uJ IqcZQ ܤ2lБk?mʼi$WuN/X1藳AZKo^d +6}e-i;Aw5=$Ax_H_'*=wՍ +na1ޮ<~,\y$&2OSS-]!n62рc1Q d0*H=#rey=6޹C-QiE/STV6ܷ G#K^6 X!(B$毃K /{eg 7A x毰sid)kQ''ow +'ڕ,7g_)Ov9H_NO"xx|$yFU园#JVIIST'+B b`e{Gj8>Ppm~e_6Z]c'3NL}6>f# =A {Ajx)=\dcg/[iSU8H3(N>$ӆWV#;`ᘊu;vm4ZFtt5'FҰ:jvy0@N0:Hg:R\*E|,IV p6/ԋ\Wu.-6S֊tJIE=9D}Q&L4lJfٿwj0B ZOw@(G+,_kolB+@=7sg^Kߌ]6#N:Dbr:z^kEJԶ3,OܸU0r5G.SbV.l'Dq="}Oa )jr"M8GD4\_#hfBe\\lKjuF᩟-sW8fcj" pp^AÝ-j`RQqv>zi:tj"7 8|<^b+>ϞA=| AM>h5Z"虫oP,-3&uSflngҏrJtƠ5/{|wX5{ulx-Abf֓1ZNڙ1[l]Dɰy=5Nh_.vݦŚ^KwO g*{sŽZD-vo Se`+GPo6#ON8hwP&`sŌuE/t8ݗeA5Ⱥu}`A#)x7 +ݬߥZ^S~4V1{?Vl]ؘv*AQVLvBT.h9 7 +c .o*L5É)B<7YIg/!n Y4mQl c T/3Bྣ^rPnxJZx\6@,a)ޕZc6z6M_ E>͹-|򮠋ZP!nciUopܲR-i2MPeqBTZk¤ՂwF RA0QOQi!!kY$}QlLnީvX,wlxy9+^s-;J! vC]v}jgxEDԟ}pl9u +?)0ۄRSL@ȩKϋudJNF :~N!6쯷XLlפktz܌{\Udكzd3{//>3\BMS`wYۜf~bnt}HZпGڡŻk6ѶPeQӴ]ѿ-4ҖDafݜЦ'JMNy6w>q؅ƙs-g0f&4gGV vf[̎]=i<Z;{TZI.^@zE XuG+뛿E%M]#AzzZ>&{ֻzVSsϭnm_V{,Mr%Ɩ[H+q*&i&Y ƕ,_dpy2@5 XVVR0g~寮V&.-9&rϙc-H>V23pj׉[Ut)գ:罇:ura +_RH1F: UP%/}R;OO .2o*<ƌ:MIM]JLve}#6 +$z2^f@9\G >hxΘ- +Hf>70xo/)Ey1~CW{8{= 7iOXi5<`nP]chR3\V춈!ZUֳ ?p~NڸK?AK. ˯:\2| 윃$Zy$ B1?&Kù3+sل}$A]VeV9ʮ~ @l_BSAw_W嶪4ɰRtَ)Dä*fDtiqX' !zXK\APY =p; +t6Ax NQ8{85^Iԧy\fÁN|HUoH%U޴[}L%%wV?ܿ|YFIzVmyU|ϙJN6v?vG{[2H8vlyB +[by~/3&q<`W-Vg}#w8b %Nջ +3e&CV>> us/;kMdPNQꆊ~2SQ&aQ?ܛ-(S#>ZGCYs57(HYx.~;?sPd 3.F eѼ~/ qߘwI{=_MWItw/8Q71(Uf1z49ݱO!! g*ަ'@H8Va(,>up4AAE]=Y߸4bk?' )6r\@T`WٹWd#A$!h553}/ټsqCdE9uN%:.:fnLqUP#&=d՗ƕ}Gb-Y]j  T5ׯb^ORV ZF.B^KoVg[5ߡ6g2xhi$sK>|ylof_wo$q'UTG~j7-r20ԴWڀ]FY|ຖPc@z̗AmO*Pg/=5jNg~V1tوKGcLX "iMъi~&։B:_B!@Rf0T Q`UC&xԏ-c~%< .4_MGs' 0n FRϜib(7XnVoS 02cs+^لHW{Bf@7j*FYӭk?՗od ^$֝~uș=S:?)E!H+90wIĿ~3"VN| +'1xd7c) X#Y->ƣkD +7mlA~_"9waUY8;@QwPmɾP{)>c!k:jgXUp{jT~=֦uS3k;5RᓖҊ +rh˾p՗ᘭeM) 9} 4Sksd |?%Š2Og ̻0VE^%pv\W,QSiW+ fK{&,et ?J3/j5O.~h譕`мbD"Q/VBۮ7EeKr==h+?wkzY5F8t%;\6~+$hnDKn'ʶ)O>Wc_^yOCͦEvj"hUG|_]nW8ʰj`;oBMb޿!~m jl;!AOr#ܠ3_'ᚘЯ@,"q.E!cUYvH+d ?5۪]vN&Otf,mUB0"h93z/ihj}ERB=8e-F/ 6A{GfsҵxTZmMl'= JФʫЖx5>?L P<1sBoݚ^ pTssg",3SUsqp7=aexs +R0+aGNSy( +C=cNuSK% O#ޛЎ퉲KRrB4?-կu' v;~N.Ej [bhk,Q#~K+G`6W-ӄ,dn=Gc$>$}u+X-cRB,1$[ l%IiهWf5 Uhhna $N60r@<Sqhc4W4?+i I^?*)zhsYu5>K%%}~-$יM[lB}ԯ==:\/WL>zjT+S y]'wYS`=YkfO_%1F_EM<"uz/|xxݏK7??/y+t\xY_. )֊ٶ5Me[1>^9ml͗NJ , +ek=XȳJV +`%pO.6LHWI׹^RIB{!,x {,ؖi$[E +-{,j/#s+/~=הgF44r;;4p"[.EGaV M**eyDwZz"tԓduibԏ*^u4?-'"v{Sb,z.mNUt]n}`7ۭ0{TtP3? ,W{ݲvxT( 3,A"F%3:}dЇPփ'.;3Dժ֑Iu- +E_W ^&g &tFcA3sZº9@˳߯+}_~N@Wo" +NyFC:y+C_K[ +S/qhK{S,0oXqAꮏsuۛ%SQ,kؽ:n?5`O+hԶa]a[;Bv]*x= S{@V:Ќ1=;n8_'МE/B=*6( &;M.-dI`2Rعg!H-GumƵugyŝjh[D va,?0h@V,aԕU)9r5]r^<[s(jdz04pZ3SK7awY*+ǨLH' _{ʌϠVT_6R;qf#л~QJ2CEoΧ\G5K =o_OgsAxܘw?RL0rFT ssmަ]QS DȋzUDz_jw\ҙb2'Čp1ǰaο948;8ҁvIю9>jk73Hy1EwNjkvC<_cJcfda$՘2ZG0vwu?C{(*8ǔėEku"q>7~HrsIa:RS.zԓq +z~MMFb + +9jqN +ϴWmބw<^@ӬniP>|-Մj^vq|%791w>dbc @[xgDb$rb)2Iw +-N㎤bn?ysr!5IRF3A#OkCr/.nPȇ5~"+7ApBK,[zwGb$9+IXE9u}h[?Fh&;V:e XMmO$\: &(%+\tCj28-jrv̡ + ~ےg i_3_5,kӽ3@nl&n|3Mun{]TcsIJ\hHF@d=%{X̰Y'NzW_6}QtcJlN6>fMb^XX}pV3&Jb^oh=4֠|tnߤ-̋"1{uYeMOer}FN6: nRF6+:з 33X[RB9WϭN %C$yGzx.U#\uLƆλ +8w36ܝ<}wWď;rmz4Qf{팶+Ȋnv:ZoERdt|&-o[Ra^`kmWЛEIߏH=A?xwXiȌBFyaY|}`7] gH|}]9ee#Y6Y4^OXEܙY=?b1t 촣MS 6=uÿŠɯ'g+P\z>~fWpG3u|?z_5,,lIB9]ꕱ +ڿRmzDŽmsͬ ޢ9\qC,G^ov1Kryğ;5Zvw"rtjw\u[;9 >xcJ+݊vAs5X\zlU2_3uTh(|Swtu7v:ϡHY[]3vÃmw:|(P]2n_jV{P- KL~WL'}{v;~qF!i&2^PןsYU<[ji3)5GC]N] z E}(\ԜaB<8>jk{ dq[OimNjzlyU^_TzhYmo;UE $ZuPCGp_^ߘ?cZ o i~CHNO.o^.L<0 3(d?gu^Yhg^l6R6) n"6\;ԍH.)nu,ye_HW˨rWq3vKG< 1N%);N+ r<%̱dc|loS{${VYwnKg1ҙj"hT s/wL!>Y;Nl3u\Oٞ9S [a 'lBgcFs^''ӏ(|)﮿oXel"o6-}׼:Vsتvވlevl\1Wz͌*b,νkobugt?\G$ʅcD~cc^@xq:dVJcqA=7⢵.Z0w1(jEomu 4Vk佧q@giQM."9h$߽Zb6U@yj9z{7 ȿG寡N'N-ߔۭ#d<'ג_Z'Ӟ`]1nM?ɰ\bdqJ}0.0%4*pyG|D@T\2hΥiO2uJkQk%5eM=o8h~6ƳNQn%mJ@M{k;>:vIF.'?6ȭnwk %++ު]W`͆3ݖœM2als܂FS(SsgF: +,V gTqzӂsͱj%td S3tn >dbTYuvj:ܨsZ!U۠FR㟂1<U-ٰ~.4o=}/uC[[ӭ 9hmdFPc-vPfvNa)vl#(K|[=KM:g̛;C; 5hߛa9>M]AlgU|UJ{vݎ5T۬ U.Rf VH5| 5{=>HsAZ=vZcZLrP0P/ڨwWǙ A1,a9ݻjleUC;@8Wjx$-20߃Uϗrz^b6=46%>ׇ}$OH +1q- >G,ؐ,Xvxz-޸q3W/l?xn?b_^/bln`F >mHL% n.:v$dCwL/t` [:0N޸ +=OUzZv9Nz6~Au op{# tKd|N$܋w\IpɱZ_K\M=t3~O#3G^>wmVs'7iIN98ӅysQ޾Z0rz)xLNQ-e/ INɠs_E`l'nZwCaN[Ll4mlq,Ĉ^"+[0O.vniWɃqhL|4C|;u}k4 Jgr05uZ[ڕrg织яFigF}s3mysA +G5F>^^mq^O-%A?j祮Fq|ػp|tF4Y)ӺxT +ɾbLj䘾ƙylKY*=8[B=9@mK2hNʆhPsFl7bpA7]W[m;8SK=}YyUT[^JtUgkF#gCu=/;̦ݶ>iQ<}{3YMdQ x./"0P:I͠9a_~5?i6_ +%F(z+:Èo@z6A2O{God^Vj:][EPWHU/2cwETk<Lh Q(euiZǠ/ LP+N3%$lL mj)!rE;Cc|chp.WjIJGŲ_G䦬GnjuͪdXF\'d'Ri&'t}+hہbqd&TP}n7~C;4d:q|6= +չco0 Mq<ڑvٿ#}+ +C링u}:LlkU==Ξ)Crr[u:UVk5cAsIߜsWhGM +UV'- h~O2ԿFי~>`8Knb!,r=J@J'*>VRI?묳,Ք?[sU{K}OK_뢲T}ف7m]N=xSZJ /mўŻ1UFmupDvCe +r('>Q:Iw[<6n.^(|ywNssoA@zkӅuEK%M?cQzk^px ݚ+$5 +ELݮHudF'k Op+=p>\2Tʦ9Eu{a%>6ݯOHNSV.Kˆ_Q"P2T=WͪR~JS3!=~H{7]Y@X>T=KAsuIjLh&dBxc\ jƒH~7f +yNѾhzz?S.wd .ןEWDAuvᎩ$+,; wGHueEPSM00ij+,_c(] "hm;ӼQ^UKc>G> -W.>nZ㠩ce}iXV_ҵ`Kc0 |Oʂ CqwO"Jz4E~ysiw{xd쬶%byHRk;= ^ +ߧ SLԊ>]﯇1}02hnzbP(b끩tN bӫvnArZ?;j`%[i_Ge VW;nhh'Nh7mc_ N5,_F27<ОRԦn`{n&'Zl-Z pOȕ9ӊ>*{eek+g|.LNfvhoD,L,193CfYI U̷N#ʝJ_zc+ +$4!żuNƕ`_hxv~rqRuvNpΣū}(~ZS?m9T: q{8f慃,ZG?&Xl)+W+WǤ=ԞԏlP7b]ʆL<rF¨^ֈ3NrGI[hɻKv^*{8uH3lm>Yx|PUx;p/gEU-;|Qc <0Xфs|?t3ԔhX+&`8mP4Vf-Vۯ2vavdG~]hfJ v0tcYS ;|(>.YSV;\o3-g3`X藣W֪j$Fe^ᆬ*{ &tIVd:9GR,VGX}yK|3U< L 뺒}/=8&*Zmu̎"s[غ9+YExKM(3kOemO0bdCu$mz(&b1KΕ}Gqhʖq ,p yMG/Ewڹ\vmJrIiN[TyΒ7giw^kD{6צ/`982*njU* +W$ +`Idp| NLbZ Vn;1 :r.ÍJfR1LIhfV3oJ ҅H7jVFж'l4FoJ)֫e= ǩ&عĽ5X@ZIit]ϪzZX+ן}cRI;1}S}#dnz.ƘϗNt0zkESg!S^Q}^. +I]hA cR}m{sSG&fgo+azcrˑvρgDZ-챋ݲq]\6g-YՃ#mNet2Ǐ=B^WQ(Y>}QLfzi5.}}j0MV`f\G-z.Ͷ:,]y.HC ]V7y3ۨO:}drn0bl( W-NdX6JFӞoѓ&4dP6_zF-d*VrI ;8=4T}l/rv꛶gkǿ`vl<%,yFKprNvv]>yE[l4Nݙ(ۻ3!ד`%T6&`q 9L@ס?]Nm`0un{u +mg f>h ?.u`]o`fM}cHi4T- Cߜ4(GZћٸ/rwˈ8^ZkչF͎Yx['Oz{oJrTҁJ/?i}Ma|{YRԑP-Ȱ;ɕ]6. +G kt@߲x!g3SXʞ8qͅDa?ęLݷ2U^ct'L1w'ZW,vp.AB/ZJ89(DR`1htqpxT$I +Ӄ.2ʐ3bhNW3 +k3K5QT w#<|Qi눏?݈zf-,-+_?D^:&>%ICN5o"(vL/կoM}hXa9HT=؀W +؝GdR{a˗P)u#տׄS[ArN3K;oڻ踶j8Q+o+ #V*4&{-1\@>GPK\!'7G"?ruuUq*n%EFn./#4jc?6LBhaL@bâIg*MIhE;yocr*mai1e[Kh-/}?Q=-n;v@?ЏDnٱ WkQQ^`6:t\n6M+ )ܘ+OE XṱWZA~kzz +zwatf2B!<hp:1o>?q`)'{c^SڹFcn +R#w~wAwA_v|:\dO)N7c3y1]̻*({CH[4 N촍ǜ j3"&tLͪw$`6Pd͒i\JD?(k|CțPGm/ҕts*hUEg8F47& q)~[e*,g&{9%YY ]na{>ȞAHYP7K^^7{ztJ'R3tttfcE` +fȬ |#l4{M*-?'w#omx ab?d>|+Rn>VE|*w h~h6Q^ػtݶe8v- LR&P nQ8a<["Ž[#yw\F>$vΨ,"H_oS.c|E+1EZCsٻ&p|]Cdo5hMZ9.gzJ+hӁ7",]u|=i¶`6L$ +c[S3?{5$ڼtm﹆Jx̷]D0V:͆ēpi=zƀuoePxut؛W޴)" Źî ů͏x7JבOcrw"I27Rxn7F%^9ڸB>Ds>w΅^ʿzmсƉ}=-W6hͦ掱&.5Eq U0I5+!a j8y:ȷ, +ӊP}g]t~S|µPmS{jRUnhkPB_c CFPRFX0ۃo1x8g5KrtV~(<ƱZk`րkT6? +l5h-T80n(/fj3kw_{e1օ%$kұKy#mms7syG$/k,|CXdC;淪 ֶC+U  OU}dJ|D`A$E_a>nތ< +g}14mt{1q`^gOfgk'ߤD~_f"/5$E%և\ͅPź+}U`_r&:#l51_]}Ij:x>a2dAڿ5ܵ_ІMU$a:ӋgQ9pVSϾj˝G/ +,?jekӷ-wh&1M1r=r`<2*Uݴ2;ߎ-2O[g鼛Z5ZdWWXcaFiǨ9~gc}(*~07VRIpx=n{\&v}40G6O{\x:eu\)S}xX v2 3隈|>{]%[ Z/D!fW{عKwZƂsީK zæFbJ 7|RwOS[2R^.bg_uYSP24V{3].TORJ,]6Қ+%Թm?MDo17#_G/iPwˋ>xM*ވwo-GjdUB3=[eiy~kT:Z^dAx*/VY&EOӘ(8YR,:8>uZrAa΁c*Kx`/]j".JFh|+ jPe2sY®L4 [Di>K0~wI&7'I;; @ "sj2$T?'pyTb̽Saw:-YMz&<S *Aޔi{G5q1}(2ڽ㈟pRj7ė8ZRY8:c7p ^hY1x)Q`]W^n#Cm@1HqӨOȾ-1尺:1o"5Rx[%婣GY̰ڵY C~g3oos7r3]ɑLq`pV-2j r⠗(U;J[)rPþm$?CDh5]W,"ؐMST8O ©T.m<ڶ H@@?s)҇z6qGW1p pX*nٺg1QеFNH}󏧖W8m +zo'LE ӣ%0mX)Qخyz?2 f2푿!FT〺A!oANI?]9yf'N˵r:_6,?}<4"]}:{w )|.򧕺\ih_xIm'R.=8q՝%F褕yܾȰGRIgƑQQ TJ"֮}N'$luPwL'/?cPM|#8w\zOytأrb,iS|lfs_D#c5fR綊-M\s hLIkErYYc2p.sJ2" ojBGLEDޕ6u.qrĵ<:j"4y0yXO.(vmu{Șc6\3+ܧquu'3km\x6(c9ō>F[pl]|ҚۚzvIUM2<+BF>4KH;2k<ե=ȎtԖQEggm4]@2 p +ħ0w'71̌Mt}D߯"3A{PBU3Pfhxq2iFgxW/Wy4h xn̡|h`-;bwV7!\ӵ'j) sg,'Ouyv|h;rQVpjw)#kL8uFX,cvsT ng{?`ԔfޛCK, &w`įnۜ՛҃ gg}}\soeV /}P,kHlNS.~rwһ}dhw䯩F{lmڅgrܲ޿Wr$g> #[ͦ*>_6[t(~ukƞPh-:ImSs\Ξ,]ڝHh\"k<,j]g`n ]O|2ȇӯW3ʓGMalA_@tCJ~ꂴM|K' y͔%6oaV$%]ٰBO +g;a^UO?Eγ3L>;{˾qD/m3dnQ> ක:Ʉy51 +8)ڦ<Y QFEڎ۟@7Ǭ[=y 6m+- +pOȅ݈pGcOQKtfEwc*"2pL #IJ>UK]nw5Us]Zɨ1ZwNk߱j? 2t./yG8{YL5 ޮOt>\^;~}sT3N"g{s,PoK40X),nXeדX]ϊQYsܟM_Gw(n&Z+wO&l sd=;֢о혱,&ϏaH`*q &Bm7#]j:*# +1ySvádKNY&EDJg*m@hQjkgeCKS~`` &gC=c 5M~gMG=}J7Rungs[ţpǷ7SĤ~{Όq:g0<4JKk>3oP{:ZnD`Er\J+s &b `x<%6z uobWvp=ֳ*K$ |Ts6PxAU$^Sw 5șuҬz,֦z%BI=j9O>K(NNҦ4+cᕫ͈QPZi*1+1SxLSfO ">~~ zw5Y! h-r^Moyue\_m~Ҁt+IgL'=l@4=qp;}:h`+)˱wf3*)ƒ@+bZ+)e£#_@q2C#;dZjXޞˢ,[_+Hǽl\+}v7lM.TVƘ{\ݬ@i9pT_aŔ..ٓUsNuRc&szLH'7N/j(w~UwOiYΜO|P#562 ׸Rpx37 =00~kkN 4|3bcF"7{b V?\p.x;ˤadOV@AG\_7uX.f@gH:3h͠YFZ3k(WZ9̦ Nv,HBњÝmGM9ǣs>%#Av9SYY4Mv 3oؠ]9 *0aAml-`βB+2s37qXKu;xA6)ucGR-rf6-1s[c֝p(El )zLOs"T2覩%A@CbC 8:e}\\L{$ Z oL2GbkUX9ջ֦0 +m[CgL_.Bpއ^m `!V4f[ɫ%'Z~eGZ )!}Т H 1VQf [FO&*bW0Fd>~ت5]ԡKd4Fx+Ÿ kɍa\eUPI  yc6%05V7u=qak*KI*j8 61]"?{<9fa4z.,I^k`|g%n̔,W<#W`u,Fp1\*}Fc-[㟷Hܐx++n)|CjC@WS +O14DZ,:/垽IS]lD8/aiAw-+v9ߏ[?k|MQ;\sI7ma~#6#.|t_Nf9b;ޫ%nMoɎ9C2O_O)V<MTZ_*g:QFCySM.AqX>wn?BǦz<0._G ~:Sߋ.W_Odz_}5.8˯Z#Xzo  Ϋ'wFV +-Fk53P:|-|6kĠLL7[V Ue.J!HfvUw3I' z> }\cT-+m7ܘT7(PYg=s1bִܡ*ϲۼ ilѹ{lQ֣BX.Y4V.mhy)=G2یi[cqHA_zՉ+a^27H[0\޸;0wekj>L歾'O=n0ZAze;&twsmM{GN}.sd-NW@-qlBj^mXCx w3~?\6`&N],aVn~G@.qrGZ\,f(Xj$ktFPmY`QZ33*8kvߊM[[!41]oNlVc#r~+voPVgdHm@ȿr}Iu"tS|hFӮOӌfrK/\}$D/lٙ;|&S/(оp'Omjb`v F%u0$3A4kΓJM1QC-b2эK$cXAwŎ<&^C!(dY pƜyTjJNTE^Dz#<coN᱋a^қcu7pYz yH%>L +)JUrTKBpE!O%sK` SJ棩EZz  ސϘ +.R9ZQrl=mLe3TD0P~VQyA +׉-AЂL2դSz!LG?f>kxd:S;قL_]#˟-Ȥߓ +2 +n\W?^3z;4:IP}5z;Vݭ j!컸_ڷRI3?Lx&0ogBV3ikz D~ |Hf~7?[?|Hf~RoHf~7Dg/GD4:\sI3Xћi/?zL<#%=|{'3N)NPs|HVm7Df? !ua>~}?%;zCBЉN6' rs ﲯO+cq׀w;k~kknQe, +->fL+gC 4i?˽oLY~uyИŽWv| 6$zx=]u/U}w\߃揮}|QvbgeUڊJg<}-핊NXsݬ>?T_׽GYԦVsp\xW̪r<9,6q░[ H(99^X7:Y  +f9NlGܚZ+Gw2~2Z±-:u&som\uMLqXӭJ]fz_N:Ceo{kA5 +U[sHŒ6לݛz\"-7uu>TWd Α#Ar'U𭐖}~fRw>+ij5#&sx3x>;^?+ƍO"Ő%DHBcqs|f} +}mXbzϪ#đmSlC"^vpjuc֯XN? 37E&S~k7L&k~ |HS_j'>Pi>j R |H _?N|گϤ_nNuxYg_!yWJpF&$ߩs.꒻m)ԇQ\igN83q>Drz3P}fGk1!8brwZ~܎CdM^ 3R)QP?${n9:qV'jM1Ŝ5=׬<-BnH +G i_?yYլ6aBQݘ&8+=;^_u9onST|qd:$9üOvۼ~{ C)tw3 ܎Q[d\4-NR\G(/#ݶDK!="`wţ)Ӝ).kPN2iŀz.%썞c9<{Wisa#Ka9ɟwwF|l8Xˠ0c~6~9Kq&\ Y<0YiPa]+T]1Զ)ywpA}|$ƥǧwg ++iKV=ͦƮn;|z6 +CBgx3 ۮ+KxQhޘ;VB+{ y=HkÇDq#{Jޭ1kӺ>9+q}u.xgKyP/|m8[U.[Aެ{1݈.H=TgZTg:!T ~~7D_<Ӕs_5iLMo~xZL}_0iLGoFYYe7}Gj>]__єa?Y_YNi{vT6<VVԀ/Bݍ6k:n~/nMvI~armO}Nk;],qjvgkh]]zf:.n?jOoU-P%kg(,arOι_#fS2{U~FGn;G=_^F9h>f$_H6+Eܔj~ϟaoub=dl<ڵ鉺?u}ldQzُi<' wAR0^sI_/qcLw8[s;Xm Y}zw(ÄUd3ZykY}j@ To鴽HV U,\vݹt:iqubxK"侞*] +V*r[oP5㉭>G >oaB;E +7}TE +?\Kw;5nUimݟ`Ù Ҕ?|jhEZ|A(研r]^z?}*q*wnH<Yn>h}H?<͙@HpCaK0Tq'@ Qg0nM)u^X׃$!@70]q.-ۃ|f¸ |V[ZsSQx&!-L1TV6-A]OG0Tȡͽ䜳v2oqYUa%̩"JnL=hnj7J|:dHl]}:WЏst#4ǥuC- fYE2&Uk)iS A9@ UB?Oo0{7dg Kx<K<쁛 tMTIgIuyOɍQ1fu1R6U +Qqr9(˜ߥ:˗6AK|eX_>xf!3D++ ]a!|+>zh=Ъkao̦4>f^pϟ-Cy᧕"Rvnh8ڂ9m٭WG~/B94g♚ϿR_I3XЛYy A4*sq#mf|Da^(P+; N| +^Ȃ`Jph)d L]n^ ;)xmOsk";OEcӺW2e~[kV4D t#yiQ`&T眷 o{l[V-+/3]=WGVQq)^֭ Wn6Ī^B򑮚j5g\̒ZFq#J/¿ȴX2ᩩ .ԇR8O|y$a;hO_h+AѲȇH~0?mo*} ( ` t9YUoU휫Dkrg4D,GeUN髮x>?VGZ\fYupvh,^.\,SbA/+ T.4N,~6o11NvFz$?(=#Mk211NT;-~60ϟ pnK_mp(gz^^oVtef˻̓_J/fji!{q8>tەMx}J>tۧ\ܖH* +TqJ`E,U_B'be@Ӌ֙+gl?/q֟ո(s* _[U6ỏ,IX㛒Dt?PAڟ{xٍo1D$>1P6Y]b ߐ/-Umj2jF,VAh\>U_~qzdւY,^kf,E&.94S3!SaӶFBMK}\fQ:Nv[kv=?E ľ%yaO!Z[fk_;EJ3a+G4?ړǓNhH=%I$o7P}1K3캝\f@/]X;< +9:p.SxU_+襇gfS6;_n~mi*MD)H&?Fww% cI\5((;%mEyn ,[ˌ^P zL;n96w˟l׷W%Fy/@dM*Mfvf|4cҮK`om&Rz91w>lնyޕѬb.fdVL>O'YPƼd>>S0p'fv'U=­b?$qB׍E5M}[ܷcb_Q+r9͘skƜryn8gʙ~bTtD2e:ՉQy?/__]O@;bJmC1F=u \ս2yCd-DjW|Qܤ3(}\e~Mcn>hG'>__'<*4Ujj\*kzE=lCbI{Jԙ iw+7Bn-)_d3=O@hls J3~[Dyz`ď߁ +/qR1BP_St+zsVi A䯙5P MLl T/=4q n ~UM>/B-&SMBPcK!c#Rc#1a,P+^[Ũ*Ȇf7myZ}Gɛ?bM-dclwTv; 5W$ğ&3\&-?^z_*qoE6|INjڌbS 1p;):HϒD?EoA`UoA-υO?>TGZf|4s{&+eY(G irvF#pgMU7I/N6KGtcuc |0·T1X=XܮVvܚZF62tG>"RݏAYhiMn`)F8\%O  cL:6O׽_N3丐]Ykz9=6G5}mTm2Ki{N.,±V0~C?'G&AL\D*zGΕеEar> kSkӐcV{*j`m$ԗjSL9JKQWmKyIvD˙'vf]!Xur݆hRw32fiys8k 56nr[3xy5s kFX%I0o7P񬸷l{OR.@Y@=:ˆ'_mR~b$.ٽVSo&p(R}Ubj |*|,|$n)#zk.mwa۩]z5oQP5to5LLi/RNrBN׻|"ؐ8]w uG˯JF3$@[$NLCVl)-Ixfz>-&wivgp8yr̼gԐ$x/P$IP_I@>KZիh=9p5ˆ{HvgD۳r0U]fR}Q-Va֛<621%Wsjho7v?/?Fq>IbY0涳9*u焯W$7)vXeq\\LSʌ}jDGjDz~$M!N՜mK|zNbiݼliܿt1!o +z*;\ox-ʦ"la-DpJig8+ڣHP%cw_Oxm6Gxˆ.n=gV݈Y*f'gmJ_ɸ N$JQLeɽ+ +TPe\(BMBU{?/qmV_gpGEܷ +\|7Ī#y. Hz˦z”.ݜ{ƌ_^ر'Cwfm 4S:F5I l?/0ꩠ4>c=XK'}"кRi|G%k6ݮL\2 φ:Q”ω<14?S0N@ŭUǫFݱ [J߁zIU}:QeJ+!s]WL1^:b[b)<$tKU'~'Y.v/XX#GB80&qw3]3,1eS?Ӏ1^BiNvBx.ȔX_sMeq>FTu=TD#-Um [VT8Ixx:i/n=G >٣jCq=/toب_ُѧ}LܧL\!%?毊OH:nE)]^(6#>rMT7LaBISF|_A>g)dehx! mIJ t9TyX<.,!A"@nj|瀅WT+vn-뛰 +ԣW^e}tp~Ipe"?~3-7_L_ΔiM_cǭ_Pɦ*ŘĔrl^n{KM8w8s `mE-oIBءr?+2N'[5w4Dz|^XRHU*{}U&FxtyԢ%$Wg^<(تx31ĈPS%K8AĨsL|bE. YxC'+žK?d~~e9oj׍xUl~̐ϫVn(럄vxp㻒\&_P%,)@qED_rь`'j ĨB;G8Bw{3#yTx$ۘ9(ˮϦO Ĝ{MG̉~:lnRtS:k=,U7dJqg3 a乻}-:ryHt=kT&K2ǒD6e<ρ}Uezr|!JZ]^y[ĜzBOZS%FnbT3 +RNӆv[7NYWA;Oio5#bU}EO0.2fփ#Ty;ț^]?& ^V;S]Ү~: tĔIݶOr +H%n(JBeϠV;$U*5칖M-ƴ˜iyRY:nݹE::)}uq˔$/?^x,VڇO6rE6 A=cO!gSKAq*[t ,ʄ4Ӊv +!uxG>'.Lx2JFFBŭX7KP9m^\ژ;1LX*A.l렗Ep7uft|N8\xl*m^ ^FK8|Qͅ UO d2Ә[qՆo0&nc5L!RIr w՟큱%kc/C7-P [Nq\\ %ptKZɈID!`Yw0BY{)uT[r:wcZMo56j=Q_}D_d0[fctijG%9kKY} ?z?z1|.UHY5-G ;8Z5sKp\_*3r2TN^u3O %q"ݸ ہEm?\1N,2 +-BU&|jުv3j93E^r5f, +V/K/I@-Q z uw.e({~t<⮢[I<7\ N|dž24j0iٙ# /5gBSje{?'ҁghvu@}v=1`wٺ +0n =.\N4V<#jz2-ds^:_I8'A{=%q$7TIoAݍC.,V}>]v%_Hs0B{I)@mOLT/lw|D|L0@R9?%cɎj'FD!?Po*u7@2}WKg53X.I>vTBw̐V!Kzo2*A_|]+0?bk /\1ޅbk8 wjG|+aso˫Ѝ!L"݉(:tЍt(P7o8w. Ii݀ǯhYS rr{M=$=^T_e+CfźN{MM|,tiP4Qޥ"aK?%Zq@aC՟dtႡe@L9@crW<%DYq(2 c]KcgG_} EJSBV;Ix9KL$?fPDߜT]tF啞6EXoC/J,nIIIJaB=ŀMK7$s CrΘ}sClQeO-jO:]#Ɵ$~_.jesO  [ۉ$/ˠA#s@>tYGMNFvٻEԷ*߫3\-a6e5.w3Y@[op& 1IEg ]|uvT3ƨu+Sl@kqg?>N{1sځ;nI^u½eޮպu +ͫI釻1Όn!g(Ae|l/oPNb{"oj>[/d1*iĨGlwtJߙSyyR}WSB8o1iW2-Nɗ\aM%O-qRf$#}Fq^u]9z5*Z;DD5=164+`{,JKڋQM'&.EOw¨>ks}p^Wk/槒V4NƸ[:SÎ̩:mRw$qҥ5O8&?);T7vO>\%p˛7-x=YF6:#M7ua5ƿIg!({UPKO(1Md(I7R>]Qϰޏ.{J^KKlڝH]c#v`1y/6IVfAuSA""·]_vY% &~V2ZdF$j5F}-NO/&n._q)[GCcGizp9 ƾ›RˍB -E)5 `?/*{oAU~_"^(o̒=z;M(Z#) |+;Jϵ+nBm"mM6:y7)a$!#_oHLYyAJ&yE?—V5BwݥyV͚nIjM +-J*T#, xGGWXl/<]2|q8${r +M>NGx nwoǵENH}EVVgۥ 9a|/9#T+kf6k,<2~r!K_P~WcTyWL:camS3!ieۖy{oMX 9|S:;6/q~QOqxcFiqe3J2boXVקZ3bmĨ.7| m{ʧigK;OYqwlxI.!W-mRzzy&Օ"ېsɄ ٞ,]kmXoaAiĜyDu8<1sX'vSMOj}ܥ>p7yq Mw/x-9/favYۓ/HRcɟ[MR= YshCuv(OO;+ʐ{%fd'_?lV(~̧]w!wLR>7|u~ Xɴ1ȩ}-㭯Ƥx8P[3>t! :>5Ai^F4IZ>)w@OJjRimQ3?N(q|+fuyz7ۊQ%Ѹ)9u{;2m)MӚ_hLOdMoPz5a8OA.QLy3✲踎$kK|i[P˭xգHηrw5kUa4$Jˊ"en nwpE9{<Tq@a;XmbL ZPRz:|~.hkX 5JA*aE|j )݂8B7~Ey@Ƹ w:D%G.j%@ThPsQY+6qCc=rvkvDh*pJ +ԧ|o hZncMA1>8F,Z,$_IObP}P#FU"1 A%M&<͵6= >:,h?oԢF{2~ÖK%sv ėGl#kpPPѭe" a]YJ(ãR +1jLewl!-rb)ZDxqtX|^fyJ#CU4't@ wm&^Yxm}W@FJJej,=ce= .[;[Ӧ/E)o OG u|5f]n0PiP<⻾'kj|WLhOy{jɱnڱK/)8_W):N]bY>1I$DϢ += c0K?qor?TPwLI}؝]&-&!Υ51"?7o;#O;kk7 +q0^i9`J<޲w(uy u׈>V_",c~1<َcZ`t(P6'=-!sݛq9r7$2|}zmŨn0iV+"ļ]}U+Z?{ٰ 5'_X)DcFz4s~R^pNq 6|_wgmLw6hO:P1gw֎;(3Oڏhif"q=";T^7ꔫڥxuIv.\})ovȥQ |I䷛_PU/H1M*YKrEW.xUCDasO(PT$ww}Kyk12sޙ-nW۶TRT"K~]!_GnGXcT<#C^5D`yD + ~-Z{u vcdQ +62RTڙE:Tݱ1&eD,}9%eӼǜpn>}G ̣evv9WFn)q,.ZbD+Ft`c ziê9]/K~2y༿mT+L5WX[x ) +voڗ#ԝ,sx)]ѳL/íapG(vn6S^nw-%Zs!wwίqȱkVcÒd}6kA.'Xz~Z&=R gsZ8)jԈ~b zK< +lW٭Z$0 -uk7$,@ٷ^b}1զ'_oo=jIgLguouM?t+Fg`(裗gbtGIr[EV~cTuuFZ,a]57O|!NiAB̋(LC^Wڸj?fI ϙUt9]AnֆzL|CԜ<FZ|@-u@==L =tGuQԝ[ni!W+[*";\̚yڴ֤TO>eӦ6L.qtM\8YĨGh5F%.kmas2UsBӧ{9% +Lcvښٻ(?"[ql@kG`L85؆GI$IY + OrCkp"ͽfqswzkt5NQ,f#y L%}C p55ek^f~ +ɦHynA&S{rA'|C$񰇉G29L7G%}³&⅃9UU=Llr;Rs3V)/&!&w˪8]nL TzF,BFrc:} *#־,yPyNjbySsii|:cwSVACd]+hI]Mqi 9}ܻƨE T4]H^h纠QJ̾!_ q6[k2LEN ⫧9+ȯ #PA„BkeG1 2-؝nFhQ|EB>T ^~~OaS&No28" jl ӹkv,ldn:kk_e)<\}U6/Kts|rԥj/Cs~F@e6_T !wbK 2ቚs?!*6 }jsnQOQ9Zl!8lG\"P8";d`3̵[bX]Crw=vP+o\vSαpwpe-k999S%)z]"|}" n N 4J`ݧ$υ,/=%ؽU !Tgt{,YYGe-^Wen޴{Q9-^|yZUeSdax0z7.҇]f]G,.tWR qɗw%K% N*P'jw(c&7>flҽ- de4чyr +u?o mjO. )]϶m͞|$sRKic"{9f+5f!WP'7FF0WQ4#̅co\&^-zxuoM^yuۃB8[}Pi~nrsAŇ|D$ut5~ޭÈS7IDzQ=H"x Ŧܟ7Zob6wa~A^j)Sn~֢_x&^}l[n羑rz,'W 6ϠS:(/wPSB]Pz&4֠Ar_g0Fᚱ,&R <,n(^lkߐ?q&Egb7S(=P1fs^(.(+1AUIxm9ٽz^tY hU8֢:k.t룆6y< $=L_ٻ1ڸƥӟ$Rc (Vj?ì*e` +Jʨsu\ȾYPTwznd,_CX׏.V!,oߜ1o.z;7G\ (NZj'FuKazI23݀ލQQyAIWy/$ +݋c fNEM>GE*qs|#Ro(1K:)r- 2.lvC@6=`Dj\U㑋#r|Gh,$fHm ի{.guTQ_(:[C+Io7THPB1*+P,~ꆋro1qyDw =rzl e,Y 5# 5~%>w*?Ras_4,91궄j2J2hME#G/إ2ܹ S5VJF@(n-3i z.F)P6QlN;1It5lebvK93`I%?#oSȲ{um bֳ=Fv{v66[#WZuHr;N"% L4.42*B3*d9\[=tS \ vzp{̹/[x-p}Ŕa@-4kZ(unי]sz5!D6 P:y1紎 %9I}mqF쵺MHq|͸q 8:!@ }nk~\GqBOuQKLe^V* 12 No hUA@TQAEQAB rרT? Fḧ;=hq[..f j!iosJK=p9Ŝ<5oo@&3$?eH+n=WH:)T5n[v+gEDUfQ V֗iIOeCn-o۳=gf(?i /F:=PyPj[|E;u\CNN9/3_٭OɊ|{_q3F{u~\t׆!hĖe^9- /qǟQ. +Dž=Eah1d/L篏dl]UĎHfx)S[{&'r;,grZW늦?RAuG\R/g=Nԛoj)(s`UX?E%Flg3>stream +٭oMf`sQ&[Q: :A{ՏZ8LҡyS"Q-(ξ;c7 s{@`Z2RrNl;H$)2FģT%\ *|H*8a!(.98#of67~RkG~]h \b ^)r3oN@) 0sxy$&5vpOP޾baicS/$c;“"W+`]Qxxcu;РZ^b-hMUB2B֓GGdy^mz=f$U{I*Z~&A"fK?"#+*!o_7j-l]|Iޯ[mbzNsܩFmwjHi`^5K!B|[";*ރ@ -g Pk}.o75t]9q"l/r YjC*vJm]Kn=v|򢐭jgZLxy6{]s?! t@x+(fV|NhF-މ꾱N904ԌL|T QFL2JQNI,Z=ÒAG<دI{6o/vU = +n-hnr K>("F~sLy7$eav3@'{3Ԥ r)bUq{QoyΟolvhmEV1iM_ ?s<JD؛l|Ծ^7{ە`n}EsK~hh%/gKGLrT+nWuo XL֭d>pT̤Gdaw+,߀3Uj\|Y!]e5Q&^\D ^Cϼڹ̉"_n~n{w|OɼVEJLиZLZ;3@߫w^Ȭݥ|ܮO|\F2U3WtʴrÛ\oC~z :0V`$dSi:uO[Ï*2L{D{gfFŹ3:hԶ j8Ać(ssb?w~vl\m1|l h)!uXgoNSZ#8&$2+k~ᨣn?$';y3Z0R3VV3f˹DGmIe (i5Xsf[r_71[1&$4l\t[Z'r(Z*5bj-ޯ줺F-Lngݱ@ Ϸ5/Ϸ"M"6_DcElg1ؚ6jvޘԟV}ʜlzίp>ZV]ʒTJDR̽A(~C▵dɶO \9wQ {CtS$˙k;BTS(՟ QW>di %װ"[`_A^/_J@):C|rm,g3NQːed,x:8fcSؔk7;=Wrt +!,"偗/gē.ღ oxqXタm--@V7诔  +~" N (RdV[|3@ N.DiRWRЙx纽TW٭atoÜ%.W_zJkp@*|m&7΂( .@ _vRE (wDǻHgひC-uJNDQ5ۜveܷycg,A 0JAa[ϻ~kJ(H# J;X,(1bkD7Jmq1θ[=]t۰wRpNدgl.hB ukm@d#X uP%Z|x/xUb\5Gy{V Dgش ]HϜX[ vl ,. .jB\1rSP[f ^Hv#X>ƯԘt{dxm^h?k;ooR5\Z2:}aO ,PL'W@<#BC68N"4a;w6( {˓ ]V^9@? o"kϦ?J;ٹGPOKPzxk$< a' IZF'٬>[7%*gX}#HUTal|{&pLdzt7[\o$#+wq I J: @breҀͻqaIHZZ8 kW͸<8 +l:dm"{GOpMLG{u:?驫tf3Vc=:)2W SIb7_f'GTʞ +&ΗH8Z)2 YȾn uO$4_+tvTBک[vzPeuF8O:l.IxtUrkO->?T.'XEdٍӢ{=f G*l,KK' + .Ks>**y%ai"zk. ܥѽ6|t!{i?2XrxWmM^7 +׋q>uIO +s5~q-3w?`Ù Rj8tÿ)+u -aI>ֽt?)6m rzR;>-m<]u[VN&˸vSt7Zz$? o;)4MxS?`k'o"(H-(|/nTTJgPr̵YW'TW<] K7c75hp5g;cvMt}U38<--o%u+^]Q3Pقb[yo=s'{Vj6WB\sg|cηI20$$rs>{suy;Lzӊ6 Kv?P$waWd+n|E9/~Ŝ||xd瀞]q_#,T![[iY&7RY̳-ԘZmzT>DIZP*AπRmy +Zai6Yx|e%ߐ5<ԣxB?KuRH)/nFSnJ\Iקu,XDMuMp5?W퓥'{yZ}"{a/ ;=9;e6ܚä_ Wmo17vE%Dw1#^O[} + %LG`y 43K::wU$ATi/[]%~mc:Y'f4Xg] mL/&;ids&o#E_iRy9B~$-w 3ߠT RR|uW(c\pjz=cAo[Ŭ'jPTK2 on8yK +}i'n[K>/yEia?crP Lw%P{vLO=9wzވq/eꯥ?3ƻi] v.1U36qjv(nGIDTE18k*53|Om|H-mπ#izv +=ԹKQ3HI}7ú$Op0k/ެdn.R`&|:{ FJ@"Ѓ1:,R[ao,xg_8?_Km G6 S%sWsdG9m+MR;&W =zO'qq'mbN:fj4Vz̼SįIWgiDžԣpx$N(U}Ov3 -x|jz7ձcyʂP0N&=cJfɾQ+|'NAB49v58սxH1j&b\p?fk(l?{ll՞Y?_LG@ܺdojD5)?Kn<]j;ʞtY}LN%/HRoIENu4;*ݶqiX1O#`2,d[gݺ"L஭I=ۮO9?2̰Us[ R(4&9-sMpgðK4nW}؅6tiv2h5-,t֭?䟗6Sqy8ذ!eV`\d7RNs!"SM|z$O~#N Bu}7)cĎͪfg,fT]S6zrvȚ:_RL1ٓBЛRiU1u:l sƙ}u2;||Tܠ:8x +Y;i?K]^]LbGA~ 'a3_R mu9G4 +53jMgƣ,ԟqBNdnH?X3i{G}d_TA4+&@2~ CWE6vq=4G:f U3lԻ~W=šgU6U+dV:Hd^LR'7Ы[+iAm]"1p-lǟ8g2BMh ߞL2_uaɴF3qrZu)vgm*_M*:,_<&[A7Y*@9v4 VA `V6P*B3 @lgZoPvz7 gQOA6ȵ[ +j.^a7Zj>#k~qM(q,6,v`4H8P9P&@f +.@' 1(V,4ŻJ]}{u֊ ~BhI_7_3zYT{4_airg?/99" +XnrF,_J+E}A?ا\zK4ӌZo +6wqKϯ:Lifu+%5KmNGPu{I?xBL`u ЫLI&z/St4ޡOae^¨|E\ƥr3w=P?--Na+#' 䠟ubV8,\n{@ kzo3OVA˾=ڟi&ph3Y*dGشt;yN=g>6pY+=ji`"4O $=cKY>se “Ӡ.[o0txɣNxyꬣ\[%+Z@O! .d>  +Hdv9ؖ/`" &p88M/u[,WwL5Yb߃2u'Ѡ-[u;~@돟~UUV| +^~:$LKw^o>M#8ZuZt[gMF AI7ww'sU'=)d6ϕX3Ԕ\ae^3btiT~@; `S{ J6>A:ywxRR/پ$cO#'/K#fREcaRwRvMCյuʵvNvW ++ QXZ*)Gd\ SHP ?3fI6T}~So24ؽYwVh fcZsV-n іbl/g}Z7j*Axg%}kv8+҉# ̄|/sT[V<\Zm=l*-t?}7`tؗrIh/C0@О݇3y¸6sձ?v 8ѻڃF]i טLOZ uV6Tn[zYvdtO"47t% +Mb|oPE?loWH=F?}ÆN[`9%\C#5`*"TKtW*dӊ!8hY=gy7\ؗ!($q~?+5O&v֊;ǕӪЭ^V|qc,freLUWQvmg qQok(eF\`pO%AnBRyy^굮'אV&}|mш=-靊 M/H'+;11f(=ZrnHi6qqJ'%2䆩﯀ 풶 +Zj&|;ۓ6N.sNG"majynVC:yzgBş^^SySI3XG6Y02Zgp<%{t Y3Jw 5ʃ< @/ӄ 9d9(Qwlmn+ss.-tDn<ޫ8tQwf=$K'sxU@+L~fK豌C!%Q+!%|p^^ oĿ !*qˇo1?3GGçwFw_tciъ_Uw3O}VH[3GHz(s㸷:9֋Wjtyz'Y$+T7뒠ۉʟ_=+(TR6xmG uN/^ktwF}:gs#m7R!owHQwo;C\a yqOy+y҆H =j˒G2޻l1﯇{zUyKfޣ7΄x٨-.zgAX˷[$K/[;jɚݶNF[ϗ_ǖϰP,P Րp8Mc;ҐRh1U)Nap4AX ޗlA6v}1=20>,vv#VZ>ѣe$)aVkBjQW$C3L %mӹW߾d7"v)VOwV7_ HZU񑹱Ŧ YB .X?718 `:UomfYzs x%hx?lx: ol~V[_q՛DKbof}fܢ<xH3?>'Դ.6kJ'źנ) >יr{1kVmݚO5 6 +JT|t^˦KG/}oϜNaZv.us &*b<ڇ8igqwp%uESJ ^fŸF{1B2wG< :  E}_UeԓL +T]{oP=oGg=uD~}qWy'3۠Q:i9Νks +Ph@J84!25ju΁.} D)\-S<;mꕁA4d9p~l<nMU{],VnQzw9t NO@G/FM:yku8~w'6(Dr:M +ַc-%VxdNbԻ1T ֿUtr%@)ꢓ k'hlHbf-U*]jo7p46?鋟 }Z)m _0F>BmN$J45njÞUSzm_nZc[flߚڡI_b:;6$jRxmF ncGPP>uS69sy^|5U DCjsv겓`*w>M_Ι;XH^ cgh̽H9mqK'R)+)SZ"YLDʝDqzx [Br'V/k]A!M}^+W~)}Ƒꫳ0,jj`e0ob/=U_}.NjKz?ʤgiTh~c nҼ>(_30Pcpj[`6)uO  +ۼѸКtO-?޲$9+]YN\}A4#3DR昕+y;laN:/8. Rkj~9='nf;wT ӽVOi-ט> +h5%wn(Qˎ;ú +Qhfm +tu+~IZ{[wڬ:n.XUnmB$p^x@q}%Jզ(,7_slc_S~nb Wqy>%8TI +M+^rj:J2[f0 Ɯ.)6mzUF'-US׵z{oWųxux؊q6;j>St^+X4^wW7߳#/N+RqgZ~c8ҤnW<9;Yl[o'fZ˙qdTE5#$s(-pnR#ɇ%-LT(4 Ay2ZiJk RhRa_ 5ߜ:W}xzkMJKMflfW_SLSw)>lf?X7\Fo\\=WVNa0D0 5:/uX/(_:=D=+7Vm2ZLe؟6]PcyCӆOZnĶz(}*O-Nb=vÕadk^uZ;hګvf*.P2S*YO|c +_/ª11tF9 |>htOO{L3;ci&m:ak|ұ4WAL?xY { kXmD4-FeॽlXAld?. 593š\њ^Iy:(,5f;- +ev}=ӏ~\eA)O*K@H)ٌe'[,eG~wt@9=7¶Ʊt #!/dMf;G+я=jőYy2)/̹XJBA~>dxIseHDSSb9bNPڠE|;ҳ4'/9fۿd}#w.2UrTSUV0|j|S))#b9>2NM_c[=bH|C.(J')0vgE~<@A]Hk<L3N;ȍo'AQ/7,TAC0:Ȯ'v1F7TU1|~i~ߚn޳6 {VEfHqP9P+K5 $ͧAn3;jFp~{E-9:l57+*mx=5H=xX?k闾^|Q%+^RT+[R+>o>jk,jtxX+o)N7"RX+]گY{z4xXdCM-FS77IkgljSOh 땩lӆɦ`"v`$.LJa +ghSG<ɢ*U 'ܓoYᎤ?hS{>ILt粪J׶EIAD +"z_͚'UMaѣ.罏˪'#<{n5vO ɋ޲"JSc-cgb ͧaxAK۳:h{Evg3>ƊznfF]{f5Զ@YC8%5;~ V +"W5O{$(\pUO$#MܢH}UQXlK-7ɾ.!{t k|*7†/-ڜ6q~4f X-oh=QhU(PL#4N(Fn.2&7Wh85=EҝV\JQEp~7:]tAܻ678 /{&k>GlOi/!TZr]x??Y2kT$#?zMHQyIh9joAxS^RTzbխu -S1ݨ21П/ F:lWj.ȡ6?9 6ϽE|-÷Ѽ&@g( %9v SQ!zퟟqms2[- +U`⼆/\.5V)𜍫Zm0,=Je+ߌw5sч!'x6?eTmHPL/Lq>q/R9m1[ݟnE[w=%_F ҳpɐK +wv*zd34d n?;Jv}*#tKo _S?]Pۗƛ˲JKIgO71{'XnWU$g2; qٽ\'ek1y +\.R٢t3ԩg"Dq!'Nܾ#S'_H=&})mMU8j-AKoVcTZ6cʑ[˫K n_+Ʒ'!ؔFʲ^>ZcfҮ.NtG!f + ^ eHƚz_.ݍ7/,D*WўoRQHypBO!oftxM<@e*"W$8{U\Zm|ܱ>a.LTvhߤ GoAnq?Gn {Ln*Axܬ;_*΁B8ԯAeGN5tWTB[z#Kt!?s[5LfYHcFy2bL>Ik Gu6V;8jOw4ǿ%=xn=n}[":J%V u|TmJ["S"!=̄NtPцQ4%DY݂6qr`<ĉmƖW7]/Bq/& ı}bwk|mpf:zWeYcYdC-J 6V~x+:F%#SNV;:7(q'I<6zYOd:n5ȪYFe5S{JK,ir^'`(玗ƹZ\Co8.HBO松9^Rz;tzdPLSʿlۣEWlNɬ1f^Իe+ +uCId۝L鍶[7-(Cs<7rh*cfPzLYV]F+<:t OU;\8l6~`fn5!2lЅ9iCK"jJ:>2J.*u&:FߪAFC}xNzL{eQq|S~ڝɑ +l ۵na +F.Z{VZLfQHeS>϶LJԀb oKћp!]_UJh ǕMv0ҍ|]НsulσX?;;ifMo TvϺ^%kf#cqm\]f}# ¨Oҭ) C%oߵk؟t'ЩJ-7AkO#70"R~/1E#s< ^ۿ`1T]NJz BQH׃ a1NLM^e|da\o`C:rɫ^: +;Vɳ-f*uRGHN-BUqlUK$%Xa^>D e=E녬ﺸÿ5 FhvVA&ZIRd2X\JU7zFPmd&.ƹ,&A]mL"R7jR`:.jI`af|hsX ]s%>:kU +ɨʓM.&Xkg 9'A#@iKP)11`<-3,Gg?[~!5"':^&PZI$oysB'z sO~T{bDRoF#!{?qHh<@2O^I=O<@ć< {%@ :?I'D!|a >%_3hky!@S7z$SiE@8R * >ɗ_U8p^ =-L#5s U'?Q) /`>5DŇTm'*w4@n4 /@ +@nl#eOzf> Q9֎ǴpcEeK:saȣ31áw!!~_H}ϴp$+$:l3e^UJC<'Yjf" GosG""!.ܫPXm".M#} UŒB727xPb%9\DNe4u'J0vj +dv pݟ-1O ]+r>Z!a8$$6fe a^g=dIRO I&RaR^~9`  +|~i +BoTgB)aZoT{{2 &Q%wuʵ5!R<)Yӛ~Ʒe8a>cx ?ZF)2=w~r+U||֭G4An̍W`ZgWWl.zj㛻{_lsv>Zܹf/H吇ݾN]5 sJm`8=rsl"[-OJ w|vfuĜ:˶x~_ #սá*]ڗr~䏭oN6urzhj}'%5rI_IFͤJ8#=Սϻ0[.O=jyն"Д:Y,]#o "~)MΎNOvlZE=&z8| |[?|\8zZ=~!F 7J Q^8U2ܦ~5ȏGKŃ-7oƄ,W6{r +icngb8uZOU&!ix5TO]o[Mh/WP o}Klԙ#wނޚCo |oS-DO O㉟(Z\uouOk}Xvs)qyP Azf}g[%TF +ӔEuI+Hl*߶08Oäة>On^Be{v3F?H{u"?gˇ6b-,^f)&6 t4(X+xhYIcG8De~SYV[:yJl釣HԘЏIM'-I4!HbNwiqƛe +{X\=nun×˄~7󙗤_Ka._Vm\Ml8Q*)2e90t[[XnZRssf.a{SFpZT6YY&c,JݲsKU3Iov*6ڝӸM5593r Rk>r F$ZmH 8(Cޯѝ ͓6 +n_XKy=߭:))V2eOpysH,kOڹԨ nvpY&GPG3fvXN\ȋrW=IŸS.y4;`lpݮZVL֢;TBV&#gs|k~(,l/7/xNMhk=*'CtoY 66}d9z9uٯ:"M}Vģ͚ijԦVSkf׹Hw6fڽu>NfqH$7ltH'I4jD +B,%; [sԴW(p./̍46>Mz:mA:nXQFfpZI&xg갰K:+uJUtFQDpO&fP| Oֽa.TSݚۖoj|& 1 Gn}nudk}k .T/O"N&^+_ Vڳ74gob}6pTݺEb5.]^ī :u;|`Y-ŞQ`[8oJ(S.u a_BZn D +%QneuI49vn*Y}^ - O9l5I~?@|$؀qr7 K1-)Wds{O)d=<M]=!4mtwӌJ!]Wukd|.UKz(%8c*ؿJtq%*zd\慴Q@{OtiqŁdL{1($d-"Hh46]fiS  Gsf4}={yW˳1h]ap`$LtOH1M.@ sA_5m4>@^Hd[ט{Po&|y5_ΨV FvC?w +a,p].!(_HjӪ'*-d=1@ 7bt"@51%}*/u~~ZxSZMlŇ-N/"Tqz7N{N'smmAR4He.@>]IOvmAW7QE _aylUc9{`}-C0/--4GI컛S7b ]$!\nt,v;୐?`YH?ߍ + +\]|GU J?ސsKYZZ׭++WvT/j=<ϦIYqe~f ;:"pn{a?-{ϲ/9fM%s5..\KCwh9OyZJ"y.S;k;t?rAmLϏþX9qZt{m}6-3'X;Zym4wߋBub@`v \c߽wwm{W_[ nZRb;R?:0Ɗٻ/tmm'Ndc{umZ͡N6^k4*ftn_ JԲ1Jb%j=`/8hnS 8B _uktVBt-pX3]J^w2H򷭶q"@*v?7-D'Kg OfF 諕@ozШu.e/.PPix4Ox$hq$_K꡴]M4 l|YcCV6R~#niY3>5\xOdzR26 +xfq19^K,ख!opoTٮIdVN[^=sbÞj¸ޕ +HOs2eyy7ʩ2nl2}n\ mf^¼)_Kz<kz#qöoJ#lG۩oJI=gӱ7|)٬:Ԟq|j \Tٴ2&JfeJ8TZ,K ?[IΗ&hĬski Bew_֫+M]#bl]ҽw`><_qW[޿=6r&_]q&-}$>'wNzyM0Q\1=OAf= h~W<*]/jX ;Ivt_֕|jA/SȭjoBO䫜 fAIƺ(jSx$umCCb+Jhph'ڥy0?/У!7qil@%4dWMחl+.M\?`jOuBk*WyHǺ*cc9ufSӒާ% ') ')){O~!Kp's~ԀO + \Az8j\ߘ?ZrTX/d3W?GXgqbE<̀=JlyLeHC~s=Y^0Yd!szPLIX-DZmc 2;l1I7ɮ%:Ȉ\~%Sqy8]L}^U,IKf4q`0-nw ^QS91Pya@/ayJE˒ZgjU t"HK*l>U:/ vƝ}P?~163yC[!\#͌4_fK)1M[Q6?qhrLua\ :!J? +['7fRgK:{gCoExHUDҗ-R7mp,Ck۱*l'OCY}PWЕ}?:AJCYd+n06ߡuy &q3_$;D8f$r΍GLm<na #tAU!CFx??fdq4a&,6]VأZԧP>T ֒Iܜ`3y#; 6j1Y$=BaW"݆nwQ]y$!^w_xhxsjuG<חr Ԁz)DFH9W 64dywNN[Pb p!. ,cf~@ a9P]{ iB4i" +51TWj7jod5[˥^!,nVj>=D{%z@"ԦD6C<ڈY[gN̪y6\y,Medߙ`jDgFt3rTr0rmG4u~|xgzRhNgl...UkCwsi)WEVi/sR8ƙ,̂Ӑ"FC(m5vK=ҭ#aΈO"c\7fkD:ǃ >ƫ-A]7ZɏZB2tzf(GC}.?4͗NDJ6zzW +կHBa^1Y32t>VX~Zl3:[Vki1l5VL\ÿӃzuJ X1< Ql}xa3Χy6Ē 3!alerh{TH)l׳cKGuCRM>r[zag=!`\#@U.S[7GıXA[*1lvWhiifEIoĢiqpCԡ YmPCgW=X!\=ZuxF$(( '-Wms;!:܊P]g."GkfRkG,:dZG+S \Oy擥r]8iGKr9Z+\j)Z뿠}Y>?d3;N?ˁ*et46 smXn]oO-h-ߝ%|X.evPnTI\Q/B,=iks~j>֮ {? ڗ%2xZǍ =ܨDnnNN%cc!?hiv?w4ԫTm{`4td!c@L@% HT_M#`z瞞q}A=fzvwBYnFzu-{Y f*ݗ\Q `}x8#&AԉJc3s[==Fp޳ta%I +}yT _Tpr>W#\]槠MrD#\K +=ȏ4[XD-4@f-*4xބx=:8 n3Pĺ`z$Su=/<[/exErSt)]X9ں#b"*@n@5UN +Hd;dy ܜ#Mr=.Jk/I}Kwz?ήi74g/+t{?ూ46t<:@I4|*suS똌oySqMiYnj3>l=|vھͤ;Q*u2eGW&؃፶># T9WHcM!]#!xb%]8/{`wm|>Rq`&޵\twҍr3yQmBJnZkq!ﱫ۫9Xb§j" +I,y+LkڕS. +B?E/EZi?6Xk)~ʻ욓9xSkxHˉІ +թ3')cqk[&Bf{)NQ`1T[L׍0̼hi\.]KŽ2֞T'N]Z.}{sLIʭ?pW j]VAWq\lڒm>&yW`oQ&m!cֺ1ukrb1u_HF [\6։뱰| ]l_S~d_VKKu1`^ڜ +t_UQo aM7%,N="!-gDKnXS;ZO%ݑH9an0asx x/QۮYrPBgo롂ڼ\{>ñR^KYVVi>7ݦ-G2Fl{\MnC?ܙGE[6Š3ڼHG@FN#a dIX%$y*)ʿOc.lNe~V~lWwEZik?޾/+&="|t)wK.菧s̜&mxa[x4901:"eDa~YJ/6ǾcߦN㺆jM]Fy~㢷,fvR}1N:" xtnHX9/Cn]q@[ls bs.OP.(-ܼ=R}};;VXLԝpI\)s]M'h}\^anG30AۧLf]i06E/9Ƞ}~}Y}]S}]Nc4בm^=+wc kSS3u ?h-"3hzuƦEڠ +i:5>zcd9E`ZK բ5xrn'߆zC~^e7uΪ묥gYDXa_N_>*(:y_v`np<`5ճu["[gQDgQA߿!ng\|u*Db& ~QsE֫wyVk^1i.ꔒXyza+T[ B2":É Yq-7_{#2<#x'G(pNOknt\c/wq""Kvk}7!p7NVZk#w*3x{g0ccߨ`bZ-C'-s1lm}kEw:euvt͸>6Y{[R/|څ._VuU] Ϝ`60dpx'}vHq/LszZ>>W:;>ns.UMnYmֹ8GWct;JTT>lΨT3vN W"Ai?+t6+b'm{]-̒VuD䛧lnڻ፶]d4C-rPCRu=*ޱgUd~ɢbzBV&'q<gHp-wIB@췙okʨܪl.)!ŬS8T=t-Z|:GR4VvS)uY]v|Wf ұuEKDKzHElK sSyQ~%=c@7Nt% iH23xYWjO\԰*WpWЭa{%?ŋ Hh+@'/*) zqnJ9?Fk?/"Y[mzX9ڧfnInbINu3i4Ks5%Y}oP|VAo$@:TI@`x[ ,fߛ0[-N5ߣsz뫺Jj?tS4TK` eO$n SR%ރ$_I1Ւ%p*x{"hnks*&f!G[aI2yoaU(gmPiދus_($ RoB }>Mi޿_Dٹf?n֡u#XG/a!1{՜:+}k`:~/^q2p,#X1Tt3?Q$ PH@R2wZ]Zq~SƟ00z4ゟgݮ{.7"n &]'G\ +|i'Y>|1ժyQQ^q2K- $kFRliSv|Ir@/,P恥K|=mԇq5ݶ xt oC`_-V NnT +@;E|twPCPoRr6ó} R`|<;{=w9(W#0hgס[^̀}ul8Bdkm;WSvrjo`h-cJen9 ~&xz@ߪ_[j'%Gcַ}eQ?Cz{=g9}C˷\EWlXܬZ6Jkꅴ7iZuZg\OUn­#ǟH4A-)C*o=A|a]W=7m9;U(n/7jo-K\+i)Gب(R^4 W7/:TKL3+"^"0O9>})(U8mY/6d$skkWK54O+ym 0%\Ktu̫T)\练_3GeΨIe^ssRRx]/:#[Û=wZjA(1ʭiAKvh.p6~(T;. sݮžfCTy{7n˒XË-h6!Co׫3:pc?==kK+[hf#E&DC+_# L/D3s3IB-@9M±iwQևw눉P;qɋx'njf~8ڵh鼈|簣{[`-v]}o՛RM鳄usG'E4vrò-2\Q7嗧ؼ + IrEy@br٫$؋0dnSau:Z$rmV=ђǽ Z+XW5tDCHƊ/OL=r)^ò{ғ-&;\/dž%SJRj=>6.A;_aQ~%t-n_VEvZ^$Js7Wj*ѕ[=Ex^Ua;x.|?jttz$;Յ5;u{Ie't&HJ";hzzNkG&mRk}%U1*kh xKL;p2~],( J!OkVRܖ(zMyO/y2:8 +&t(ОQ`I2<ԿrEvЭ"zWs:ks˗8 _)+)G$v}v{ wlR)lmQ4RM<;ӘgnYfP4(t\QN ʂn,_o͡q>0 z/;s5 s]b@7X~;W??5{0t׭I{|[CFCrYih`0+(hï7R@ROnK_~_B#QZ!7όVWfmLWp#{h(g&r^^EdC= +ֶ{[Ɲ^w6%~E rx!(@}w9涻`-m.eeIs3 K궈yjP(F@Rkސ*ۣ;wOOKf\"Q/}J$musfjh$_z(`͡i^YZG9I,޷fo&.uW{wjX)P\OEV2$;X.:'g}"{ƃ򅫍R ]wCwn۾Xxe<H7*7igw[kt-]ՒLy(#Swww]Yh1 Gn#aKODp-#l>@@` %~WƵNWǞ怡/>@D }G'2Éþi:YޚiZF Dsw4ݮ#9d6hxqJO|?:2rlhK{ +^ MTx+j +B +j>[w[=p(=ގg:<b ڼD\ꡬ:]Pc0'̷qZ0#?_#5`\IY Kma@ܡ;AT C.b,YV3zMGFC6K5k8pT7D.PWyvJ˜*{`ؐ41&U+,wQlw;kKgeMU:KϦ+MWN4 6~R/j3?׵h Nk18I-F҄E.it1^gPx ;l]w.Sad?MR_FFU;4X (`T_ 6cV8|٩YÔV?U6+&.+BtmU ]i+j:7v_5B-V ~ +seba  +Ees*C}T +$̧06nr|aIfpKFM}5/w9-:ض}kCx|δK?mp복'Yu|_fqMZwu>jCsde.=:^*q|9I5!)l_QF m/:l~(K[n64$-k8/ uau+`U t~{ yߗ + +ɸIWfg_]uE#v qYI5k/QgQUϡZĭO4f[,yhV~| ;}=!ՍK`k1`m|S{m+/7Ҧ_>>oeC@뗫7 +Kmtb3ma +mN1гCx&[5vIAf7x{7RoK|69WYy 8Clr<3Y˝bv|׳*R?[D9ENgSb&!`aOƸΏyy38Vi.Ş{^ +|_dͤY ,ݚXm86j͘l3 7g9^FOU^Pw+m_K-q`(FUa0u&[j ٍ},đnN|?D0n1;ВpnS`{b1_9ܮԀaFUZAổgƓxEv8[܄&~ f Ƴ$dv,|fԣ p{Ak8fqmf <ymƁխ-m673BN ^@˟#bM!+ΙjؿF׏I3MxNz[e[R{,3-l-"Yֻ㡅J𳂶)?C/)pziӭ=)m*$^&%g!gsNF򵱒$h­f+Ň;wi ޛ`nqy"&$:aAy#^T6O)FI,{jaˠܒ5XQ294Y 4Xv]Ձz֦J=״?q ~=wԧ2n&pY=yty.\'Ƀ*)43[<2>۩?tmmkկjCɘX!J尕UQ܇'BUG]79n9~Iܙ\}|XA \絖sjKe 8Ew8et/% +(雒IK 'Ё \wbկ#!St@z I;$Q idnvAm_b͉aalȿiQ;35mKRoT}lGa-c f +5J];n}e O\!a^jxwH,߻gg/NwF9ٺ?/?JdKH4iP9@$ ӽ$pTv#9NUؤ_u5V wٝ^v8rgM`vѨ6m`Om[[6xl UyVylvXGYVJ+sBVOc'> 4"qKGowcwUѺuMv_yv¹s;1-:~{5mc6&jϯݵ>J}l}Fzz[Xm. +[t48xo\1j:tK"WAy7;MWfZ*6:h 7~-զuh|?T_PojcumɳJ&P ߛ{ Q iǧ1{fUޅx *TrިRny7eBʧR*oᇒ[8=KS.7+ɑX<}Qb `^XN_Ԛ,Ÿ)AzNx kN3q!ݨA}othaB[ɭaRTm#pXԤj~>8[;#C7XO &V N $ǚ_ePS;MNc@2?^fJ-ǜQf{Umk%h䢻ʡDsdJD Uok^R_NJ1c%?d{`-샠7"VTUnjb +(~*_y\ӕGn_PEH0EldUilzۘ,HOنG)> +&$uRZK%̋ն~e7V6ш RB)NxXO9N&0 E)rԾɰHLlNb ]!rItFI4c)PQB(;Bx?r38,N4N$0in7'A2NR3jЈgyo׹{˱Ɠg;zt^4{C/G>aK _~*rH>tҠi;sTЕ[>MηwyG}9*|>J'6c:>Cr=:ˢDbn@W^06}k`U7Bgo#'%*V .A.sh>d +KBu3ςX Q c6W].g1 έ??)%K4& u\{Nry"JЏ{/]aQ釅ڟϠ?0\WÍOb{̅}7{=n#NI"'vWxtyھzxCqS]$$wn)g{ѭZix{h w4ސ٥ԳG'oN~<"y~X[oL>y4vZqjٞ*p -4Mrf$ +qJzO bb$;œ\']<yV|hBA)K?[\~7x\PY!Mn,u=s >dcOc?X4ʒ%ɿnAɯQgjo:E>$n.TR^]{;s[)o^NJ +dt 3=ΰH ]i$yZe|WxZޏ"| mr}\gCZ4D{<~sor Q 6M[}MlrҮ\v}L}2k8Sҽ Zӫ6YO(`r%G2{ZbGm]*Fj± }3x+> \#fsc;Rwr2/tz$jFJo`CR(ZJCGݿ RvfѦ]DLnW]0ٺ-PiD癨QF8/NSas,PkJKoh5+?<}Ԛ؝F"Or3H3ֿ$p +rouAU2v.Iċl-B:{loյxzsTrFd|?'^^:P=M43UP(S?]2wA4‰1 E 5+i|y0M.yTؽW1^wnF6dMMBZUm]0Jo Y *Z`\oMe{J!vC3;8z~ꨠOԶ@Kt)ƓƗ]8S|4nEIYE̺(ŏw}zc5ܬhw-Pyt6?|J"Χ ;/LdE|YtSf۱3aδč`]eGhQM9MY2ԌOC`m},pAq} JN[Ylɮ ?]!X(."F"_^f>CWdXOe딻nई5<5d+lE7W^*l )ng܂AաzWAp֭\IkҫC<՗қ-aB5|} !Al?T^[/ˡ1賨ɾD8&B^za_>Op)i@ɴ /ѝW +.tP6i֍:wv[RdOu~Ђ>ՑPs5M'){jO4[i"ڜW^ |M|֎p{"VyCw-B@nBPtKn+\6InkwnmW*z}6{r7\_Y58ukpB72_^o_[0Z,۹4?oq^C큎sk2.taڋNEWVf)l:7٬*ٱӛt7e[fpZ՞{?V{fX&K|E*&] M@ fy vyu?Ϭ^h)ȳKJ1K\7G1^Ɖ[ Ygıezq<)N/22l)O=BRH#&뽩>-ۥtq .ZdtDJ4+Džx@6O?Wgie) O:5dxq888>i'( u+)4]> ub"nu|kTOGOpSzGB{\X:(|B+4\~|Un?_"1o?GI7Jd'u>N9h٠0&EwAvQx4^{r{V9>q^=ߡr"_wMmG}۬wuL񠕕~Q0|].49, +b}z>ku5L<1e3XxsOS\u^kZ!yzÔ匣Z[T5ao\^wy{hۆSHC[;JsP? s'vF-+_[rX lϭ|=ɣd5ڭ{4%ev\_!hhAK%ډ%9 σ}X [r/\ϓ/bKkr;4Se/gw#ߥ;$Pwoo۾\ǽm[裝蜕r/[lrў3t3?mxnIInp%y6+k#X#^@rBi4·&^HK}E:-?[5 +Z0@A@EA&̢A{oz)fE<zh+8^` z~]QYM^z+SWnMkt=<9D[97f;^6\VBS{]8;{Gt6i>֬g]ߒgx*dcnƸ;,1p<1X)'W<^d?{P";^G*z}aBƁS8sr̼[uM >{e*{$D /8 Fc Gv!F[R64&}79խNUԡ /O%7NQ17bί[svǗҜ_d +i+ c8T)Y윰f- o맵Abn/l5âa~:PnȄud^K4{wkȩ1R^q}f ACS[O)dfq|>#|6lFugP`44Ǭ~b᪖gU"0 zQ>Vc$`%7,Lj}ׅ SrS ْ9aAR5ߚtCn亓]h÷>lWQ iH#R{왊9/ܬ8 `DO_Z=H]'*9n/.`.b|$w {sԝsDzcdYQ*4+GW_54_`DO*|8Iޢ&Hh uX@vЍZܙwoA`rhrrq < +aR*6ߟ"cyXv`^ީK>+Uq^E ҡáKn] ׯ06X=<K +XAiv̂ T$2\zCZmfIg/_~;ْq9_zQ׮5'g_3]]'QVrIWAQW 4?p._Ya[KSy[ 9&&l BHmw4=}SUG9^| ȻuU +P>4O ȱa`liZj7Y\'hMvGYyLؐ[%NdߋFv*he>YkӶƢV z8 ƪ0Ek=ċ +=]yvk` mu9%ʈyj I@U8݈9`hfM?󨂣8ꣅ +E3<ΕҿXjZ8>+CxuwVFS%%8I:u.ME*OY+skmFB2.Am XY =`zi6gʿuVt >khUrg7)n9A0CZ?l$<<]KgGs{'KX,Pj8NBnB!dnS0IrKsY[vq!!O!v'DO,ZڴF *N- +J.߄C۳d$McrZ +:2ontxeȜ~(! 4-uNGRۅm۷A +.V1|=E5Y"EoJ{̡?~Z]c_wԪ#$>8qu} !?,=3'ڳsfgbg]oe*IȠ)oinz< s\ +]o;A-c-i1f2S_wM$sّO](23\Z;t,!0{Zez6*&@<;Ij'>լAI^\AѰaO/_kmNW[2ӸLT.G*Kh"fecΙvEviwD;pyMyszQ*OM gۤ6A9R$RIzr3!P&U.lrZPV~w>.y-?6ݡe4ߪSHht{5ؠN/:OشUs#r:Zb"&kp?4o ᏛܩKr{໗*p6- DШ0z` ]Vz2P 1z^|Mk8?spw3 vM5/jNWʽS跧V +X l.lX, )7~W*WV~o:#~xCw韔ҧCPǫ^>l|ӟ{6<Jgy}A8f{  *VoDܤbg#@2@<Zʘ3JF3wi $1w2DҰ '}z{Q*+}, +?puCXKK8Wd}ˇd3;|rXhV  +Pp +P{Pb k "#thP4ӗҴiʨ4MYf2\ʘt3d14R8wy>yΫV#jp?Jt_?71{!]\sxIbK 8Cx;}7Fj u3ʢ973XJ-̓ݜć?U&˧}^ޭë*,m$ }<;I錬ɭ %NY?J.H?̂h<}'eͮY:',zQ2r-uk{z.Qvv +7,s.WgIa\gBq޵Ǽ},OWDWgks!q[8%}_$3KQLhQˮ0fe%M? -ծiCx`T|T%ެJ%@{D>:v-ܯnᬷkT5}ZW yh3݄#5ٮ|g^_4{ u蹼v<4QGtϑA‰ھGC]`{X6m$SV*cIț068lIXﴻX66ڐl/%rc"LGzZB+i0H?~,:iViP촌zçeN:WZz*kuNb*ыg*0"6kQ] B}$Wfwړf/ +Z#{NاdnG̞؉r|mg{/`t=<.uXvhPյ6UyI fu;} W|_FeX +[>]chZ>&ĤGI`MO}U.kE8RqFLQ?~!wX9ʹ yhOߒ6ai39U0uI]:ȄV|PF%%\&jA۸;I;2>s<ȯxNv}<~M5S?%+w`@o+Wl|(bVcl5WqzD%=+0 Nnvm70ǃo.p0!E`iP`קzPxx*Z@^> 3сP^0JwhoJ5Q"]oJ) ֹB5k9z"ϢkXxJ߲M}r0a.(\16I115~P0:ASK\*֤]s׺.iL#dW{SW59o-\ Fg Hų'D/cο'TWw@cay!K W~7H9FǧB +ոI{ӗFJ' n~+DLEY @9SYFC RgdjԗwHxG\]{0ØOJa:VQdhH**z<)9W*%1%dܐP}'mw;R?d/oJ{<͓Ov5rY>UqF7zڳ*8بBj` `۸P}M1V߃2 W4o[΄/oq% (OKjYhQLF*%}!U#๝ؽ |Q;v#M5MLj|'%48nw|ijꅛ-+"kAެȭMIcz"X/['RJxxyyy2o43f/KVY?k"tawNQ|:Y7@[<5x<h4l8ʕK+yY.VN.7.˻[ejk%5IGr|^1 Jv)_{Ͻޝn <7,6f|gmi@zbdE[$#YuY?i'yr)d-SH́7o J[h)}.TvK業/Ϳl>9*LNLYrrpl5u;u4#̕L2Zh,~?w0wmY)= -jkgTsےӬJ:AX$,4>Q:Q?Q n::/,ة`iJ{ŏqOb[l\L[Oi1 `-JH6x4(,C׬D a+oh'pL?سcس;< 3 >:`"}cަ'燽uo}ɑ#{qE(aPg\<ް;32@jJ6TrЫRyŽ^`W`)3e똊 ⻴eWq\D/ cfht ZgK+*"9"Ғ5~z\U2UYZY=PNw*U2ѓ + &&@wR{g/G!>*}4ʐaI + ukE۰^qgC/@mϷUPo +*9߾@}5\Qu;Yo+5ǪerD'ؗrɊ\*l>?mLy=G8 `t1.®*AS͊ ׏j?5ós^BjGպϲ^=O jSc(ʘ H*ئuVS޽ڏpT|dvs,Vw,vVQYk U2*+`.+P֭LrL3fױFK%N:hC3>9b*r)mNA6~8=T4yo/хB>CKD,q;̆iիHeJJ)J^/VлUX?^7:oK|/rqwz8CG"(fi> Ga.{&M]UёAL1xI7&bEJ @\Y,J  @x-#D$Z:Ml3.$VC rّ0gk ypl@>+ǧxMm߬FzTqe@/VPwX'"nzKu7V "٠]"3ZXz:e{p^ެ5hKc,.6uu +&y@>KҺмi=$Z7}| {C/c)C;J  ZydDw 4#dЊ?[|t@54bx(Blh >f;ɛr-6ԣ;/ ,@ |]Rd}4N8thAcPy x&w,/AӗQWYҔi )2Kr|>MZ{_v +O6G$FM%S pK^WMǷHWݬ^ "Q٩?gq:͂YK5fXRVnZCUKܫbn1ٻqɸiߺ$t|wVpvtׇ?I' u<}"s٫d8c~?ƍܵ4}K5Y쟌g<,D>~k,;Kks՟%Jip^휝B># 89\H$^Џ0<-y|(lV; FZ޶W6'Yw/geso|/ڻKeɬ0?R,ԥ(AG 诖g$'|gnqZ}x.~ۻ̇F,}* aԷ?5~jVVԽS^ \/XHϮedƌ^JY8ĚIvm #G|ݯu] eիWrXiwi!>(^0\ᖟ|ZfGgHY.m"vȝn۳fijz"woG=nOI)[Fptqr~.9X"myok{\X%T/}A uN學_Olʚ\НU\HӽPApQ7?N8Gzv;Ƈ/35| ++|+S$++4xTp>dIu}Zqg^DZ?ҏ\a +1 :baש}m$K?$RS$}3w( ܈=ɒf{0i>-^ˉI-+:#LkGV6c?du|gWS, ͞G: Uý_Sn2ΰo]] ;ңUح c9JeRW.UFgvn6`Yޞ6Azl1gDr6k Y0^Ӎau6ϽWiG-v Grƺ~/Wx5;,5wuhﳇn0]dsx0ew;*A'0+`sAUp9S]Rv'9POɼo馏6~95VM@Fq/=U.3E#鏺5̖CA|rMf5r*g{|aq01Pkk0SSO+Cӷ;҉U@\ɇݎEC܃SfE^m\V!Z\f?Ddgjkngz+؂q:h(MPS+A~Ga@]0emRK@xyWIU˪R$[OwkI7 + I7Iksq7.w׼wdXX3z`6/~Z?#u&^T厹;6 +p=J鑔tNA/vH8-WG~/|x :#mJXrRj{ݩC +ܾ4utK;gtTZC퉴&jEzE]*!ʽv-,·/ +Qǔocn=ʌ#Ҟ}K7FƖgs(`sc؜>{e +BMsʜ&ncLb(lM{YQRWsVЗ7Rn8^ĬB~ +=' {i+UmΌN)ڕD>SzLJ -S⌖9C~a 4֌=;%wmvF:UdU@CnP-9!AIX F?'7~`nq ʄǔPPUauJ5z +oTHr߬yqB5j෽:AC;[~iO}Xy;0`kiy'´(U#J;Qac N7.RZpr X*{E_h2e?yt9Ynx05=2esWiMNtjb4 +NvH ~-6f=쇣J>=j6=#{4=GOeOeۅ4nN4֫Y&Ceɘx;bj<- x.qB5/< +y,W j B`Ux1 x_<5p|PH(S"(*;)1J"<㖍 %>o; }5蒦xa W[xRJtoTZS-I37dWs:76W|t~950}A蛄)=')^|JmX5xqݮō!źK7/?D뤃| t eфGRC'~"fy]tMgX#EgѨ⺎DJ bSR{jpupwX*n9Y;pIKY]>i994֘FYmͨ.%)zKy44N5*oT &cE֨i{ p>XHkNxvb84̷H ]۾tk{K!n3"FSxfimj(QwU uƻH QjaRMܾ +U@2%*`߫ҷ=l"BV'e[4V¤MʚPfZ^r6*RYnEt4iʺ}TkYzeוN*fˏZ.O<,/!la*1*,U.mn%>~8ăwΫ C?KvEpv d5~GV۾RMɺc#Mt>-Vb]b͚Tw (b.U +(Teȫq|wӗ Hjgx XPph28#^,f\^#+Z.JnR\MӞ+聂 + nW#jj.Uk–xJ +GIe\AWRտ +P: + tN=a   >L >?tD.AA=㳋*qM}=J).Ph\ 2 N0 E"@ُPgvm`@\VXzH|7vXl#~SVQ+Dy}M⊳-2kRB|j +`dt7~#[>ە oy]hx@OC  #HdxG C~~os+iRk+ +vlU^P):Ś>B0cnw^eb}ĹnvSr s S;NGJFKPCᔱHSD)/}l+%iڟR5W3Gxhq5.`sٽHfxk5=a\h#M>U68 *<{;Z7T?2 >63~jozɮC!]Nf[ [c.=Rt^'z±Fɭ^zgrzvFRZՍkM4A +q,"lblۋ1z5vt_GrNW4YNw]>ao]/L>4z#{xWq'筑D16.nm +[tW嘕Q),Ӻ>=/ZkRT]n;'0w#}A8}ZFś&Ua1ӝ fc3Džm;bpY26H>uW^'WԹ-Y_lwh$߾EJFՐ+ZI{vE͇6 ganZ_|l´(O^[|7@uX[rGV|.?[-(b j_NfDOv<ܠC>0.,֌gJfD{y/6y} +&<嫍p\'_-L*F]?{!}70L@2-cȮW>tMw9lUn͠۸} &l4q3IT׼wnzy:6~4ͪxHIEyޮRr?:27c" eok nkӷ=@V%q`ˬ~;ɇ>|{lS +=YUdR-6HwD8QEqvjvS|l獃h=>`o-*^[CIăH?bKPwzNoO3c&`/eSN`{^kZrkuQ)E\^N,K\/T;za Q>Y+w?lg7Tb[qX4S߶>p[\qȧjZs*ȥާf7zl*3azSe9 GR\4C18=~~oO,!uj]BT9wk4~ Ǎ6NKt0Tu>G~U{sy}iP/.<>ᓓ$N!ih r ,4(ˁNy$[_$x??ȓ\l|aË{C5ZCqۗM͞RzΣ}ugs{Cj +`Uhu.+p-od/i3"CG8[g9.vKޓw0u0*Zq?W)_ +~NcVCs;%DPұC +kP8J͵7zqrG{6ig\3gLB_ņİlW<_ȋp{! ml䰰[hk4cz8ЬU߄v2k/aԹH_ͤ_}׊jT%N)=azoJL4!E/ +.HSC~3^3v심]BfQL߅L MuCZ-P/>uuY^0^2H|h`ozVe?ɼb@E[y!@QA$N(^ Eu3ZaoyCvY{V\EG82`ݞRQ6Hȓȓ(!T䩰bqNQ2ÜD2/UwK}1/%#æRcz>\~c9x۪g}-9m:h3j[̐a#nw96љ>!=[xoNnf}53(;wk11/kvOej cvaV\kZP ")&ugɖ9FspHdVo5A ޫw9N.S2*~՝sIʍО^m694U*`Px3*,3>!b3Mb1` r">Pʥ3)= <8%d +o*jvxX wWWj98)HZW]Ӂ _)| +V,S5R*@؊5lclv*6Qׯvzd$G6N,v`{c + cOv .dt8ۥO;Ijbűl_:YA289U,ŤU6tg<}PE5(|<|8p5SG-nI}.`snLK@ ЇLcV_Z5 S}@1}p<:QW N 6w.wN^ +5hR/w] +&Kw*GJn+ t(\>kd[osN5=?w_D~31X{ !H=wd;x@C 3_oJ)NR4_ϧ:a/>z>IQݦ8>o*#)#UĻ9Y1nα^WlݿzvvK~%ՃzksjAc3C8t!<՘J:eyIA> {9s.Dh|PPL'(u:iOc\ӿzu~lvw3v0Z!-&P)z_˥|I~[Zg6 槳vch~8Ke&ObnB;5(l{ulԚԹUThsk,[ĥ96\߳O\gHSe5ϫ/Ͼmb! )|uzm1*;D6sY@FhyvƳ%.16.8S\9_i(vβ|}\ <_hLG5ttO|os?Aޏq.yf H~;_,6.n)d<4J,|dxec?OG[I"?&[T;75@ߗ]IͯKʳs??ȃ]}bq:5ߝAYw˳9͟fԼW@c?Z!CzI79^ -k-[BМk{q)>{uw >6"1ڹ-wx镃̻q?S}{y)FѩFP>_#s$[ˋk^@o𻹓sC!7[^jx L)ރONڕav5d5ۅ4JvUK@w-_/)%LΧoF.%Ѳ+LW'VMOBbKqOl~9tfi!7u(+q\ XDi4!-?_.v[(?v[WJ_X+[g +:9Z2Upŝd/[=kX:c O;EESwǚNVrdǪ%!0ہ `e)Y:ІV*Zo6"Pa&>g=crZ>uKjt<8v2PUj6[|m2n 0%R1v7DP"GFCOFILF##'W%&CVG#tp=}⹟^7:#ps+;./^ldER{AkHt46W+έTwc0&"!y#.U20Znw.4U +ޥ-e;ytWoj͝n1YU<~,@WS=rory+dc`IP+T%{?d36ĭ,>4t 3j\[4jx5q3V&:<8XaU ԍ;f|EɊG|\%?ݒ\qt!Dn{OH`åczaYn۱BB}ng{K+KY.aЄ{fF Ԩ-ӱz)r=f oH-sċu/`J'g"QKc|L=7rHJÌ,X4 2Z 4wbt,Lv/\WкՔ_{֠;՜BY^uv?Dpʥ\=l7[nT۬׆(_lSBK\n|n`ۿˀ;t[{{b +Ó<ȏSOfogm41& \oQƱC#+E, [SmWWU@(@,HM * BR@ln] Pa6esHo׹E3-*w ߘEc{i_WأNi#HH} edZ:gd7r)^5 '"=R|W> ".~ I]OwM +c}xs5]b^gѹ}z>Zxs1?^F(go|SNO>% + L~ysmywz?&}CYi>O~9]jb;8[16gZrл:sՔܾiIS` G;.}7WҷuE˖s'nLv?lV;yn^i_F,RegnrD(lh$k;Fdn1 r=\#=ӽtOPޙG]X[r7%~Osxv̲d:)?o'.+ym]kN"<цe:+EĢ?:MU\in7kbv]gLP|\TXՊy+9x7P$kp"lg+w/;Δ7ЂlJ]yJ9ïw+\twyE 3|\Rc?#F5H%׻U=Vv B[H_D/}#;#k}Wzέč¡YUGɭZFpmWllxɽ۰>}dϙYϻi/ [>q6.-v }v8N]._ۈvVAWgXA!,ZMsK]SiiHE=@/EL0T}ZY6&Zq{7h7iKྔp=z%Z`LM&~N5pnY ,i/*am}u[BjRnt9Jy?Doў~b>(:zfl:8 w>Lׇo=•O184ʰ>+ӺV%jGl#TmJMo +9gϞuaHR9}Ea )ٮZ)Z?~{7/vWC +'HF$ ^*aQb0̨5 躵[j8<9'>gSF֞X+Ov(XR.L؉7ȳ.X𩓵VIl\l[^hfdfN= 2QڂC#_= ?g'<9Fn2wrjWɼ0q,\5ݶV75w%7kmCh@`WxK]+ARx5qwLp`(eRhx}h+/cs7LzeW`K+#2wug$ӥ{ML]eRuٷj;$[޵4mPRh?ͼ~*<>weOrDT_罇8pw +u'p=H]IS1Ni4o|S"˴GVY}Bƺ04Z&0ul 5{ʬY8)2)f"^׾暹ey)żgd.3sGnNV|f&-Y3YN+gjCs5dִ̥R@2wU+$7y5ێ-:Җഢ)Xkbr8L|vvnGuHfP6'ߙ²wvs4=lslUe;3x +uyE뱌Dږgk\%bA!3bjȀ]G8ppat] ruj$^B\6 8,>Um^"K"n/&p_d(g!OJЬC}KQiKơKM$qGuDV[ᛖPIn뒽z,>ar=ocz|eF/RB㻌Tk dר/pp):Z7Zu|7 OyiWm}<iH؈svdli?iVX!Yz?:4] kuw/d,fvN{ga%0B=j| *,n`UN#6 -khgn3z +c׮5ƮL;TvW"@q[?G7axǧ +kF_yP|N,DlЮwq +PUe_5z;ͫcf{]0jI/ FW L#T6Zϧ6<QPsS<^Bً&x+Uq1j>wP0h0B{ƆΣ3;Oָ_S$S\i>stream +KȰHZ4+INSi%iXzVEVGOsi4Ok Yf!QWiaB9[φq^+ gL6(E3 y( OIn86acFf2xLG .^i\=٩^qq[pqr8}M'޹_NB|ʩ859Azfs}*y9{є!ܮC]\T}oC9%YH[s,-ңT_dU ^ qxQZ4?Jb4^6K/jA!#25[PsewA|=4Kd{ +n%*TBO 84jX)wڤH) &s +խA]@:Q*ӥ\h<{6sZjϵiMɵjN׆+ Lqy QJ^sDm5+êHYoVV)b̿-tg%(D O0l>>oD%?0q.AG2Zyyv2/'SK/1Leۓ{ o\A1պН ZDIq8c 2+_c6T" +B;-3̔&_al|Az`݆PJ9A:E\(/-̩0ymI,֬>cJ6n}o\_[f 3f 6N jDgW~NQ߭mx#R̜_=jtfYs/AOBF׈ Ci~lOz64,;'V &E'RK)~{^5ŵH$) ?}ǀlnD@܅JFd&4a!J†`'`FVԯE.|!5sEs-5 /PpRO@nPZRTC)$JjT:B[&PfPGGTַ[G!>Yd,8yY_vi[/"_Gήn;+(m>%Jw=#:9`0F gp*%Ūqjj[ZGsȌL˕D- +MBz}աS{ WsXg0U n% EܲpEx 8j-R)6pZtG)&xЧY[>u!* YLB+* BPRE H0ؿA[\g oNp:s>z)AcE T5 xZѯԱ*bRk@>#iT1xq*C^nNۦ_#?oR @@V@b_% u6er& @J E?E߿E+KMI~O ]슶;%x4A s騧scZ{Q\΢C|O %fa _?Hc|Jï+CC\WE|.D]!|8q(oCt{?Kf}6ûsNmYo&&;}^Kn1iN͟-T7kw%xAޱA͝jU;ތ{>[9t6[~:m[[vcYO-7Ɵ|dxect\0 __ YYeѻ̃x2&RiEO1jqrY3rM%8c8{k־G50K~i;4lQ'6A}>Ip?d'UO+{c[ gSΟb2OԼw睹W:F[ 'JNMr $ ^DZI9wpͮώH{VF{mBZ6*2? } 5'sff'\ +-lbMuҦcwo]+Y.T:+]K v  CZ4RR+yLVHKpVa3}u _·K L--G۲5ó'hL#t'ʰYz]n:ӤEO4u߸řKC|sPDj͇I#auh.̓bI3K^Gtxk8yuʑiQ9Cv;肁ZCd6#7ćݞ*ꧡXYG0{{2iJy[jdS bmw k/*]!Uƨ\\oiq\n˿t_;_v{h r2GrO][ԺRZAU[תuިRd!gY;ҝn]dѳ:yٶgƶM.vZʲ 5j˻>xV\8Ծ.G}_u8-·Z,aXU9zG*ڙm;f:i{n;Z6/wYtؗnF:όp)ٔgboka|Dkr%=Ѱ3V#g9Dj(r'Mg7EѭU!-o- `3k44WqQ>kByz,.UtGZf5Հ߹JL>,iU +v㷉*7Pfn9+.l.֪|,̚BCj)V-›YutP:Z45\n+W*W +K),'RX_$1n%*M5HdkdUptw+slKƑNvZ]]CORڀ/ Z:kZ~=e"F?=prEnd9dj4 7ڱ/HTUY­C.8  +EO~ޟLlf}L>?ջurN^t&w P )QƑ#/WbʈYҦ`Ru={ :A +MW8.#Rq5;nVv(_ G7{3d)j1QzZd]F6P@ŽkSbXj"uY©}Ni #ZCm׼)/55}48>rJ8@ARSA/nr}3dsgpv|웟eݎ{JY(gRPml{izHñ%k(ߙ /:=SFXT[K6?wVJl- LtcLFzzӣC[0q #8sHFaf+V:L˒2T;7Qp Oq!p-=m^SNC47[ e6k̤k3\)X]"sڭSpiz^+|zQhmEiՖd 䬚Oq@lʜvetCk -yf= 9>l}_\01V1rC!^oPk>]HQXƓ;5Oɝvȝ^)9P~[= w;c2FD #y/v3^dvI)k3 b|IPZk\"MaDE%!$WwH= bsoq`%:9%qhXCǪ&`E➰] nQCf^t>@܄ʊK7}]wј;C޾j=\,CXp0yiY"B98wmb.j[0zWW%Pu>"AYAm%`nv2Vc! p47]-}-U'\pk:}n.("oIM£M;v/ |xWvZ7E?߀-g(o/ +f7Id*@2@t aA%[ؒ.TAWdnb’N'Ӽ"Flg B:sÑbZ/v8Ȗ[u >ۅ2Ww  hQ92c5+Z=3I뱆O7k|wT>u]):[znuc}6e܂ŧ P>ZKΈCTd.5m`-69;l:\s^o_/9]]}b=Z*Is恸7mIx,V!E\y>ˎB-o AN@ ?D"U9o*W~;֋QGp]ϮBLW|]ۤ00Kѯʳ8ϷZ/SE4o)La*>adCȽ ASg}bz R}YsҽtkxC۠( ~Rۀ2f+tjuͨ_4X5MkhlXim[(g6*cR+GcvBr.kiO&m슗aE '|UVh4fT +K:7=oFN=CF!kkJ޻y5 'P/RU{vhU`J(d Q}=z 8vy ҏW+,剦Ʋ t'M=ǂ[ۥZޱWUQ/jUo:OE(\rXerQ듋SKJ1k8=øQx{[;>4ZSitmI,uOZ|еFiO%sJ%E κZQe}k 8fZrr[~U gevM6y4y) t(Y[G×ޯLמf9u%a5Ys杆3meP*6Tb=m_:e=4׀IuxիiJ=Giuq|}<AL[H=BrRjTtV҉qR΋m7{к}s[1v +@5 +=NjyaRr^%4 [9,2 TJrv-{q"҅ +㸙N8.2#Evd==L!wF_1* +#gbo~P.Я蹤\հTk)IuPk&ZrXv뼟eCEͩ›Yh. I#dmH5mG- - +3(O_sgZ[kB{}ʋu[te>Bf\(z=}S9(5Z;OK_;H >z&j}n%A PXN*i~&$oglw [4&WV;S.'vlI,A.,Alͥ8z®c5ʆ*)Ucۻ- lCHkUnVW9Qϝ-]ˊ%LQ_,8=CFC#ήVƢGwӯ疡2Dխ@ޮG>>JD{D},BdlN=^-e- +z tԖ}5IJb:`֬*oo n+ސd [,ьc&f SpPn*=C* [wJk,y1-%6C +qn_v;?zҞxIɬIlYB^HΚܲDj01Ċv<$N3i JGtl=Rk%_Ddi%'+"xGxqC۞52z=~/X;p7NZ:j8S$-cK+aKO:w.ʿ4ȅQW0Iu5%CE # +' m|Zx1?DMسɃ潞wËPuWE8F[jz$s5~%A y|(얽LtGXT)zk!5e"' +* 2ŋut͟5B%fiAkam|er [6V+,躵]Uu@ mWLm/ת@w9&g6r^0Hh_4KQ:&/Sv(?O֙ݹmS*&ѰP@7k);7_?_yڇĎr{a- O + z Nqw}vZr-uaeS\`hN.jnUVϤ*ΰ R +3ɵdH;c9RsnBx R_Mkwчg<ծJ1k k<WdRZ*3WY|،,f$؂7Τ,ڎgcwYZ6WOgZQ/|O>PʾsrP)W^Q*ףxeJx s *:#jZSZ;8GS$4 p|8Aۈ +K3F 2'jtM#a~QQ|:i-[d0俇̦.Zd'w?v8ЬӪdO  )}@) xtI! +tyJ\BA?*5@s;z?,tB%ܝ)#//.@k˩R(\~C _2lHJ1I!UmOqR@V< ubQ@C) + IwH@ lQRTߙdάv!5 7N{Y5RφPrkTL`@9|R7SSKIqjV*)vy@͇FjPmbf8'QRmM˻]D5nhRSMgM 3t짘|="׀^1~ZF 9FRl{4` `t0ZLV(zXJ=%yol3&n#{ <Sh&Nu"g3u_~by~GJNn[C`up 86)pWĵRmGQkۿpF>Lr1=495 V\o`m!Lա/ 8ƾ5 7w &Żx8K@ڛ13 ?^R?r WT+F nrDp¹r| V_%g ϡu!oĿOpE* U)/ Q}HIŦ){Cf9Η&,Oէe{]A2 Ue"O%LL_?ab?¯_ր\.@(Pʣ*@PT=~? 3,MvJZC,F,"fkoRn;׶|[zoh6֖>5)seK@!̐wb\be]}poykK60ƅ|Kզ ďk/[z-NG.HGڵ2G8raBa,_ [zo'%:!cKDncm*nUR]>YcE(dI}Rp)XdiŖcIB|I5ʃ p  O;sKkT~z`ie ȇws^"t?}LmTOCBܑ+M +G }FDyd~0ɶ&eigx6'oN^?/_Cїk+`qOT YM{ʠx+*b|=nmmhjNvoh +8G6@Ö&X.8dwGSj2&m©3Rl7pkrXe _OJQ*98N r\8hٛ!۞\mOK}#^7oOf^ǃA5黣48^a9|-t JKyBKE9Vs\|/HڞdЫ:n`ˌgJ3:G)`-Xg$GƺRzg/}Jw4rA{Ӓkli4{Rt +GymbYlB2Tdb +ş:yLSl=V Dx B4}zٯpI~J{lj\ Kkg?qL>nRfqw@N_W6{W7ew)<鳠fxS;?ބQ(^AՋӚ-ySٱ6XGgYxbR:Kb6=:U2w{Rҋ$QśpaQ||x +l9>>qN"\U\pV9G|i{S)PWtjqf~ Uw +tTiJcD`|$YwGԾ`j 1`[͉϶'7$s>/V(bulyԌuU:2C^y)Ea1kU;՗[l>??9={7UX +sTZ}JzX[L|zܙRnr]u[1¡Ɨ&3֖CMu@cKty&kRAXݷHĤB/oVry𨾵~c*_Ó~MaO#iV2"-tV"Wc>6ݢT %by݁RQzD^_1ZYW6K|r{<}gc0ޯLݡ^D_JeO#ڌ!dc+8VHc5Oz3:?F*%g&yCeyԈ wRh#bEX!_eLP?k`71'F]eU$Хe1ԘmǠ]lފI]&wlm嚤0 nHLLYhh*)yk/ m"6'Ub߹'},̱k,AݖX:o.0ʗv򤶝网sogMdHՅuo+>_1ϋn̂DG:M:AGs5$$l'T,Xjѕحm{'!v&qosWӨ1F:rJA!*"!'Igd}|:v:]<,мz:TW2.n+>B{k(p6$9HUDne_% +)aqB9; d2}73m&- ̬732֮+si]뷅)rڮ:̾:/P +LaOjFt `)P  +M2[+ zA=p[:bLXh jA~tȘS{ueȎBرΰL>N")lX5McHςm fJeO$),oXV^C~w5}bx=sLQxPtL7NAஞP%)j_4^Yϥw{2=x3ÙL[5Vv3/PAZn%@hDDpbnRt.)Y@Lj B`~`S>Oq6ь^(cXVG{ÉbNAY =7ʸu1duxpq9!IS. V#> #vȸm{TB&aOq \׎YdbL5 o:o.y0|]2bkk90!JKfC+S,Of2 +ʵ.Dj] }hiz\nz/R^;"{Vut3uX-< bf|&"߮>|4s'GOalp#O_-nF_q!)LofsE]# خUG{O `fY}rG՗8 GzB +_|:ViO~>[NM`?nVoDcn'ߔaN1o_Ono (79 N1_PK3|?m~:΋ݣlkE"2o%$L:}Gzs ڋ@tx ĹOq1ST@1<'^{@P F^X-i~~thO?fL+ eȺ8J@іQ'Pʣ'Pܪ|n3pǶ[- E@ =V 7~x8)[DRy71IQMشsV)/%跛%PW_:|uÔKZֳ$]:Lpfv㥰vDPmp Q_#32vERk]{3t5w7Nt3(FZ\,ŧH?Ghpg^OcXGM|y$ k{02_Qa:;1XzbSi}W^\t;|y&Zߝ,~.ٓͱWPS~bL݃  A9?;w?>>{qm{M}WeΝ (/~mdlğfTכ7ױlbBe)NrT~.22gc}0]K{؛:MGyF[䊭Oȸ-+Kzͨ5N4o?j=Xԉ5?,q/GT _Es6sۢ]@2(W~m=6oS`Qny+,fT|o2sl~ojԧQfLF'6൥RMukNU=*UnWQE}F׾kޓg-QIP|ȖgJyX,!5t=I;>-0K9Q<-~@I7Bk |Uَ* VTX0V0KWduAOd}"ipUV^5ZzZkCβQ*P4zomOm:Oq{/6:gjgޔV-{m[sDŽFP e)yC_dҟOPP/ɽYZU6;Avi~Tm;nbٛgݶ ],6dε#nqc&=0}>)0(uW5?yoaXDF\^uVϲ MsŒJ_]^56e[Y,mJR5Hj-}=w߻A'Uխk,5i,qT6̲fO!dg#"掸JLNZOİ REQInN ,NtIBGJB˫HB\%)^K\?St/JKGQߵ G7&UO4252*#p9j}7zj%pZ*n4z 6frplJg^H´Q#j v% +pĿgG8O{?igO]fks~&!%2ՂE=2#M[AC=V&VᱠP!ȩ-mE:7/ I8_:žOE%k}5aTrΆ7}Ci1$e*7;@gZcvy% lyV1rL%Ies;/ ]ٱ^a9{"PWE[ ɜSRwEkb-a\+ɩxCvEPv!vdb˯=Rd/j 򩞓%iScEu!`5LrR&[fsjCrU? + eԉbNm(~4OȋC-RĽw1 y[Uֶ6%H?514C}Jzӈ6zYiVk$Q#+p>[7.׆ oݟ8f& KuD裑&g=֬һ&C@ =xAhR7>^^(tM0񌳋YE쩎4.?/ʡ'-'#K WW| >"_\v1EA&# &6bLe]4E3m~|թ#yHݹS}lbh#rҪaŽϽ蘭ڢnںr7Eεa4?deQxq.C8 $)߀n7C%'؟,}.{Qv0QYռ#Yz"ޮԺ\οV՞]{ZS_NWDf\4WSpsxYbpşa9IX= 'K=be7U=4g/I!3Nיzgg⧘~iSmC h'KnDY "JE' FHG-@N{^.VЛs8*Pq7 he`= /+Jw ož7y- R9! >ӕ&R,(rVg%Rɶ/%p+PHB@9}ّ6F!`[-K4):ZPlh J>;O +]\C.B7Yei8~z|f8?#<%([v0g){OS97zsO_jj~QU$)h%-? +E̅$-®!I>5k/xgn"=K(t0U~ڀQ-IJ0?tGojc%_D'_+S7k+”#VuDm";)'*r]nc^˙g.j_-wZWz~돃V'zwlfI3Ym61's+Y3L2gAz9_cbht[o TJx#m??{O˵SY Epx-?16Rܺ\RnG7bvH,G江IN:58cKA iTjHr ?2g3,]LNU[)6S j u"+@LOvGB='=^m^5l?J߶*y9wF >\.OS =%'nryZ7{7Kp7lǧx:P9qwk^{9)6'ei,YCv8KrU_6.."-'ZNՀ:pˀjOOc=1-0 +r+ ? >qR;#5._im[l 7kq+%Jˀ1*OG-+ߘG +ߙOc- kSg>vΣKx;䨴{l[[>g<VXd =>4T knngRR?H*=[{'6WA>2=6x/֫t,c6obDVΆ+N%2#4}/qQz im:)u늗[3IX9imֲ`hՂDtPXzʳmi.:@F-}N{`oz1_?:$H}b>t}vPZawkA'#t7XV2\e.6n \=eYMFBoZB֢s~"f(-*-ɧ)fOct聛y&iueMdq;ǯ鵅Ϻ }WKU|ʠ`d*jAP@\Y6rbڶRT ͒CegJ}{3#ۓb bhE&C}0lIa?2%~Uvb1fիIoVz+rX*4к)EڰiY6;e'{ӹ\~fX[SxH98H](f2oOmyji\Phok5؞8=+6ǕѪ9$]x3ۨ] l(k<&LDM:EPnf*swu|״sJiQo>WTyUuCz"(*VmoyI^4GM)ZJ­169UKeהрBƝo wV<-<.KS(Il +=dvgIjR_J +ݼ)ds rHb(ئBTk~׊Ċ;mkt怓tg/ρöWrUK % Z Y˪nk^m:+U{:Tf45Ei%Z1y?2\>5w06E$ BbÔ7G:wzl19+p2gtd%q0tmzt,>c̦S}NũnOx3 udn!JndUP^G\ujO`WJzJ|/p q腳YuϊLX3uo4ƈ2>-zTVǴ45+$|o bNW'mD׊%LVyVz|Xk1P4w`LXHrMS2:NCɘ_9SJL65 :(rSLӨf&xsNcY?QXv`n03i 9I/F)o*S JI!w7" HF/#p#\/f~/_Zvi6D{S SU֛&.+:.g G/3/q p{IpQmr%5t*'Q[D;|Âsjˡ0߿_0~,t@opoر,@@("o IJU- й`K*ODaרunV/W@SνxI_O# xNB28l?7aHR]b@ZXv# , VRe3VIX.^ >xZDyhYxF{&~ϟ_/|reQs(/KʻPC=t;,U 3%=u:̻G&_9:vI8..&!:rNMI F|K=t+t%?N&ICy~6mLHK[-~y0h#r=Ma'ʋi{wd˭o76j$>˩ b6u"x]r,̙m\[{"HN~Po|7rQ-o)2yXnO5n_دU*̭/|j-a9?|d sCޘVw*IGXoa?6B^y8tC)^L C1BH ^4F?ֽuX<5VcW֟.T<}hiڤ2Iqؐ^c|FQGFFK 3oeNo[=fu=\vVޑB^h_^c-Vߎ [_liHC' -39gu>Yk/h${I?="pYKR[}oU{y dk7,o֐ $7)1$d GG:yvG"m[agz4s4`[VWz5WCbܠ\)A*TJkls)ڜI%q9Mb]]]40ֱemۜ_s:aP~%ivjDf*W7&Z6a,E>)8/~)йfG<ZW-/)=C:1Xsi Z[r,ꥑ3nld+~IdWEdwEx<+_ |Wu/e8O^xU߻9&Cf.Ȱ\,)n<$w#ݯ~M~}Kd߉zMܦTJTt> +^d r,99泇nKy/?7#sZf6qt&Ihk ȭЩϱ'K:_w߉ש=gE6+ {[ +?@j%NTS縼Ӵp:s6^J㇝Fk`ʥo¶iKMk^+n +Co3+/ov 檇]L2bv kOy0 -媺]&ZY-ؖZV@e#ljl䬒 S%=muZ~NXyI[UV̒&&vU.̻^nC2.QBng_ d^7UTүbۺbJŅb[b:[GBs@ʲmuRNk[jwis}P&9cW~ @"aX ׂh͜!h2QhUpW*jғӥ#Xek-}nH(Nݞ`1`l,Z4#zlz(:q&E_R?jKyŷb:$,ytв] +2W4 əŽ8LQ\?BCj*Uph[9'W;Ia%iL¡J Ksvz*֩v,]w L$eKB68$.!L e34O*WwSLXqb~s`/b[!*aںtΖG8KfqZ ab݌)̔/괦ۈ2Co9SlҼ,$?kzz&0N_|u5Fb|$F[U(VQteXnWxl67,=Xv4$$H`^ѕGĿMݭ>R.$r sk1 V$J0*!>onwGg;l'sˮ*&p  K@mg*lbRvE ˩K2yrO>rOk]Lx}mh@S-Q;/Rŷsg yyr =m*O,RF |N# W $RgiΛIKTÓ' zrͽa|`LB}I91!]ZpwYaG}1 +1Bϥ !]A;Fndts>~WOxP9bYb=uRNk?lmHJuH62+"5^0]`9nQj?dճ%z>-hkx:J~A}"=!'7bV+gj JK7 +~d'L }QN v>erz@te\ 8 1Z']@zK.*s ZgKp5ro~]6`뱹C MR +AxXmg.zwfuI-k{z GnO /nBɺQҀ~QDb*ngЍ[B>7m>gTebE30;-Ĭug;AQ{'6:W<~I B#@T K7 bn@1$X. % ʥ *, А,|(ʖm]̙qa.Eo\\`&{/*_\q~P>%q 7 4D,2W%@w 7T!K84s%9멀=@z +HdZK +I\Wz.J{)~e|\5h2x@hP #$pb2uzԖ:ݯ +j8ք˭ PTXTUm/]5DvDS;ĔF!FuPa4Ȯz}!?e*XS],0;sz}#>?P*PѶ,ßQg^ÊH+Kp{T%qCLQw/o,&k;p,~R*`O#Af5n,%e4m9rq$b6Im_E2_J+q߄ ^p~7 #A`@]_{o9;$jZ.w*¯O L,L;0:n:Jwr#1HqBXL$q߄~b)<('  +ANR}cHd# Ѧ$pZB㴪3Ll+S,p 8k,A?=`r!߿Z-eTO 4@#"e%iSjzu +9s)V\*ص6r$ܛ\e_ :Ժ?ICTor]shagm1O|~{đ W̎ v\oڈs2GzZwo5&n4A6JLonJHdT%7K/ l|r^.Uf-gI7 ]+,amm{~ORIEaG58Zg-d59t>Eq{̷ .(<Ĝ6=&,+clF}8eooU|ɕcvUqo2ӔvڹUSemrmiCI[5Nm>4Ò,oRԚpqXO' $O}rr +VHg vxf4Fe?3o8(EXϓ[.GǬ:97%&n0N*xlW9\:k$O'Am]~'ꥻlFꮙzi|i8 rw[GBm[(5tҫGՓގtyCeWz^p },EŹd_-E}(-fJN[2 t16}' 1N ZfݕN1A,Y,MEr߾^4 Q|Cސ"}0N0ʞ];2Gle4tK[mH}d6;L +s(\x%sQИQ-F1׾q7g:Vhҡ_rbv6 R5Lk<[+\FIF؎'\?P?,֪ñ~XOP'Iǿعm 9=2jJ4Oaߘן>K`aWQ`N5cVEõZu0Ytt/skjB^7o$^Túo4J~kPRmg29"᠘/+VVp^qk!4I?~5\rK{ S֚9d,%UNW;JJըaQWU9:mȶ,.ϭz[r 8 siF=$UmwHg [;&䏪&~NJJ]rt^h<۸l%M6+9j;D?ż͋SV+2@] -d% %x8O/eH;^Cv#O'UʨƗC'8CJKl챚[`x͉hc9IRGidtQ}6>Y{CQ"8. s),oI\))X¶gɛ{&tg߯')IV;DqFv-qpaapGr6rrm,I5 D +p8SڰɍP$`A掃ltg iәޭ1hiĒ{4 +N1&2Xҩ22ӈE>QECA/ݙC|09ҩkDsw?i`rȐ]e ``fViT&C @:PAzDc h:Lc9'0U21@YPRRu14Hu$]QwyI!:#~R=7b +I]F[8|{9 (=׉>_-c wK,xQb|4O-@Ta! n< E׈x3B~93=fo;ʹTW$G4`neOURrJ.[) 0X bi\&ys2]nצ%`0&&sF]q,uq2xE hWC>vY"NfrMLM._ Z`2%ػrr.Ih,56p*Ʋ\q6WpPu-X͊v(S]P^ 9S@lsfGRmM5cgpʬ^h$F{E*p,s*>eBІW{@%n}|ZIT4}Xy U IlG/0lh_CXƶT?SYE/*! foym"yT>9)e UHƟU@"Ht:ټb)IX1\1OX]ɾvNP9'xqp?[. v\bG cg^?k @JX.R2Preŀ ǩs*cVKme2ATvx˛G<ƻB_O n ~g`քC4hG3=d}G@}N\t}tуmgRs D624=q!\<-C'u?: 'I-+bV15d\-8VٰGAlO7kU2$`a:5(bn`)%dإ0$B(gJ<%k˟0,I`?U?7u^s>0: +L z --jIPPO1otƞ'mR6 /?Y/Mߤq]8&״hm$ &c@O9 .~n~;S 3&Lx +-QKvZ{Ǝ:DR10mH +s&i@${@YoW@i^@q0(کgk a}G#k O%7 @'v' uM$?9 S,D{ս?ލ[z#ipzS>@Wh~xڠ}H6yx?IJ:dŃݷB32w{euJ):{fDfw4ošQ7όyO>*{~ x˹O3\E&Гk&MoWCK[ oTɮ:~&M?l:8rYmk/܅@?5\5QO7Kqumzp Cx*"7ꗈtoȲ#=jPO]t,؜6=VfI9W}0un}7l֚Kh}-z]p^hFnpژ揓u +uvߗW9x3?uߢ jϤǬg߭ޱ.NP,kNk`7>Ә:H/@Z oU:y˺ն61ggSǕ^W餽tEn9\T<4rL/dMd]B'd'UQx%8O6斱5xyV=qq0+~HWו+PLh1K<)~L8-jEx__%_Rh o)Cn;@97sg$$NJ/FvY0$YpⴠWc)4bb9O4)Oܶ 9WCYe} +#C; ~yMǰS}kG'IAknJf_~+劑/0,(N0N[2DEkof:hHjq$Rs;VR6;0Lȭp)(j@͒5ۘ' 5!%$P!2yI66[ض)JM(lXt.Hm奱caE\uv=|񱲉u|&co6/ffIEoƶwLzi%T`^S0lmh2Ke=ĸϣωUЀ[݄r]3=5zX&c؈q̌tN2D3O">SX&8uFخ5F]EӰ]&v$jBKcFomH{/轰x*GnD?[m+m*bdc-2; cqmEb1Xu"U|= fx9kp8ݓbb\^AOTzwZwQTZ,**Z*rp8V<٥kph+֡ A,U : )5p:c7=lbq{X!Xb\>'QwFIT^=;)!m!iM{t ɇ.MդO +}i>UB9IZ/#=Ӏ6Qd}IȂS}ũc5X0Oyh=zyhڨxvtȥobvx!*!^= qӂWk ;"xH! YbFv6eVH%зitDcj2h5އo7vbw졧>b!5@e4=]Ń(kR&>J<s[dN<[j +`JLJ7!K$ xa˄ZOX|!Hv[+~oxGf:zXQˠn64|PjdoT_@vxn2< +j,b,x$ +a|Z;r WpecOP9cwyl8cMX]cj _@.H6nd6mH @s%vX@V\xJ%)e0+!yOyl葕rlT+Ǡ~nmZ zOaSA@u X^14@<. -2ұlO+t=t_:@j~|BKZ̕Q%L +N~缡m1SxRG33C~mN`{fl:?{9:ńA,$|pt$T,f*ACK5@Bni(i$|x6:5I +YV:1sPӉ2'iP}͒_mvʕN|z{P(zkԠb5gОܯ[s,#IJd_cNLլw%W{ u.+숒]lTlۈ[e7x'۱C`h 6$TIlұLtb,>z`}fVXZ|"-RJ"?>RD)LpvW%M Q% 逜c[NlXˏ]bqEnmdEcܖGޡГp'mٴ]Qk QN`zH*dOBqWq/q]1,V؜:/KI3E +歿kjM,U܂ku r- k3u/]-OQZvM~ɲ5ffgd &|H~8xwAt@3?ݟhG$gxo}&p&gym}"5zDy- bV|NB [-"g`Q<ն-¦ RϹ*;PoSh572i/x~rpU-{{p%o X+FÒϔAbsQk}>JX4o^p|RRѨUdV L>t`M[`(=w"/vp2< +325rYhǛ颖ۃ\DXK_v&rvlu!.{l6I7(\nb<,0X*fͣW姾?d`P@jB +uKyQ|4bCоVS5٪D2#45e*0w, Z ~b 3"Zd.|,gNQ^`"#NƴI%>04O@x-7y +-V,U"UTܶ1MikٙN[06ú&yvڜǩRms.29K16 '"Q/(bAF&nJΩD8i'Člw+Did}doEWs?HKA,t A3?Oiu +k:K ~nYT@>J|kWE>y,zc]C Si|ff&;8kSŅQ?D}'Lߝ?|:w{c)N_͟ )L/N_? )h/8%-)|<>S @mq67/rcst$wfuL,Y}ԗ2┼| PI>Y$8ؙ@sRD\2B:PL'{'¾^'=OU8D6r"o=uMq kѕ +ٓBٵb@!fgίj픩rʐ92d7R0bU|)ƹjpqf"4~{Xn㓑 .޳:VӴIkdN!1*ɖ!7'SϹ.BsufݭGQHD":/|Ьc!hhu#q4l+{=ݓoHe-!!^ON x 3^AWNm_?ԯ잩Ponmg$aW@3QmShe0Tȭzh- +.<iͷ'횑,aZå.euS/4GVیnFD|Z9ik125^hːeMnռuˢCqEý#`N~O.oבߔc<_iE;+nX1QtFB\V,#8]u4ϙotpzܢ)nbRyJR6g0[^l㞦fyY_MQnWTl()]46wmr`SnԀ/nSWL8\5O h搚FYWe9SVϢ bNrZIC`Nq[ݧMAᕼ3O_ m>_8{Uc)zJ +Xd +ইn]>Q`l1(.'؋f{.P(e:^~C@3nո^J +%q(U.W reC|:@iyk I Qf"Kkgnk,EmZ%mYfx $]p v6PHTTqu!6 ^mf  |gCԚwɏ[dh Eƭۂ 4B+mvvPB1Ob]3r[w)lk7>Yl]Fl' m~< vo-[nÜj 3$U mf#ͺ#w`3kd\V}Ato$i:Y`r+X)vB~,щSd/"*Kr44rjvᒱ֕ձʝȿ?8N(⳨͇P gE}'IxHa +qfn84oonqC!cMτi;IH[Dp6/4l'9{?PϜZa/u|auB Co46` Bz N0 Wji7 l7^|)%3;)7´m?#| Pej]*MNYj1DR0~]8Tq;FO&/4=^OlUGZ4|](jOD7+{y`LORtݮ}W/\v2M\Բ/O(@ Y|IB NClֳAtvMep?#WnOqPyFL=C*9ԜPsxdBb")D8=+Gãep4yC/k>nILom|H]9{>j~M)QgćLhI-涕(Cz~>(ڻ2 !8[gް'WNTgDU꩓vV#,5O*(ސW?vkew;_Yْqg2YOަ Qz!jE|tS|1+*y bKv{#/ӿwygpxړy5lN,8ou;Ӛ塮P] KB=Ciz߇@z3wyԩPT!r-^)# =T{'۵sҩ#2v0OsJSڸz#i{.wxC:Өyo Mj|k\ Κ82&wwdP}$מՙ ۔RtXj܏䳟Ջ{/j-;qPB+ cwA +EJWKHpԹK=#9,d$xy̶G5"4z}so +t9?bz6v}6flC?C C u[19ɆKթcʂMJ W%&\fwv VtMPIj}HL;+'&k&fuD!&:\1"MlYHT?'EkDsO~D;k 4LM>nd4Dhkdݙg_v䨝{nCoN PnԦz.en;2gAoo2RXf- S]@ͦ;d19{›1y:ZrAE@ԛ;ń!&)N QLdƜ#SXγCn1nCRTprdwBKVBYmNnZ"PA[o>YwZlų ?17Z%@m"E&tTHr{{Op:X@KuC$xmklԦakڬx7yy jm8&w8F8(y;cݦm rSh}^YYbXJ7A\Ej)X~Z}~CņJ%|7 *Tu+H^]E~ٖ )N14ۨZyV강!+SB֦[}fp׈Abe[Fhvǽ/|r\ lOyóx6ٚ-pI-f qC6df|8>N8),;8L ڧQ S?R8?` +qR'SC!)w{Wg/0fǃk>TUj,P6D 2)/R8ru'v= ċup| %;;#oMCb@x +j]R:݅\I@NRi_Of;"Yc!]Vs|.r ~ U|AmH2.޹ݿx' %4ucvӨz]Է;_?#A1oBp:ȹ@3FAT{T&{7Ao^xijsyCfpCF%_?8SPSva '=GnG7NKo\*-E~pcNRl_on=qn\9 xPlj0Bű Pz1zO>/.Gvqy#L!ՈJRuFKܝ;A\)17έ[p K)sT+ah8B%ƢS8I|vM#y6pQ$Єg;ΰj1>!%&GF' BwILOzGإފ3:讕0TzJr%l՛zN7o +-DwGkM&?J K(R.ZYxJN3Υ-9ΠPd牢nRؕx D }mJ1.s?#ʔ|tsr99Zo[NKrq;,Hp8P4~go%L ?؋~^x^9T;/ 2u;~ӘTP,[e{r][n5]Ϧ 1)N]vλDp(T|u0Xh7Ed6F5ZqꭺM$XGN2!u%h0et*еj縻;EFL3$~sLw_SB +-UOG^gk_å~M&+7H6,mܒ7hMgv)(^`uBnv1px!c}M9"]]wn|wY|wgcDBZ gX??ͧ7(RTd=Ug5;1|RИ$y;~_wZS_З!9JQ7b>qң)-9"tc[6A[,=")"V1Ҧ6ܝ6[N r'l+o&>m9ƇQƃB;Zb}vUQs;5ƭ[q'!ca'^lVzN1XY |FEm磫z-\bÌ}R--_o~Gx/+qOֽҵF*vG gB-+??4(7;D;in]xwm_"x+RWa_3r,(z EJL֐}jrdW'8;oZ9 4#jmEbbTi E0kWI WkY⒙<6G\OВwFзEP>whI0-8z~FByr8y/"|z\󅊶uG,'2'OuZR-_X")*bk?b01 u |#wpB3峨7Q }Ϣ4{)𙐂frw6_/N8tHa +z6r + q^~.Q4TEˎEE⠔E&ŕIjW['wqѷĖG 9 8[g CƍSo +@vvP%8p+UF-GF{krvǞ+ޕ#OK-t5BB! |M7_~PAH)(@TV -U .|~у^'5Yd#_Hm1z1frD/~ћxWm1Oj#'7 .aH/cCYE囇?ӚLe7vo J?G>:f}:R 鐏y${u^ۏTR{qj3|* h63v"Gy+[Sp6S@J=Q4}I9;Z{nTR֔9P e.zY]#d]BasЇ~9TP"dtڪa;9~0^+Pr}qnrQME +@WCb8tʴf2ri)vyƉ8ySwqØV^Mv+_PU_8/Ǒ Zn! U +p!az)>ٜ;oU٦=.f& {V~?QJܩZyB"#W_kssB6ΫDj_ 'Vʒ\(0Vz9Tq_~pGc,@- Lć]}m1umG郎cf9*E孱^xp~H|;.3?.l^&TSNP/Do 'mg]7y;6&P;s|)'\[%˄LRZe4#~S<1kRQW*E]*/y媔0͍Ywd|+|RZu`kFbunV/Gg}ۡî0~;G/j-3r''̞従HebgMVD=wFWq'tb^}}+dUܹWăRy\13/FD )^:4diRoݶmw6a~r<ź[k_Wڙ|)osq:/78ܐut!G3/Mr3gFኌvu{hW1-?:Ef[+W&R燗Wgya[{rV:C(/=F|+G"#hNӨ+=9.c`?ݍEf6 +93sY4aW^U_wRG{T.mfCR=ҊӻJҎ\KOi}Y"]bk: AE'H(gvL>.ɹ_ԭ`;lO%P׏^qT"9W&YC:'6ֶ؎m .'n|D]Y=E.f67G~*_P|,J^wug=p>Cs-;CRX- MHX0 IlX-;<C3Kp޹jh-LE%]";5=eܦ*ڲ)Ł?jVԇgQg蠻oXm,}vFFwCsG:I+R™OsK_$͊z>&* +Ie/>Ib&[t?ہX#=3~v>kՙAJr9^)Keb/8 Q )G[SHEVqK I9[sHs!B?}^jgNБnݘh!:ً2 +!N5侢 +^ xz7ofQOC>$-Q]mʼn?qΘvx_n=ހ9@VT0%V1F+' +% Qf) P*E^-K@U!ݻYύl^u{&ق#>{ LB<6O%WGyS9}KT[ji0k{fILd8RWN\.;{vUH~(.w)4gl]XEz 4S6셭`uZ-]PVS? .3GrlgqPs"]7ܴ6θ/L _ @́!&@H[mkFr6nieM1RLx1] F_e:a_djl*RGJT *L*M5'ӺNqCլV˔(2g܇'6?(rz m;EXɬoάGT\{qsuK r @,Y- Oݸg6YFGcԗІ; D}֙ˮ}!M26dX7wkEUoگyTXc.Dsս;PVjG)N1G܈~5lk|JL% cCRNFHMJDw"ύ]n1M"yԏ @lNL,|@㳙v6жzu J̥!c: YhR9hLouZ _w +VbnEb.T-R).1~H3DPk$n̨vʰ:U"ѽT,Nݚg:uiJM4kڇ4Jð +fG+ +1Bc V]cJ.30t^~6iaN^u:k%p =aAٶlT*}ƥR 7R.t Vu=jL-8wP䠠^㨠b1S)öRP'i_)/ +aoO,;eB~,;8A3ߗ&0n9Q~#,G.s"Z7J]sW.aӓLQZ曺lBm8LBΰ);ْyr@*H.6ˇ +8 #A.)N,BJRdrB,zynRN[$7rQPdEi_-o' G.E./   q$ޟ|Juyf6tGThGf JigNQ `HKQv0򓷥bJJ杫 }]PCI(bZ! PY,'C=GLuUz~B6a*XAleɮX +mX҅'N]%'|qefgCQAHoiRzBA d 8 Y֏<C3eJuDY +6rm\7Q|~韤5D~،!bd]Wf~t8YTe Qjq7,cG FTo5 r=ʆN%Q%Ͽgq_lX7FRBGڊޑ6ʇ3ͽqR`Գwy҉ʱ=E1uz,D?(}ȕן䷅Eۋ\\(4ڣ=~#! rP$Vy\p¥cIơT4qcH)D(W'&sGV+7N/̲[t12f[ջ1¯4#A>B׃|H}EK>@Y"hJE'!Fq._Yg4\oLI8+iKÎԒ*\eQ7H܇+gqJ~ةG.[3T[VdQl,q*2EYH/=fi;Z{]pB~EA>nNe]iJ&*fpfK\Ff1⫤sQ]fřLY=qu+I}^~Cc?$AoBZgFQcxv|yiǑ_>^ewfYx4ġBzTX%RT}c4e՝;%o#? p!j 70AԃޓcVΫ5_^..pFX9)RQzs$W=ŗ1KB'[$/2z@Ө z;@ +qPHwJԽ}MeƙjiQuW䈊ۚލ01FAO[[8P̈́byP狜uVyBp2ढ7Rz +Oo3DzV ǗzMrAVS7Fxf#Qlj<ͬ,Y@?I=zTAh7*9x85d#GºKg0:v55pz>l7{[ }##͉E +svC77M(b  ͟d;yY#8)2_3q C7^@6d~lu.GyLT#E:Ks=Yv5C>+փ$(P^@ڛ.4nXucts0hD#1wT4P蚧zy..'QZ̹ry>iY)OK9nkBMb9Lȟ|fPpƌDy+7Uk!fWsh8gWJwp\%-yAoN)!:iYOxPBӥXi|Hl5 +GIԺKeK~ب&eFk\/TZcqvʔV]H=8:c30ѯy>Ƚpȫs,uK ~֎7m  +*%{Uv3ml_P`قẢJ7yL;F7sluR\ǁ2) +҈yn[z<˼gm3Slw4za:9:븇?i׫1vzչכ^VOѮ:t j\Kk҇DuiROo%Z/[gO9ld!}\asNG1nef?yF{C(8#x]2>)sh]SgQSM<O.GcK}Pd~ݺfUKAƚo tB^۞e]5~lVj{fm z \^;C.=N])E+]KG'jPj@YOw@ͳ-NWi/tn ݬ o! ^'̂y8Լ&ͱ]PI2F]D(j@L+]=Gu!j6>m@)wT`sk$~˦SJ6&Q9x&ߔ %3ǬnhsdQ/o?u~Ps?=e齛0`٤'zSSʿ.m0Zz8#^9{)*P3Ig +vUW/GPfb4R7k O~EI+ys麠MOum92-f|(o*"5՘d'*EVMMU4׿(ׄs Fu3Tx^I m |ɻ;Թ'46`7?N)w䛏v,ژ14C] ur0KW/tc\Te" Ӳ㟊s1ԥZnVje-f6\&Sy]ӚQa,Xs…W?#lpRɧA9TnV@^zT %iW"WD<+mY,{-nP3{Znb̲ܛU8g^c;ʣ5# o/|?h@qNkK-*TiIa+AR/-oeMt;|&s3lwGY8 ~`;Ռ657/,kOsn8_+[Ѱ}~@z{Z2_iRowѲqc0S=LN*+fi66>Urʍ] f/bQ/O`i\mܐ݁sh(c^DwcY9NέXcw3͸Eyj!pxa`~ZdЧVgQNIwi>?Oq\oi`ȾOj8Arʤ8Gʱl>L%k'ȏ><~wAx?>jƃң'w7fR9USjElB{OPJ;K5wg]s(q[,%Y=1<7ܫL\ 9$>B<UDKFOx +>xǼvURMЊ:zto<̬qq֚J7ժE\W8fRQ.MQɤ3pRv͙- +ނeQSϑRڛ:p$KԪmMߥrZ&Ѱ٭$UeSf!#ػ{3nƹy!q5 >#ɇ&H'y܅Ѭ>o9Pn8r +(M:ןGo ^͔dվNپ_ܫc4 +rŔ峿tL8=? vI'eϐ]maYbwϠyP ,` @a@c[JmߏVdչb14SE,[rz}sl0IækOu,tckq/I]4 +M^\3"(s:mtApu)ߍ=v9ɯMwzZuRphO?m3ۛ1a0 k51~͡U(( |J;(F8K:z 60;Kr.A|еiZ=F:ӧ Mt' Hgķ_;|rliU +<-PeK%I:l;S.syh4a/rDRm +]}lrb5\:*;V /\)Ɏș,E~]Ʊ5 P`ut8 o#J,B3q%bd6z=-:7\ԅl,s[pf:]tneTfC3mE~O+s{Z̧Q5NeySMQpq]A۹Jui)"irP,`%HrIDn,U0ZD_tp@{KPT?lc:E-aW1T'E=QQ8*wj1aX9ce*"^B\{I'I$k<v`Zq:Z!1#6.(M=yMztը-̿Sϳe5ąkqIv'?g3ђK9ߪ(s^V<I =Z4 ս!(owqFsL +ceaR#}HJyVA w?eu:qE<uK?yv.rpoXݔAq*764>8g wiKd4]V]>t>έczLH}Orc+GB[ :R8űXz\HrQߧ( @D_9ÀAuZd݃_kc! GLK쾽P\uf7Ru@FZFX{klAՎoab_ۧ0;0o !=~8$NyOTO᰾UkR͛'n/p*f8}Ʒ򊗾L Rܫvhmj4ɋzsNà8!.|g,57,/e#FQ[ϯF (_n&YZœqVÞTߜ˴0N ,w9Maޥh܉^{Upe]6Dz_FNf;nЏgĞvbut(kڡeQp_I|.VhoLg =`HW'1_V'&uNׄ, 56L +qPSJ:;pL$6C -+[.M#ci+5 6*Lh>2 Si|}gy6eEZ "Ֆ-Cܺp(tV;pl[7V53[QqZ[ L|F$3s̴K>Wb[U<~ cP;Ԥt$0:jӃQ*H;zT[;~gC\ YUDݱ M^Y _w^({hֲNnӫu^ T'Ϗ;v<%ǖ:$hIZmyVjl!ީ8ʰA<擺fZHzjTaUy>ȏ;:N4=;iWwíVQo''>y^%kp]/nmZ>5 ѫZhj%l'Sǵ<9E %2呵$ZlؤKzmTB=p4nA +4-@"l  85zB-y_߳2-DscQA ConZ9iNt%AˡăyHT p;"ZMuCL n5 $F&&rc/B4=dkgJ-TdLڧe>q%ꕭ'ᕓQXprՀo)׉T?LQ`jY/M w9+Fl,-;\[Ӌ}Pj4qơǞI +GXW*̒߫ݛii5lV?kuu|^s9dMIr{WS}/|jho~VP\E/P1.3:z)U!={ś^T i͹d: +5%x42IW9u9pmٕ`oW|u{Wtd¸jP'Iؖ Phg'P_W=x/{nEkQi-Lvo+Ӗ&= > Pe",ӵ5ײ);~vVfK)O?;@,l= ل"Ŷt +jTKo +{q +"RQU]D;=۽2uF-ZUJ-\-DslI)8_g;3m2K4{ +((± (s9nݏ+~ߘ+wb0!;g骠t2(Ey5= shtpA'hIGQBR'h}5tQ}]iKݙ+4f +?} & b{p`K|+PaP4k-5CzÀ:wz ?X 5vN/?I&ul1Fy\h|+㗏lgT-r36Ns.4ShFKPq%"N08^+c? =|ffB`I^+]bz8bO +nMTU*L< oHNTjPk$fg)5>KF}O(qu]ӟ=@ݓFLI/׸%AԀe;e$zFSoUnԲSdIb/El`( ~m4F9_>.!_g'HP+n;4ޕo#赵y6Y>B" n˳E>?̏6=O` u@?:7{Fې_ AMf-'IoY#nYfqByR6Jw1[@{N([p>7%5C Pq+3 2w4?/Q,C,woܴ[vGY0F5!3TD#qNCC\P*YQK0UlM@ yPJ*퇆vht/>=^}׍t2)EKƟ n9z`7b~u1gF j#*_*N owqun6za4pg-zua[@\eY.aG g! + َi6 8 p|6QvXaT.Sl^zы;?j7(9aJLmu:ڏ1dGwdˌ/Go89X}Sk͆|gĖj*@P_B9dh]ag{D&OϹe^.g¹{ 9k83~o 6d$Ce ۝̎^grʽ?`<۳/A)qʶv7Dv=~\Z֏AavzUZ jqVƆb&xQ:7 l*IOnLIZOkMת̗v*3綝0tS}Mth/`4ӛ֙F)9M7XOI}JTnMWiET&!vRYrWRHy3oµw + +a=.$ƊUoةa'!~iborJgCѩ@zw9}wP>bW)hϑTL~JmE z4t|j8B5ْKRYgL}:~(4PP"%]a.T +Yj-ghm;)ϸ- )|iRk^r֣8VI`*n+TT*=;/tf-8PX*5<%Dž?X v&tdV.w85Ml*Fct̨S-Ȯ3o{ڙїYU%( U 7ϷSN]emN +;;g~(ܜwm:=.z&k'ʴ*E:sCi[yj`G~oYswR: ڴYrnjsְ(^r'[IH}BO.kU|}(}Þ)ET:^5˭ C'&3u1ڟO^Gꔞu:@귷u0(w)9͎ / ,^5X7oE{/mKAi[W~͛eQ{r\-*}a׊ aΠpn_hvjg :0~tvR6z\Kf^5fkR%JU3VJZMjD}:?ܿC-#DzX>^$y,aGL ֨1՞EB:̖# OB]ē~O;Q>Rl+o]}UYBPźF~. a^K5~ [6d{9*Dbz|b0x^(;( +Z{4gI7;>{}j+7圲g&4-+R3咵R?ˑ/֧㦦n9Nٶ#ä {'.N6;lJn/LM'VW(Xt1H/Y,!w@̚g?VUdnBh⿦PGQN:‡4l3@~ܜ|X e@AA~j _A>@2~R +B=a.Q|:m.;yv73"''fǠxqM\G  _S rlUEro~|X-@} +D9\OƳdsNV zq/]{mI^.YH}3Jk;-a5x_XL(BI߾qg>kf2SOv?Ҷ(j$VT?E۶lً3S4`֑K.B T} VDU_qԽ.1]7 />:NUkge@gB`<V&7uʢEdc&6Vvher&{@6Ii8l}*F+n7ǹ-l + +Yg +(֧x1 Ӌ77{+ҞfMpS"Vc_ΥW ǰq +!1?_:'VɊ&..^Sg+/b>2(ּ ^ơO۬{3->%{`Dزngi\>;rDx`_ k @.|,_DPbVz=ew2Pak}Ð (Uccr a7z6|;]5ponޗ]maU7ks?1 ˯>Qbl5ZŽL7d{v 3;Nw i6x^zX=)|0 Kw6.5lr<kԜzH/3`+>7mv7V(bD˴v.vMg /CS\4wFAj8^소iM|X폷$3/2u ~ʭGTt" ,e{cjZm4$6WlH#Z"v9)Uݤ#F&(^lL +> 2cXO4sx7EKx<]Zmi:v}_1TsPJGK扖X<b}ԅZP.qO/ڟS򔥺3ݻ3<93ܝ#h#HJ?;[J^l7ެ`rξ~}oNb|o#\}0qigUr#Pg#?L26HG-6wmƁYpqn)x.VK93Ȯ1umk2 +bmL>,;Wg̶23u-yᣋȴo^wwSk?{A̲rXD<m*%%2z1Xn͚,%c{O$̍iQg"ژ_n+bҫLɓ"ggV֡މ +cGʡ=/}Q%脃yʩ/{<ŏdz1)c)yJιKA*5mSXa*֚d$kY_5}>k.#(lSknlLQp\jvile0׳rxBtg{_^[a;+z|iڽOfβM[>Hz\unpPS~[?J:X(qWU1ۃqm@9D+}2͘ ]\!!J*kHc"hg +8,Ԯ^T)j]~h2~tu+5œg.>JFWS6?f >hu+E!lзV oδeÁn/YbG̷P|) 1W!9%5tV],xb0VSm&6` :K:5/ɟ6l|n뵃%^aq.L6׀XO9w(/{+VILkAҦU*y|bܦo@n嬒DoqX-O[87iSmFgZa~ON94|u/uXگSĖEzQs1i$MsisLry .?L:i΀ݜr%A]Ts$T-LrME5fܟJD\o_r{;(%v-omWwRQ7JO7{fֱzޏv\NνPykxtRv%ѲxinwojYSmJfc F~eTYKRCs$3=z-b O.?s7ÏL*ݱ,P}wnj.:]i:m5Fj4UOf*r#d.I|Lݲ;h-q&s-ɔGTٰڦq8^4Mɠ;4ZS zb1vH&v9}aM5AI~aӚLj+3ɏS;!.YhWBM̚?9D "ZR ôDۅ!` 6B"tFahz9w(jNaqdkkVT;N$v06:$"eok}^ș<$:dK+@ >3-87}T# 1Rǰ Dל{I~*xET!ÂJޮxcxqH)}+.Uk]G )13I_Z~+Lx3g}[+ fs.MvQ(7VEKEb&WX=CXPSڃtG6 +CLJuw+>y^0xq :.:X[v=ִm~X0ӱdyPD0җqIg8cpX#ׄ4ԟ: 0KGHdJ_ pOO*&²-k|ԽfLn8(/kS +endstream endobj 28 0 obj <>stream +W GAnðc C!Ύ8 @/Ćmi M;*;Azg]:ƒYWn^,%k%EktSa4Q&ϼک/mwپR@ 2]OoܺJ6 ^9gDN5׼H9;ͷuCWġ7bZ`9ə(% 쿱CIR6:NDk'7?tJohFC%"'YF8,'xԇg._~.SS?nok +AN)*csÇ:eZՕRKŬΦ,ʈR]uJuSDktQZEZA/`N3-Mۮ˷^Ցd6 ٬=̹a6wKI'>=ԄW/#)^}cKE|^ʋc +<='`wը's*^@f'^p7S̞W4fPšϯ]~ mc}t|KSض*gl͍L"ʂo+ZvZbծ}}xp`m&nc{OkgۺYmt2TiV D t:x ?7:jAG#G||O`?\3-RViϦn&KwiϢjNl˵ܒD.XgTP {7Mw󻜝]poziKo_-X~5.ǮNcwg{<[[4ԂP+A +ۉ)-蒪N[5W}-ViֿuV%H$$pOw-Uֺ[pf&5׍jޟctomߑFskze>/JvmI(b@"Jb/^*JUrs Ϳ[shLfwlHT]p1iM3{v{z2fCNkj6̒G<)9fITnSpTHv }o +Tv,Tʊ< ~fuYN?mݙWYИеT^ylfNzǘ㨆 ~YjCv_¯.M"R=D8\I+z[AY'Kj2d3t'SGO4o Ɉ"NӅFcl)QN녜 juWU^ʖV/w +\E㚾NG%NѲYs%;oG6r{,|l ĻzdrsE4o$PYeNܬx͌sv⏇*-=gP PmP}?>2/a_\tnߎl͜**Q + +y2*-/ľܫWV̤˘O2VrN3.4FP%ZHt{# &^ f4]6kֵ̾TS6?啼r:3jyTZi1[X#뚒KY2%6}̿7,Ol9@5c! gͳ>מVadΛ,pZ!{ G弒S9&)YM\ z"a飙*<\ڙ/Mnr*s:b S5t(͐Ogw^[&qr*ŕ`:KO ߃畬1<gBb}sڟɴRJ] s@b-MTkysyHY3}X7X""W07o< p!TGag/qp1o*m\q{.0jßv⟀FuI+#@^Iq4Z]ٷh 56; ِM +g]b;@ɢD.q& L aNtfnþʦIh]$\ L듁ph"K+[ћ:b,}by@zt?)ro Hl ;sbGb [6^0npvWQ_?~kH$we\':K@K|2y# +D8L9#o@W4^>%-8fCt D,DMZ>wɢ GKvz7<6m}TߎVqʺ%g(?]|Qǖ{)ƫ RMUOq0sf3Z߈?/3/v +B~߁[W6#n1ٝ_F}S΂L5J+|.)o~LQan2&Sր9"r3)G9~ X L^ϻp+Ǘk8=Xis|QG(tP\{?+*.ivp0$R u^?]E7@^ ]ItHn>EqDʁTTRc]Wf[`3iVc|NWZ",izYs +ٷ5mp5O +Wڀ,zu07=H8zK8(~Wѻ!'/j8rcQ=s~qdiMϴǔ'+"3.fq_8t^-g)ξ$oPPj`LA<@rs@SPW^_R4FXF؍h'έ-ª%KiB3znV ٴYIk2x *pڿ MM>Hm(uDW޺кꄤcި^[kH\)=<{v0X9HF6cQcf#-kQ,?;';u %IrpV&Yk7n¬`\z;,bVi kCEdyFER0l8<2,|}/9ڰsb})=n}Z՚rlQUu>G¿l nM̌ՇT2. Qxi0xAŜWe_LlϮSǭh1Bƥw=mp=+goxm`_NzzG/7sNګҫ)=VJ`zWɷB-@-+$y"}uZԑbz{;4wKPwyx:{:Q1ʎ`Xxje ]F>]/{hpykHl?H,\tCjFh8,|Έ3A<+Lc-$Ƞ6..5+Gn+X:9}R]wrjVZȝB3:^չ>f{LI=UӲF mކ} ì=QCtǽQj+;IUN>ַ|>ތyx#8^< nbwڟvyL.wcOCg`3K֩Aa Wf_ +_21Z6dҔpˡur7}Hg_NaemLV6&@ pqK/EK,&st !eԄSƽc:=;b/TR`̜K~C,4^tk[UZ݄5I"Z*Nթv-n٘qlKYVPފa@a-nw9;)<6 +Vܮ}>-<򤯄 *| zX.3!k5.TlYt+bH9]Usdc,~?_MHǛ~"Ujok\^3G_M{6.|)4XlI>5-rmZŴ{vLr餢R<2sm-DKbh` +UT|,J'~ޞ~pΝrU݀k[T.+/Z3崡6kk/>AX=VGo]Fk)rЧҢ6rн\PZlI):6'y[>/'Кp)yMI6=~^zs*[qR(n4d~c m|粎1?R0zVZ֋z5[ipנ6v5v[ibB'wުn5CUz+Swt2 78w֞,:XΣ+kndnbwaP69=(DC"!n_Ñ? \T9O8Ź-[6U ȟ8_tҚ+F'\\"(qX2/_ ++;W$Nġh[.fD q4;rW0s޲UcĬoݪmH1إAe@(Wn$R^rUWsf*W0O Y͓j`_#fuO0N;|nnM^ic_N%Yk']nnE9 .%dz@RVZMnPL6"]{wαf/3a+B8ޟXn䫋t}ľ+Vy7BX6fQT3h?OJmKvSP2t ˓yc"ʲog6S$3kvWO0!}a)c3 )eM|~Ɔ}eh6Zjq֬YmK 2g᳏sٔ_-.lYq\2E+b:Rr`QǠ1RH#EfH.EݙD1$o*~W#Ia|Oj'GX*#g%”MnLIIJExQʡd+@9<8(eX%Q!# k! @ [ +ɴ 0XXp\ ๐8] g2ph̖zm_iu]N]1D踗F\Y{Kr1s/6 [3zko(,B_s# e2|߂OC%1J@4ČK$rVk!GeJc#PfDsty3hu@xG0X cqI[zpiG]ҷ(,J}F Za]ӿ($~ @P*3g`2L?Hlb]9I/ +gRCӔ93lqIN~rhUoKͨmp +{{ Z Əry-?e@9|ĉѯ@0 >ac1*ϭe{{5un+%{:=M?TΤCv缐[[bm^A2aO̧Pj6һ:b#@-A6Bt/Gi -7bzoɾ\Š]7~e_2q/esәQی͒۹JQbJW-,ܧ +~ 3œ~Z&=`gFXqg3La}c॔סP%d-2^# 6rv}'.!M%VΕJ-)mXCs8_fCf1 8e.mӃU)yz*E.ھ~ݳ}se3=^Ӭԇ]u~Ki[:2ע1:!혞aj=ӏ,~Ogԗ4гACw1~#ϥ' XG[M֮}zil'snZo ~K?;.iΙ򩈰 :ǬqlJl7-X70V#HwFפL<8j~xڱvz>w{kN}R VZx-2?lf6 +سVa&8 +͏"x;Ii#z$@R(UaBf~ 4H>w鼣F}ojsB\G;=~Hctso0}1IJ yj3˲tZ.f=u'VF!y3h=՗q +cx~o@uO_(jCҒF~hGpYi{gu}ukE&X-f98Jhb{r5|49n8͖5_Âs?& ʻE +@[ܾrz>v(R}ql!]hk;jZ_aG|ٲ2hq?:HMF se_(z8*_~|+Q[;6\'La\ƃoeu_f1Y6}ʣyݓII,ٸ;˥ OڟqOޟ)qd*:GNx:>!&-õ'`9 f"fs섭ƤK~lV_3ٞU mNQcG*I@ _zj~S&|F{ЅmI7Lg N%/-dmH[ZC]qd6I6rΏQ1e R.KYc8Mk hjIz}MW^08S\wPJv?=7Aj|/wT)_ߦ櫝!甖ޘ[Fnsh<}26B̤wkMyIlaPUgV1kwh|SҳDߪ1TcG+(ָit{y0i j:j-Y.UTj9d5^'kt)x7wrNu_DGVpx"b#qao0\ Zʵ^jp;fgWt- /8DJ'%R gGĦRҩfT+ VYb iK>͞\ +drA7t= +Rcsͬ[cY6i/99T`}16|u\R-g@z?n%%d ;3ϧ_qrr+FV>˳KE$}C Me/aA'3},}i)vM,RL_grX+SqAlXWgieJ(| ],>΢db%](Z]5,>cy'$cfyfJ JӏoCjA B:K+ʶ<Z#|oq˓po Ulrz^ϋ:;GjT}f}769l&sݗʻ=G/CTwEWn +< JkBTWc a*hϊ=Rr?K"-𙶇BBez+?1qu 3A|׊PJ]uR`` `5@/Jž +@>bK 4 G{4% OobS_Z'Y}s~:\d[?MN;FV +sÊMG@ +3>ȰοOlbRʍ!;( DX >ު|! K +7l?IwV%(o6nͺA+vH}et`?X@㟧` 3>nU -KP; +*{sؑqjϬ!焋o~+z-x< H(LsUa3៴ o0ؚa ZMU,=Ml{+6&^zMXDG׊-(߂oXwruz"9o[Ob2 qgp#z\g[Ċ+UM7}wϨgסÆ{/= <^:L] $9|zFi|s& c7-MsnLWoLguQ<_ݖc/  v;s=TࡅݥuG-7ܼaYK0KA Z$vﴵ_eZ[U[ {Op?rN[:eMa {L/s:?=8.K4һ|ZuS.''~mfve@*?ሏ j .wqo!OcSl}_r9S|i2bctr~|;^{o'}v~WO-[?@ +,nU$ ;8+>5sJ=㥩^r8UX8La9wviˌv^;^2~۠8n&]9;Z*l:|\Z، s +*NTOh{nfy\FOc7;bqV%mC{Tvjlyгb9V΀n,rnSg~vHNyOg?7Cvkv=8%^|w_KSpG#w=upR./;g:ujZ5SRB~1%rfbyϪKWlosw,uUpE@,E@P_d656at]Ʊr:}|S2ܶ+Ss0RQXtc̚s$3ˋgJgTJsٷ&ٝmzydv?Jw+hQJt'hǘU*^wZW8WDk +yiЅŅ ow--Rژy\(q*֨Lg]mE]׿cp J#Fdz ;jUAOqem^e7Wkٰ<\ yv?֢';̖M1c9W K˕ܟeWPmnr6j="a^vӹtk5cԖoU}.ٿg)Pabf ]9{ĸow]ι=4r<آݦxaMAGb>߯ꂾџu^٪zU{N@n)gT"vڥ hHU}݊7fN8xqs3u% Ԁ^v@o/Bƴx;m$,9C Jt%EACiO6"Us^s5_|um45kn֜%箶+77TݫAJ%EvSMNmj{dAg00_M8/޷ÄɨSA5_A:߮tbҚcP +[W+ʫzf;VF˚ϛh4u2Ť<\RR0C&/20#G;a.exzfk]jydOa;zᙧNG+YSZ-XeJjcj+]5Zn$Gr 9(15}4f;f+}]:%Ao%!z_.gAii>d8GEg/ë}G֤m0 N%Ũ#YZ. , 0xqݮzҧɹ0 !uX7R+rz`Ξ5WGa|vow5ZM9긵3> Z7 zxAC/"2Ka7ymKB ZfUjq< Ff+MVq6hj,{&Df?Mft/nH+Dq|M~Cmڵy˓T\V)ID.QnW<4thQV63jL~CψV"w*=3-ES1n Vmgp19ɢ;Z5je̵i}ԴfvKc^׿b +W+|;]{Es#_Q)5žuZxßX~N{rW[seP産vyigAT_(d4t&2AQ抑ꅘl?[;1 OQ. +^m5 +m0}m[m0M"E57vWUy_ș\Jn.tTX Z~M`q?YeS8Gi5ow,i9}8A-xڀg8 ep1H"𑭗KsA>3(J*$a~gtY^q|~~y&s=໔ 380o~o$[d8{ZHrbR f*=i`S֍S/PBܖ^CyY9͹Il̨wXڥŔ2kc;sP xI}-9ƋiUzGq " +z#"$i\UR?7v |휛J oY%>!Fj~eaEY%\./y=7ϗJ@޷˒#r yDs +D0CdzIbU )<4@ P](Z?'!V ~ |>o__}yb,B?kO Jy Nd(vd1bH[T!@T;4c (ޅ"c7; I/l_ +J$0K L* X'p=>o@ISO&<L4sS4r^iG~<*f}.ft e%A+fvse&xu'(Vd}DD.4 %g2 ~gWX1tȍj{r 1lr\xlޘo_l󇟮oztx&L2O2dJEfNANyx'Vq;;\"P`}v+uft8ϫUq^A-7P{PtcѯwV m9Mv$C@tr!%&zVsuw4my8Ig[/cbzsjIA`g?59t@P(|BMtCݠ ̃2H(*K9 yZ +:5F> ŝ;"NxmawLvfE=bP,?d +}XKʥ`eK0髦ii+˓hdo.ԮV\pf~dK4u:l2B! p$/L>Ͷֲ>YqXC$#IPk Z/}RKr|2 1#-;POZx y ]jh.պn%\9vaVmRyfrCc͔fcyFc+'ݾ`{_|[qk>`Ec9Ư^vcW( 3NyY0D o`N Ίd}3zXWnًW)\2זiܦBxn(ӉRRp|?ɔRJr ɹ%!ud|Hf<"<%øѬifD^7_9M[9mnթ*ƭ8^G6;dg_2w.Ma>Jr(v~FG?[h3^ymZr9Y _參> 0gںl4h<`lUqN"OmV\Zt!ɬ+JD;4`F4 +Q>Rypޯ-|fq[Ȝg4Gz} +tQ4;B}Vri(kܙ{4SEбނ/\ +li2%*0/#&Q.Y|h_*VdG3q*nٜLN*MDg䍼ȟ霒//2F ^5ZGg|MT4Kpc4u/37d{Ȍ[ +lJ:;psPC1 ⵖGhGn%%J߾8p%ש +EOڐo𜕍^<Y\E[EhM}qǒR&Kܜ,>xx2)?ki #[s{9$`#'p3jfHgLH>^CQG\ 1v}Na9[mEґ:r,׏LZ,/` KHtr ,%R8ࣻc:LL/o^L0 +{-l v_f~Wdߓ,x?/%hnr_ 굼L0;ݖNNIr]~zRȟNq Hp914 70Ò^=jz[Ϻѐbp-d[K B9A=>Cq6Q,b~#Uꅂ0iL lZ1*f~z劝A5 J7Jt"MC:3Ct?;UaoJNݖhysiݞt򔩳HCƚ^uIt9Dc\:Ÿy*L\g{џ/?XRA!yF}*toeTaN<صp"!P2?j?24nsju7ҡ*#' /T) v)63(<@xґ)Teg [A#g kys~c4ϢzcE!h1"z-(^ΒKG ۬l+I6Z~1!gC=# Y5|4Qg>z9L;dwp[OJ{:JMQU|KvTw}?|M9 Fek9-7rP*S5+L? +LtӒdzS%!P/K=Ȣ;GJx-Ԯf~:U50az2{urM!bJ֬B=~k6.Ym&5Ia WUFS=XqxŌJ3Zybr}HK6,VUj|yn]]L؎?j@U6[{9Sori0)s??6zvTM}ݚcZ||UKTXyzTJO/+=Ot-oQo7(3j.1Fk-=՝2gqWqΦZUr䡨[|9-V5JQ+<tzܮUK܄]u"QؤYYtcv⃹]9+ v漯Zn:Go~Ggʨ7~h* 5\zpŰD'JmԎ8]H(sY=-u" ;eg:$fnG%Zw*`DF|Si`{9f E4IbYghoCo$@ M e?SroQjğӎ?FV?,gz0btorC6[No?bU)՜*]6s .s~П\+s ^׻V:$"U@xNh+!XO>S?q+c@\?7-/sW?R8 PCo#tI-Ĺ * 4 Tq^_mxWqp2@g;`_[l[\e8FyR<;OgY@g@KsV6ڠJrw,h9v;,BNʀK'@QsnZUy|pKUR&b&%BEñm樝v:I$\'>&0Jஎ%pHZ & ]CƙRÏgj+]đƫG=[`nvtx}V[FD?yA`AMЊWMnHK&&@^Nq/w"^'_Ba^*ld(;GBoԻ#ߘ6rm"2w9+.f~W$QVpHKjXl$N%)%蹬'#<$FmnD&>H.->sGֽmGM[t|\]IxK^ylw7;g1)dQ>ɬR<ܟq!fآ]`nzU)0)_ VܺH/yVs}걭~WC5wU)?"gnwi쮦C[ޤel r|!:$KӴtG[|5,ϑrߞR JupC?ξާ9yxR; QせC'#ٯ:&#ݶmwz5Qwfޮ #zV%8oLej=Ӵ49x\ +H4R+ǓAІgU큱'7_WN4M[vtp6H;GN|:փ0zVT"%r}7Ƞ% :8II:d_zNT^;tͫ7O=) bR8Ҿenal5~{,5WRd [}[+kv^VjKI@U MՌU.`Fy=)5GY_M ΌocYE+b$8IW멀vS=NvoyO9yv%n\ocX>O󽝰YR5\,NF|VuB23|ܞL*yVsUiԣ3BWd4s.[۪MXCTlkź]}򼬽\?K%BҺPd ܾǴ3899ϊ2VWy̨queiY>\ۏFJM˧T摁#89oM!B/kig@_-UAC.R9o:x[G>;(}K(!f5^Ů%gDyӖ{,)t*6bPU; +v^/mxH~.1tP7RCL\J?~Y +*_&ދ6_j\FK/jpU N :4'*'+ly"{ GVwϹ4zή9M&7Ҋ_G?KYpBG&t,IM;d3,ƤaX*e7`jdHZ=YDlX6XT؀K)#sӺ\{}ث!U壭L3VHכӫroNk~-/ə/]{E`9rH:$M5$paR9Wqd+!:#Z>uruho1U^(~H vmK/:ʫ*w4sWsw~ʟ‚|֨5Y؎LaxYD FTGR0DiהFZڅGY{<}wod^6LG[oPD / +2u[[QF񜳋ba?+ xq9x\IH־r-]%O=L {D.dϷT.S84 9b[gU}-`Mf^g9mƾb؞ UaX]N=Mx&)M4Ktҭc7o\i5>|Ok=zRY7B?`?E^ F ݵf[\uԾ:evKQ"z*ϵl5c`( 348 9aZ~ nСT|҅7ę>/į.TӐs=0ce␱M:[KHgQ$jkܟ/Zz,n~rԧÔN&mgtێfڣ\jN` ;?ٛO@rz=IJ^nL\f6poXdv֌]OCd#ԥyk!#ʈ giWvۼ$qi-0mx.mOmtR + +ͷ],6w9pm5k]ċpwz0~Lu;CEJ %>jDr*T'vqFZ:h0lYu#F'V7?)8l#T+wV>T)YdۦQ\6 iWؘ$?Z>DU' ٫3ٟ] ĠlD1TU`ĵO>وRRyd䲝k GQ3+pllTHDm.@dkD=Cd=Qd<gvA􇴞j+3ڐgIܘb,~8V$Ӭ3䳙ٚV۾QBC *BdMUo$|V |@f:ԭ?(KZ{{ ++8N"=-q⼫q2k48%N7`y δXb 'L)(?(l,eq: 5iwyb?U +''wlqg3 +$.@R< :I$s!N^?|R?Ob TJ N yx$wgϷ (t:\3j=p.S98/(exg+іȶjo&0}&+vh`yWCop6iůoYo%MWFu;0v`6((oܱP4<.%y.;[n?6˿Q E&{ O6HL% z5zʯ T:21=u܌v{测KZ֖%?-0fIN.;$ٰں[B<`pLi0i8hvKIx^o81l+A|GUGOOȿI9< Pӵ6OѲ~T=vlecd /_ a4-]I&c#.ʤ}/†Yb,ܶݲrF܈K <*ɧ_wzb)k.36se+]`Xq/Γ/ [M~ :% zfJO<ʋӒ?f\$Vڞ?YqlC.C +e/Yw7Jn睽oޒq(Z-%6L-6%AF[=Ayn3f)C,ˁy[)Ѹ{Xԡeo-=[~P:ut]m׶6-okq&}2 `C^n|kT+=?8,lJ/ r &%{JM6Xյ<zYion.ӕwWoe ĵ;Rjkɕ 5lDV#,<޿qye FyKaAI?otBPGGZ Voܥ2>iY4|B۷ie3` +(UV-Geʕ:b\NɨB{1/8=y"ϐib WyQ;p43$SߓL|h\˵}hڣ9N46tɂXkީORL&-JًGy ҋοE}TOu{+3gN}l S>%Nέ+ T +B/WmYB?J]9ծEVOvG!LXڲ|u{?C/x$u1Ƌn.O*[2mAS_r>L8Vn:40ˁwH+PYIgx|?p Gۇ\u*ykMg1W&3Uq;ɳX0ZOK7w˾VEv>cue١McL.ܱ[ifۍ;cB=HIktMIWX"Mωuh:Cn.f]СvT7RZŜ +b10_Z[ +﨣kLOǦ5LBQŖu&ҚCd"A3W~ H؃II{xv|x/Ӯ5VA0냼*9އW4yϬE5ӓO=vqB:D-{q/>*|C*#c͍].N7%Iv^)t K0XQA3:齪r6씻C邾xuWrǜe럙i՟U`Dv s$mGE.iW#'ptUبtه4؇$%՟sQ~$S8zUus?el][00sE{>Bّim(ڞ(kIہwcu( ~rzg=:`ye܃C|.a\ӧq0i7f\,xGzv6N ;ɜݞ,/ UX J^|1zg*Ħwq +ݽEwף nGa02 |nKӅyFNCFKߘכWˆW> :nK77,azmjfCAqz*?bΒtmgcid&R7ja!ہO0YmsFyI +UeNE)/MpgJ˓%B+> f$r +'尘>zWo:]^S9D't83azG+Weģ 5n Ό ljkHO}rl:u?Yhiͦ?Ξd9Ԃ?_"rg>ny “ǣ/YXV{-!zfRM{N/!|`l\\,\/ʹ翵vv:gյ`:좜$7 68aVB#]\7Y=wC"Ww<+%/}%y#r4Z74)xkp[w6.ҏRA*w8t(q=4HwƼF 'ZG<XLPlvrًs;{v[n7*xntpRovuSbԨвYK&[mo5r޾HM~%7nQO[2f/lXAM]մ덚X,~5\H[S~9_4@s7.ѭԾo6]Wʽf?i+:Ur/RX2,f(a`Φej.#dWoga?PVoY6J|vi\YRC[^{Uv7f{L;$ؤz_)lg(<..M^[cQ}u(<)XxcA;Va|*׋:e6AX6ZP13vN˴9jgr琵DyNae]vegk-H$OX$5/:wSYyT!{cՖrw2ս:NGtǻaQolΗ1l_mdQyL2Gʌ5G/wN!ghy̋3*x^T +ܡ EmkI׫h?Y}C@iwKY'Ax\|$o ÐgpFδF#"! ZCτ XP)@d~_'O68-)RA禣A継vzՖ'ԨSX>h@?Ẕqi,2xTYs=`u|[y dA<$`k Ndn/w?N3NC$lzwX'F lUxw B8 + +m#'0 +6@ 0W;|@"1п|W94+Pg,8ς1TW\ l,-@%ĉ~Flڸ'b+(`[DP@2uٷ#:LK]~!MۥN T5oA^f eۃ}~,O)}z,q._."3o8V_d  }'g n?`#^>FɹSujGB}}H}F׽\.*dz4ѱcJnQ/BU3,v3ɉk/pw챫b:]%H.O 3A(z5.mx<<"XxL456CK,o_< ݮ›1gqt/\ew::];^d+s 7q~>*YǦ/uor{LNKvgoݧw4/L~Ԍ(Vs@~2S[ßĴ%jm,cD\v/Ɠ2x؆f: x0fJ|5Yr8ﻻm/Emy4 + +u o-dwX~w۪fw`c=Yt{7 +PyzuY S +eNŝ[c'%嬙$s^}4N ǎ<1Pg׻i=>\6T\,3ZT[&Ry*EqŮSN]v3Oz4s+cZ9%غjG5C4/}RH8!?%Ar:;f\_K50,:AB;ZU 2/+eAh4jz\*/Pn%I _[wՌ58ԀԧE:T"\rut?}u(CtoTW򛩇;ޑ`w {[<Лo\츳^A3kcrYm5\Uߥњ#Wq/O}D'gL'UjnFnH0$bV/^=1K9)/ Jo[۫O[Nͭ)Fm lzkLۦɔeq 8p{M=3$V!7M:Pgr>I9 ]&r;\ rհ8#Ze%*{V,]̝ +iHvy6 U'8̖~̷W'Y>=} {25$_[cݰXX!*n{8 !glz|Q]0\E^(:4YվzLGLjLGxLGܔ={.%M?KA W} fX0Z e6|KϠbIvKm* C(f{W\ZK~Q#[XKb-CkHord =v̲^Fk*MaħSmvĞ[`a|']3i`֦5N)*3Pw}K*GGVjU7\1J#m|;4M9RratG9! +~*FmKDi9؛qR8)h>_؇,@2ݵXIå0uCiY5M,XuG{>M:OV-j,NE?SaI"@p\]'8[yFSd5FIffĈ\p~-̊E,!8EYR͒hgc>._N?MBr"V+ynUz фbgcLިa[PA:]$^HPUCjCȭ| }T5~ C + {:C |3cȞac~q lhx 쫣ERnC],2U~νró+tS;ZhE?  hݞ޳Dp-<vsMnCMj0jӘW#if5oIh@}%njas,3-nGev{(-۸@\H~oăQ@]0Äfg^LeƲHG Ძ7L׫$ nm#ZJ0Z?7a9p]vx`h.#K/v%m|2Be,\kZ3P 3FLV[G^FKbi-Gɔ]ۄ^Jq<-ym ɦqS D-2_Y1z+xW|!ZQt;B_Nay|FPT,]+@p.+xU` + R(p;]G术΃b~9.fU4ټb{SqBISIUdw @ "hTZ +ے}.5͞)Za&a9@t :YMʰ1e/9N&@\ܷFӥ2LHL$ғM~^+3oMygQX$6=$x&A\/x 9,I$@>@ / @@}ʮP0(f P&z jԾr]LatM݆iP>g[ŠNz>dz:}z6SW_ BE*5X@ +@K\=ʿmT/y u~@J7Ȁ9Cml +oFs)[)q5kwIwd[|b~= zᗵwi{\?$^Zik?n`DEE;FsGlQđr˭(; T%9y+_% sSnH',LHLW&otI]j49b$.$Jh`zKBl`@'a*KIt2r6ϒ( |+V+e؈hkR^>ZC/|S)ΣOy[rǶK&\2Q,.I; U%8&ԁ O ͟~?"p>(b? +6{u.G^@w?g r~nj_|>Q,j(n_R' Zgʣț(Pc< {C7bk[N.O򽿌ZÅx8~"I(,C;$$觗tw}!zwgn ѻBWrGݢ~jV"wjO+<)ROy6Udcվ 1QV4cxouĭGotD<ёtqꢭRjC ߝ/ev{5ͳ7DO2[氚UnݕZT7獮1iko>qjn"V1E[c/G^^ɺ!0q&ގ5*6o8?K͕Yfþ +[u`&B)]lpiB6EjkL!"X'/%Mggmqå߸k;97J:-vBYǿ@2q>{vӠmrV{g0M'=u!VڣC=g\, G}!pce- a01'dujpSMΪseZCRCicgpu+ qvԚv.v{ll-k͆Ѱ9rb%!Z#sέ*sSk|TSk\[sv.r>r T )C(1+@8?F?FY3:r-mfF +=TS\^ZEvn t|ӟx[9f39IXѶ" 'Z4H3zx+WHʍd4`V{F.{I"y[J*q + =gi=q2Q8DwVrzzL S9鞗~<;%0]tuuhΞY)֤s 29)[Fŭ[%LznDUgKX[vl͑E7^+j&6vVK^,:/npo9Fey1'`1-ݰ4`;9z|g!Ҿ'cyd!O?z:#$ϳ8%ؓ**fr4wϊwzC}~Ray@Z3i˧JWwnZ.cZ@2ἜOGIu,/u_8MUc[ *u`\ci̫w4gнMn&Xǝ(ϸno]Xƌxi9\skԡ䨦Ηd˙Ȗ:H}蓧gFN!T3%w%5Wf!|Fp]&s+`aA.V#)ze0Q. E}OĚuٰ\uf܁{ĝR,y~iMY8A"Ҧ?4^"/i8U$xElt͊U+r<DZu'g?a2["TL]W&fT6iׯW֩aqc t1 ЩVR(>:(A4٘#n?(\4]Fך:LAy:^8/같pD.׃[. FV.$a:񀼾Ci==G>#d`/t +h*#"ۛ OeonW,vx=͑RӮ[~n]_ø5\>-lAHANvqT7?4jS;lb뤅S.#^D^zfX6r_Js*/vbg*i!1ֵ=*KuD ͢ &1K9-o)wo9=)"F@/G@t ֢12us‹<%;EٖUUfX^"7>5/eVxNAKX l_x!,;\>:O||7~|M^`?A5yMVjR.؎}ԝ 02_n#8W|]ܬ ?G^fhF>%>@dk!72_:V'$-NR N5{9/rrQ]r+^$ݎp[!DSun\j92=b,eVH309@\_/ N)(iaPF(f Y u2u;_I_'~ CWJ>{R*6{28${+w^>W`brT'xv_/5^ z;$^Y v 4)(~1`D^&XQA;|Z$ k+L;N>)dO +;prZ៵».8;6xV8q1HB,}F}N Ԁ!$0I@.d`l#XE>~M+ +sVOZ> J|?-3 #$޼묗DY`Kv .` +'_- ]n=yyXAC5%wAၪ aas^ތksk-N}_z¤^ìĻkQd^9 +z5@k><5|@s߶;]kJxM3T"߽ip3z\ҹ+G.n +&,;z~rB1(&xפ;}='sb6ukAWD/\={,隣&9eMp2ա/E7B _lsc3Ro6XU0~#?>0覒[F{c$0 ^=kjx GNxZSmTӫgT%4m/5 _U> /--x0b.(_nZuIhϒkчrfݮC4+w'9nusԣ ^kgcSYc \䝋X&ӢTA|[UrgM߀&7eM(.z~45Ŀ/%k h%ǩ;XhW '~Ng +7_ӕlY4\1rfnRSX["|3tR'0}߇X~-+{#!LshwӧeXJnoiYnUۢRd˝yRgɺӶ,&*oE?o&x&lfm>Z@%Ɍ6MQUm8ӏ1n5r`aLǙ6ap| +ħ%T[@$ ġ$}xpuxZF|3qۓ,w)oEVp֣v,\ +p,Cj4 MvCƼ~m+tq#2?3 7L<:;+ 뜺.v[fO+?osvpk$iW4˜IsCh R۵g5ڠԣL"QŬWA Ui}2#xi/@>ő_"@~7Xϐt1<7-joVH SPfӽmG:=]X{ Aec yP%hyz,ʑsJ3NHsFp.P1a˫̍N*a}Fӭzw(! G50 F]Axo;6vEr7.YNj<.oYK0#cs%E*K7aÃ@G5]|0f^ڮ?xܴwz;y:^6.֧(nE\8bF\n*o+y^r˹x9$Yt*S\-UrۮQ+=[v@CA5I +ں = qaWÎ߿:4o33HZ|ަN=e< at;]3.2-L.ӱ]Ιgw7ˋR25yJNj4RV8# +/,7em],9RKT +wsOw`mk<=,--\c|ZMjn='/0YcP|sC]3i͹Ku͓ՎH $J!)`B+9,: UȪSHmy**!P\-ZA ObB+:~ N@DL_XRmxSꀠ g*7'\'j"$u&ZXuv>m]j8z0 +>$ʤ齗&Հ4797r=ZL[}>ܩRl`=*IwtdœӼv>ʆƄxp]U>>|°~dq@53<|k̚&FZ%Cu1"U׸fq̈+'UN +T99r܆x;2p^v#\쨏u$uBp̱ 3!<34f۷X ~9s>P{Ui]S6) Urb@|)z\y+ E}io~1k՚=CZ'ِ\'w|}BL*+$.ѭs-C Ӭw/z̞kؾaoW XN߯Hv<@WU{Up :lh)l "K#Q"|6}QVg F(JƗ lo_}ݲ*+Tph'?q-^[-(KMEhJB#*I'A0+!3u&* $"G *;<[͢פrξ7)Y06c&xegwo_-6bĹJ:|;W~tr)f3r;. >+?Cnq"PGE[[qJŭQьK>]"EUO~gYOw9sas<&n'`oFZ},-s$3u,-N(޳C3>|BPvi\Sw ͡eJRF\ҵ,HJq5Tje׋[̾^NX>w*Cn6sAa!^?anMt[.PޣKzݱp8L龙#j=&(19/DV%7{͊MՑjd9౨U|cӬsŠt2_ԓsgR6kȭT)z3~[v0Q7&ⱈgn̷~աN2̋ɛUU>f~\.G>CSk{r9uf]QGfn=%{>mC6D2"$!:[(nswyi ʂahz%8 {ǩMkɡ=bwoK\AVcwwa_u†e1ts3{%?B-52d 2w4hKfd*Y/68o I9/nj‹q!վm=ҳ=maa;{?7!yz~.͌MFeF+Xy\5ɕԽo +w*4yphz>DSJq +|OKU9[E{8APaR!e %3x:%ǖ3(ץ.m[**Vr_ۃ+bﻥ fW`Q{6 k3AK)Ce7^I{$tFnu->T\{RѶfVf8"<@ $GQ-Ӵ,@IZ8HQSMQ5YCީGK-KuAOs;I`N> +O]*m6T "S_1`t6ued`I;% £#VH /akBӵ_KЫjNֹWzk!@5c!#ZZ*Y}v@3723) +,WHyb,D?\${.ԥ)Z ysym+SNQJQ\z}|Tgx1/kNf[X]UWI8~}Wky4ڰDO$=z_ɸLv:;8ܘ7t*U T7RLQ%W Oѹ`Mneϖ42}ڸO̺q(*[eq bRSPm{n|e0}~똝GC;>r~&’ݑ3;bO5ƋDg'ec9v`8GN~jn%[crUuSVɧߖoG+v2"-:V]aKf.h)lN9b-Q[񰧶=7`^*r_BwY0ȔMBy5g%9b߾±w/2¶᭗SorgȚb, `uT7l65(򔌛HH<+c9kոљoΠoDߵ}rYDygARٯ`56 )t^rvY>c-T#Wf-^ZUQyR&[ +$㭏_e[ٵcK +G _H'dH%e|}ֲ'0,fUٙ2 ʔ +tNd(@c+D^M7'Zm3q)˭Dd.D&R"v/p9k9,\SF,b~)TЙnQArM-#25)HLyVgvDbz\1{DrdJ>f{MX8xֱZ:Bfv`6 +o!`b|9- Mр:4w-]$ܽIh (ÿD0KKɐj[٣vU A} mi͞04,).=ۻNs܌6MčB qym0:i0b`bEjKcόrz]<_}v"=Le}$YMk@Z␁z@URX6#qtlz٤jZ1j}H#;t]4j5 +G6[s6nݥ*@UvwUVqusx3sCęť'%U\۩ +"dPh˗* +ޤGݟ^TjVwlVTjѾWhľ^Ulʦ\n׋~)ܙœeI|yLexx1Wڅ\zFL{c1g ӌ +.Ix <.y .!{&!w[Yj/,>X +ɲӮ2o _5t-NW8_1/&0vC 0#ڞN/q +hy)T@Ԉt uD 6k쫎pLIBZ¡ !ׯWXb)dN md4KH z +@9q ;胞XX>xnXXm?аB6~ߎ+᷽Ҁ+䛫ެxekyUWrGR `\0r0޲l/,_D6fYir^O+$xlϾ%LN\QqS5E3u( XNY;bO/>ovj~m=mfm?Y'Jk(dt9'{mXog\o G/n]eybO·{kS3̟rZX{ck'W u[Жk=Y~(L/| TyEX(W(N}Q4ҥrQ9=aզ݁.[#7榭Zymɒ?:-naebjy/C-+j+ -99+utg[JEP$gD@QQ sDWk>~y~¨Q6<=F,0OFe1yC*u qt:>ۄ,˳k !S*Ϧ3' yZ$x-q# dtф݈aH@ΐh[E`暃*OꑆC:}ٯ (UﯝYY׭3^Qg@k;c[m|JsBUח) e,.T7aٚt/ݽolkNc'tFwDI3]8I7r7(nX!IRn"i /1͜`7n8jâA?G'ý=:5֩.W"cʻ6O- !>Fb$QkRz쨹a޻Y)} wR>#R$[L醒.B;=xzNM}Z>p7s&VM$k+7qVµgnNs' Czzimtr:긠xVls7lzjk8ov\<Ahl@I+^9 B n4E[m^j}\Jxl^6*Jн)<)crthO(Ow;7ιOɸyoVRv]7Л8 ;03(9yfAQ]|=?L+7jksJ_StʞҚU/Xev YM~_3Q}NW?'bqI|E u]`ܩZ[6FeGHIkɳQ\\'2ZcdkeAV Rjo6x>FpYިL*ATNQRvl]Ҙp^G]}+48a2笪6ڭiZO߄Y6\Sw!W 3=) TLP_SP*e0PrJ6\*{\ZڻV.]6ɆΧ/u%{;nNmQ)j\ w]n[2eڅRkmݭXg>E ұʻTYMG629LlYGZ| ^07MyϪ#_Y؟"^?[Mۼ<{\ ]G[ۜ hO=VMqU~3?${ݩZ R Xڡ$'~M֜"JWgOOELBLg#߽?,3Htf)MGͩ5lSֶdMTThQc4P5)ӵ3VFQީPF*UJpx@iugS1'hҁ}Bg*wߞ  #uJ{6=6]Tv?J _#׺UA\^1M?O.TU[ EBwBSߏ6@iۉ-a:.ÅbEezU!3a{ZhVY5a:0Pb@˖&``Aj#;F\5/%cVjƃw&j!Þ|D#ܸȰXe8&I[crFɦk 'Պ)R*J.v%`ep&}ڱ 1:\eHVQٮvQ7m='9}]!; +:Vwf2ʧt!rٴF]L,/v{VN-S9miЕShm b`V0{:oW>!s{̲ܵukJR1⾿`"7NaLϥv#Z9ׯ.@91GuCn J7qAT圴 +=ft?##ˑ5#Uٕ#ؔ,.*!=ܻ[cfسm3SôQWefTظt:hW&OY%%bU.]S1ATao~￶X nΖ\ܷ,U YN1Vcg,p,0{X~by~c^MnF㙹zz +[&̀l -Eu^.Md.@PJ[ǒjrb9CEmy@ /n&mW/>mZ +X8RpjV$ߑ4B FhsSsErWQMcG.·(0z"bW\,M uZ9 +cp>ߪ +,aS4!/h ~B% bk7t.Ƨ$-s{󃝹&&B:~N]"M +[l +?WJӠN<,.?=_NO"s9YZ3>X@EgE]lwi,a `VZ ǂ= .}o3 eSzJW[XpV\˙PHun^=ݔ+$wAa;߬~/V=+n}Z[y\W$?!:#pbq݇2;W7)](_[y|cy`lŢO*?쟣>]z#ܶ +-mUdT"DΙBoEp7L&ӥ$I ~ͥI!IO窮LVFИ8b/1[RJ38t.s]y⿓^k|tg.n֝GQHbj֘=5߻dz[ +^ɝ|D oaql=x|k9РAY/$-np/יYϰ6\yN8Qϙ 3)2*㭑+|P#Ǿ!(F$=Tg4pM&=6^ȹ N % +{*ӳ)O'Fk ;~ +P%f[`>l_&2L#{mDݘBѓHp@UrL04~)AC<4z-;N^u/0) v1ʅT>_˾gu 6[;S=ox Z4Ve4ڹJ?OU^{3=t17Zhz* u9o%kBgҵME&TƁ'V?ܖ-q!l1qux_ ~NxNMĠI':kݚ=KSu Кr yqY[B^f(X5(upP$BOX[ChpCϭ:WXd =rYT^ޱ$YSj4wn͠Uew-[.]}6mt.\擊ӫ8~v( vD"xR<׫bΎ},0丶˚%?BNH6|kʤyou3;/mzaZnϚ7eɿ#v~siΕ ${{@x~Lܪ)falj2p8_P'zZ߾i|HaСaF+7bENk6CC8 4Oe RdRܙU9/rwm5ݖxlTdPev÷/5D5m ӅDo3-;17h,ߕLaU쥺n%K7Μ9XSEY61eJp]lT*L{F}R+IFLd hփQ ޓ;93vdC6ٺfG +G-73zNVݫE|~p{ءQ PX+NUv}iaV!EU3xE{D5y&:r~LEuy,KHE+mh&UbE5q>h/ơM~\c\tX6*k;\G^SKW {&rL}圭 ;^Ru<Q@BdBUBKhca6OBkBM%NOku_bI/YX L UnpgT{UAT_XSGPV3Y_ωBN^.UZ[BGHaoeXMdOwOwm\@>\.(v&p{P󙐠Cayt&Ѳ_MXZPإXAF:b%53|3!T[vdS|i9mEmgVlfyq}OO : /v0nYI8!`A6kIJ4|(8mg +Nɔ +uk>MgWNiRAj{CdVIɞt% kbj 6Loɟ+>s#e#1ool2 +l heZ~.Ĵ]sf4+6:ʪrUSXI+'n1ړ1IoNѧR"$9/[IdbSTóGČ}nFcĂ\ɾ@M ˠu*RԷ$W; W7$Wӈ\MO1o>Gn2~49%4{{Сӣ\^g\1|VrK|?qܝuc쳖Jk|6]A!>Dt~ݩӅnC U% zUVnT9*rC|ٷ 1fduǂSU7KӞEJo3`n2Q>0D϶CHCF|` N$b_ٜ֩TDFq 0r8= F}%f"ms]$uݿ8u\xd`n.kWBM xW&"GUNת0ro.m) +K^Z(i+&OqwYHt' 0F^J0ރa ɖrsĆtg1<ߙpBld6OX4:¢rd¡PA;Lg>XqBt7~m`  1fHhqqh\Ξ͓q^&sXiUI<~mw̆(9]^沘|N<"JmPT5'aY'@MoPAg +jXj1E##ARE+AC0ף],_U6ۍ[]=pf 8z@5!E#~rB>7p?_(Œ}kk.G־[w}{oԹ͉Rt+늑+֯o/ͧ'|p&gLtM_$"oϥ [k|A8]k<ɧ?Wπl7nt'.ꢼxվХ@ +烄7CÞh[Mn"nnWnJ;=l)g"קO՟t%v;r}ǻw}eH7e.[p N~0;HkvWN?9[m*^/┼ۋg7\'+uODV&G>F 9?0x:n=V7p ?~`\_<NC{ލ}뵖L83[{`n'sX oqȟ{$nA,ܡAvD©/4O+˩o\|mWl=wWyr6Zv8Kc!r>(~gT٦5-0j ~tk?p )teHRXi{s=Tae\Q#Ԭ-ÆKe=_-Es4B*ZSf3ta[Cqb"&@w0OJtTWY6mnXFtD xA9Zkkh.ғJj66UYvI bTbzjف.ѳk<~yu(k}< Us\3ޫ>W?}YCw#'.`ͦ8bm?x6Hv97a`eVMZ4 owӘSdMxM8$2yܧ 9f2H+ {6ё/祐"/%>C\Ӆ*ՆGs9_f}ΧP/-Yy_,_uu1ɵEkipNKX߱ÚQ}?@M뛙Pk|t&MoYr vjnO +hx}.id EݭW[6Ь̧κfnϯW(ELoyv&]\-o늺=5܎4n ${6 +NGNb Mrs*߁}?,sOXQe,T9f-öxGzLtb_VOmR[ aA'LVS^+TQSD˯-J͗uauӭ]#x}iSo=L#kG})gkvpBFIWOwmWejbGP NÉ2:][w%.(9%k*=*2\<űM\q(iACRr섪lۃ;眾Y,Hdyu-ϹќR} Km 5,U\]E{J#%{Tft"'O%ѱXBr$T*?aeMh}gٸuHhMolz>԰qdQU7W@3LP$*4i:p|}ّk(7Tlr+I{O(i:w!n{Ni^k~v8x|gGr*z;h[9`ׇO/ߝk~thW?*c +rUܵk\F*guv`]V":I13궗L69Ѻp}ӹS:Q mvmwAuw?N ˣK*Bg60hkk njӭf5=t76̈́Vj̻HfdEFֿ&ZL=nԼ(#=HI,HD></[UșUV!}Nz٠+m{ߍL[,:ST)$,= +m_biF<Uf4<.jSd +^WU(97?ga t\B,hE,Fwni#VhT[iq?w]RÕ }>s72eIwIގ,zgbG_JᕣfjSKka=3ﱗEhu +T U˓3FӖů!ͶiYܺ Xep}F yGlĤxoˏ|RwYҋQ6lKj)cXV]<,׹Є6h=KT.lO?d.H#%d/d"ʯ"a=">sTZ~~)t9*;|sw52F}OWr"yy&d]ɾC"]\P꧉v9d[jHPh WڗaR %M[[ |S pp-W WbxdЫcx/OuvAUÍA{r-iCWml\")dy󬭿;;Bhl7:qgUƹis@ U% /P~[zi3_NxGGVs)RL6hS9ֶ4:-L +=tS&54W{:]U-[h:qMfnf&(1(WEPtb.Ǜ=]*n]UFWgF~ Co +t/a)&,g/ހC|`LJ0D,>=~5AOq!|0 ~h +ͭ\VXU$g(j2Q.Tj` +tm0_:ނ%y"eE@0Y]oBS[@T[Y@,BlA,1@!h /~v>5˨*+R9F Q :Z\s O47nPX^A΀@ + [8 +PGd +MGg@䃷W~~:  Bky`Y,v'p T&7ϖf[|nmrY;Q3෯"@RPjeua.fh@ShC!| '_'ou`Zك6 :;M&QjfyR?St>mCTp;̮L$&4bq[lh;p@1wM19> 8gq1b;N1]~Gn[ b]%dϻOI6k'G\7l"|Ȭ\3|497nBj@X \G f68c -jxOl],?%2-Yق㤝Dg"W7# gH5Ag02j< ȂY K_'{o7ɧ]}Ί/Ᏽo}ٟ-Y*͙#.Jnr Laotkh7gd[? S?¿ޟYY;-d"G樻ۼ"7Xӯ+VbD׽Q۬ E8رz֑!u_ '?i>W[ZҤy;yף䆴e6h^~9 7.zP߿WR}uJ ok<+9+r0nןe Hb5TiL]cH\ d4.ylKs733\:^owpE&GyG$I>$ꝯ̦bS5Ch2[u`urȧ')of/Kxgǽ/[YyO_Y\*floؘ^}āx[c|uBۑO͈95C4,zǃXk_.pZ36G3n{u텫Rj')~w:E0jd1>1_>9ƣ;A,AvévBxu?#2pͥmp?Tc;O.UVRe7`#Ru6]i{Eulgvv&֋zx +xmW깥W?3#٧ZrZ{^A{m۱ r꠷ < hN^)#(;oe37 ^ +U {[39>y$O-F>^$K OUr55L@Ewހ)^֚ΏA}Hp,7qmסqU?>y)S/0m.ceBG'jk[HEܢmD?kecgvwvr[lXbYm{fW z?5y?^=|3q v.r7QtG 5L1f֤;'83½~zvCϬL믺|+U>WZ"z,|cٴ׆-tq}3BkgZja?=?I,,۟e A{:/.F͍{Y:iI^fj]5 99-S^-W9͌Ua13[tZic55%1=llcTi!RcK +42 +cyyY{H ̚ U㇯}]myt!I8wC OߵXזks133/HZ/F$҂HX_W.}6~UuU&eLEGwx<\qPfW +|HF7I@{VQ3%2{4T^w)I=jT_ 6wtU=o{ +5XH uL}J!SAQVM[>HHX*ln"Wa)%_b:yx2iq6psμǃ>sm"?,j-{ +SNpZ";آ-G-@Su[5^5yCYׄdF2["!Ĵ&.Oﮰȡ9(Nsz4JiKl"9*S=+4RaU>HK%]DyDl5 o-wBW{J{oLLzp&4}G}:'ZCkVMbd/8#>VU_1Q5m犺+2<\GEGr*hMSE^XwhB'02@6b,Ȕ|? {rQQ"WԨ J$YE1s?{gآ %pZ3Vb#e}Jȶz_ޔtUD\}᠜Pl왜I9M# L畫O6u{T䄖Gwpr&L,,nP +o{JZ4">ScBoSv{ 2W[<u/Բ+PE5a`̙0l]~MehJ +5 l +F.93KuO"EMҤG!|c>`Fo_ӬM:ɩMr]|or&r;̯Xwb6}H+ UnTyӑ**l5zwx~$:4<9ףwQ'yJCe/{'ScVagHEcryDٱDelڙ},*k>arA@Jy3+8jxk@2('*X!s5EZ{E3 )ՋLss^(%{N:6I.vːDu]Z>(CГ,;K(zoڱl>>2X"2UJw 9Hkba,:\WkZ8:Cz'wZ<3 #hvKBhYFolc&3gǼ~XL,<`BTj,XњyK`Nd-#tqOPǸGW.PGoHŝP|.~<sZԦ1vF !pLtr{ D,:2A:el5>]57 ܏ JfǹU(_tMnX٥dY<xߣN8|7X^on7>7+W(ܑ V^b)!{ef@D +U߳>qYJ=X(qi>Q|t!ˡBivAX  yH_ą?N?a +/x 94y=8f1H=JU4>KyOx@\ m/?7Jŝ9_T W6'b ȷ 2j 22eOXg4cA&\œ+dz;2ő@B̷+KEņN ӒNx/MID~7Pe  +@˸% *T<-lu\ +h 'qwk;4wb{ZPܯk fE*s2?04'aID|Kb;$ޮ|ĀQ4`t:0&;2D01a +_ fM $H4]\ObɓXs{ӘS|m.[oDЯGtomR`%MrQ_qOt!Z{heI +%{#'ZVZ3&R: #~ +'˛CD>O}m쇢L$Av}ZbB=$7Ysi#;JwgM4oD]g|>h)Hm7Ǜs8r0~?Їvh0·{P6B}eFx !:̟M|O5II@PKYjR#Yi'ya9Gv7MiW .姹g{>{sΆg6Bh'UVxL }AV2lpNP8.xcDAoh^h7 +Z:4ߠn¬뭎MggZܼi,ߟO O'1)x}yh3eg^XVq֊">> ܡﮋpʡG+؜O-/ۆؓץr67V )͍[7A<6?ppErp}ڃGMU?]t܆Cm!1?=ZM* rApۭs{2mO 0R-6r?;[}|\3Ur#&AE\ۯFJ ::qhG\Ɩ^y$[Cݼ/쾴r4\nХخzث[sT9{^]*EW٥FӥsUK-oW {.\Itu/f y.BOs(hk+Sz~P[ W@Xt>y`?vV!5.*km緆iQF%%μ7mSX rb\r4QMnk9 /l>-=:lpftzlR-&Dy? ޮ`NuC~˨=JuJ4>'cR^ihAY<.E[D>yl9H [FT!egZ"|X5?sh3"0nh2Е!ľ9V| ~ddmLKlϣQn@J~Y__쮳[\,r 2ޖ!z)t۸)cwلnLnc` d)baV_gVrdr"w^"DxIٻLWlw/H-6/\e25 ,.n3{ͬ,,s\#v6(X2苊}S/5^O?V6Ƥ_Z*Ϻl Wd L{,tet7?2:1QtӅ2`tjWD?L]椙?>Kh^. ?:4{፩ IȒ)>2±Q.SܯF403i) V8v +8voqωB"C ڧ%_[|q˥ +AZY1snqY/~N +{ eYi͗"c8ө͈yaT7lK`wH@D'^DTP@B_pA d{x&A!|>@uN2s:r[X{z`R/{Ve^+Ԕӻ*rimQαOα U#熋]F>M,C'ڜ`T~uJTY~r X6>TF8鼟 +a)x/ɁƯhphm;W:J;&l:d7>(0qrtD̡5whعevp5ɰGe\:|ϗNT(f5Խ*E+FegoNLǣX#2=ER-E&zϼ톷 3Њ=#۬gdɸQ*ʹ~ Aw"6mj *,^d.X1^zzv$"HQCiIZFH_>n~9VC}M?2U~W[ۼg5Lfuҽ t]E t xPA%uIdWj fB,;(Qʜ(|ӋM3 rk#WoTģyر]ʉ.Aj+3iKG\xurtiZ-5\^N_mTtMOitpZxz.l kĄA@1h!)LpVϸb G͗;:nwȌҾ5esYPfn0F.si/~X'h ;\ko&jzA4*ENB=$tV +hhuI/`҈&4z.]0m3xֺ9SgzruJVg[MU$'vXv htQNcQB[t댽~a+3eC9o<#m"S"XSu_ّL Ui|#`Wu+f졳j\_Bf {mt4*x* # +ڟu~Re+5E. q(&h6V&S{ PQP~j6)bhuQB1n`R08HlitƗuRHm&*n.VmOyeЛA9S7Hj#_g,} 0Ka۱qzNa#NҀ!+`Rz@(~y#Z&oyԬﮙx?:$T2F%fl/4>ۋyA7{c`غl/vvO`ӿ.Sy9p `_ۜAmyo`z:B {dX5Bb{#ֵ:4uqc鯬 .Ix/ z9V,9GcQ|H4M_Oqojt +g᏾v6%k>I#Bs=KZ ^={?7?C D'@} ^,oĔ?襺@@u,a -gD$6,ꍠggKRH2GNW" Hb&oK&/xN$V81&7I:}a_[^s@h:m?Qk+Mc#S Z7_?CqO(gIxC}>q&Y5w\6e v!Z1]^~ۜ7:^W\]ءZ=, M$a _e"T|7NOm~4&*nwgS +DRmTw>tIU?ڻZqWf[x7 ? _q⒝AP>O(1T3w[aW418)Taj7v;/M5:mupKy=#~)#a>mşAw 5|V~;07hUs0uŭ$gg&βWM'*(O(rL<*,=U q5bPX>$Xe#F?Vكug9 $ƨS/noOʏ6{uC^xcnVO۠6)]+/<7gsBD@ӑ>WERtAójCΪ\wUs߮ҵSpnpZVjɷ1ѼWe|3i6C5xMЬ{$e) RsnVH ﰁQ$B#8$&>Mn|1h:7\*Ȉ0jm8֡dXs4YrzP%vģmL* e}JA.Dz(K.l\٣^WE쵽+خʻcx/x&2^|OVʏL?rUxd6ƒ=%(+*v~R΋g:QqI 0Og,J/ُb} - ZڤU°򤼭KRp^KVo*RRU{C3P(|`'FZ)p=Qĥt|tuFq|>L}XD|EڡRևGmu7;-]f)bh,=+Bf9Ýﺐ滘zi\܎[ꎳ#;le/(ۦu SVW`̊f|ߛ7-uAHs/-dox kyė{Y: պR@~O<[蔵"[\Z+}7m7C%ζe6݌ Kg_\ô1Nv +}Ϳt-jK}Ж9"ʤ7l@\J<&km@eT6j<+'G)*B6V oh"q|lx>yҰaG8h\φ鎛Uٜ_CTf&ُ g^h<sՉ V +QnoZeC9f 4#kB?+=5UFrx]JcId`밙L@jH}=:2p@NJXTD+ƏD#eRjuPڠBˈ۱B˲ ;E МmW'uu]E__,3E:˷7D7 Z$CGJV̩0]('G~>F|dhp^3Sg +Rfi$O37FjS*j9 S9nlr +lT^FқLZKV=I)s %"mZ /Q]w޻B,)fSdbkՏ+g-=+_[ګm}UFQQY:)?*kΕ WR=. AWtHP>stream +0C:PrAgI~@{@TeLXB q;We*8>62ˡǓ{O?' M5_e&]r"wu|uF2Ife2\to#5f&!S lr4HT'#-4Ӯ4ӠexJ6 .GNްk ꦇ:/IvjF=).l8E!OCq%e`LXjEkѨK27#*]w.OeLbc|]\qz+P.pzf~.qkf# 0}t!Y9F%<6Og:{\N2#W9 +r&+d.R맧, WSQra{)a|nGOqBg" ox?OD:%-gOש@G̣'xt5iIz#3}ZtޱeҲWGgvws w>Ж :NC4q9l).~trz%>ss nMtT+\Ic ccIwU,`BZYQ_#7k"a4B FmGCV7efjW)4>`9h)BcINM Ar]\n|Yő:|ua[۷Rٓ|q螕qŽWެO .O6;P>/UHnBY襦`0KaxCb +%{6婁ObH✈=lE//ݥr_!ߛ7W"jk%&V{[HF9L HJd\vbc9X$ 5=Љ^u@% 5)MW0 .Hai?\t.I*KF?U5&>|&tZ$m.-]*}a +,CT*J42Q,,*+1rPJJ=94(L <Q!o#g{H &5|"/B\eCDt@RBhhs ]ƀ@O^7@O(;=7Ʋ9?fj@r<Jdsy\* ry.čʄړ2 ]b54ڳ|ئŔlŋd]zEHotũ +L+f9tYN4K*Ef2fsk'cj*|ػ4w<33z`f{}:@Ǣ ëZ9KX>$u_nx v+΀/])H۱L׀G0:Hn5ndOZS`*뵦e)*}b"JZi2.j R*̃pk<[7˿68]"0@d DΧk5 +p뜓 sY/˪ZQײwES8W۹9;ΚeiRO#M' +/L.O!}1G<K0lĝ4QC8"}ݻg$MZŞM \ +-ۮo@M,a:d~֛AOiRx+c[mle?'ٚ~Wo+t(`9#"$SbZv a7 [7oQ7Ck\Wl3?vzK\烒Zk| T@";^S~p.ѳbzC%u'F6zp=Y{jjJHa)M곴hunuh6gfǔrU/Zd!Uͭ$mtiβ@Kolޡz?uּ⫩ޭHϓ 6Tj.V6bٮzdPYUkm,WE?C_xsDK`aO5ӼȫXXy&Ug SDՕ1R"Af(&YgR%Vyŧ;ZtWX)T?sE-u֞sTEg:aQ h]ˣ$ +5ڴPա>l"ۈ[5zuT];Zm$Zbj\Y-?A!e%^°m <'wϣ;kM%N5Y ! Ja.ڍdϓ$s61+ +g,9GJI`t<}uʼ-GGܭ,bT*`)@k>ג;\t%%$m;8>}0c<ү e3H nq҈ o>׈nR0f˰E03KO:iZw]c<1=UX;mz5Zj9? !ޔjG+x.+mZBJ{eR@w8ۭd;9°b=n|Ci1:~ZFEIvni-Y#!ʄ5UV~=iEZVѲHqR/lcߤһJe[#,pJްD\jفHr-Fę yeuӨ6ՊuvGυ" m4ΙV𑯗&ʐ(aJW(/BƇ&Ң Rp%/nd6 wL=siSɄ PVELJQTT+9+\-#|3̞_!Ed/CiH~\ZCU@wB'Ώ-+9`QۉuHۋgJߺks83Z)o}!?mnښ.(YEL`6wyZ=%8܉CѮsar +/ o5n]\zX;X3bb283erb9Js[,j?E0=mZgu.0VQ+krjR_HL`MQR&E d,Y"\UX]/1+ejؚ5Ř%0MїŶE+JnHuϫ?/ե ½Was͙ݷR_׋ìQ6syª ԯ18*.IaڙBܧm$qTʝ\cZͻߧ"ScJF_2B+єz)آqЈ/9ЌL[r21352dbOJ?K.2JlAiAK4/ue<(Q\}udfgZ»HI_JjZEW8^(#ͤA̐H;XM`CǸ$˸!}O!"R<yր^=_kj]G epdM33[ѝaVUbSOz\z}2j692;$digxs"EsSWWzgmO8Cr[-T6W>9!o\Lwԣ(30%T{> ^ifz X(˼!jpZ 1Zךh@L9igTY;@4Dk e_H/riܗӧʹXfҳ*kΠgԣ0-pH_,HkݵEn˶ =vKgEtbA59tP ]UArsDfVy(dWNAN7o3Fz)$YHlTS iXd&,@< g#`1 ^Ok@N6ȩdO2@6 K^҅dvG=^tPO.ez.ѽ%YP>3hI`)< Ϣ(•%:CPTToCjj.PoeG+T]'hqzC5,D}Uxʕj1I-2CۃYB'nN!P?f,Y:J lꀁ`dlw>fc3@X 0v0` ~CAќ(uɘVd3ԦYbvS!&ڗ3@e&`qXll>>8.|ᱜZ.,EzMi8\&e^H&"aݚV &lu~#?% Ns|˙ F~?WA@9<,f9  R}&, (Tc;_w{z^ĎQW?99?:'vKXh p?Z, cX9d\>}?=s@Z@P z5f\y_W,*G,e|.@؀sf%>'7aΉ7PKɈr8r?wX;}q7P:J_ׁ@ ( J3elD1UTah`3z#2LPOXoҦ='b"i¿| MLLOo2p[:N?ίi t2zz0}&Fߌb}e6q& KcTb9u ɖzfXr]iLcwTZ >|qGkF4kz^*Hwu?[C'I9LKv}.agw8ˠ4fۜ,+TOsVo.< "FC#Ps7l)歄,\Oty40a51;Zj"QB/l9Ͳuxµ.d5c+s-Zԧ3[44Y- Oqm?b7P~(9Cէ +%1G~C MH| [>\5RRO׋0f=fPLz 6ںjz.dz(l(nؿ5^/"Ep{Qe?vׅ;pCbe:>qq}%+uɞAe7,WGǚUZSw1޷Ң0jV:qzbuhseHNk[{nW#8Ԧ@8mV _歋MQ]&Gy. +E9 0Y9A\W}kN@z5jC7uIcO<ο9xFY:9kW/d>ݵc ]I&:jMkKǶR~}7A5>cgk<\\&XU׵١ʜ.JM0s$Oze\TМ/ ^?3f.d-p -+?ꙋ8ͭ<;x\Tו*Y5lEnhK.VtJ%^|v\@'qY]Y!K.]^/ILI_}&׷~O[Hnjޱ~zҵe5=Kn4\݊,*f=Ty{WyO]?[̠=y/|g.wݨLݯ2eCMB7j;fFϋ?p/nVuN~ݤܮ/3l*1ۥ[VWkA ̧ +hcm4ZYٻX ,W,bG?jW{Ԩ}M䧙R:ysְ^vPf|^FBW2^ LlQClq-Mݜ5ߗ^)#쑲\j=<)/[Xw^s{} M[7*( ](+܁oe2Yӏ,~Kɶf+rHĮl'vps8>akw)V:^s_&LYmoU"i(}" x( I;b^ļ]{qkjBm7u֜W<+*AT;)LkC[HW;VU7w?F(̺<_2$Y47( MԖR[w̷͡}##wxQ v *R՛JqYsHkͳєVvO{M_y:˾2BVHN2s^ʽH?a{* |UOj +,m +zˆVTwtsfna?w W[خmc1jFh^H]܅+mآԮbt$+6Mc-5 Њ4M)/ EXEwVU(wuFWkL{.+Z~7lk=r9i HGe[ĢqsdlRgn)imcԞ"YDI1IR/'"a%_@$#WX[rqwl4O/{,>ig>Ma|/!E:('t4̘3IR {qx7&shn9_28%1lGH>>֪w|T=\l{.X3Loa_⁢n@שO&Z 04#sFY'ןRخKw,1\¡G$*@HKâ;}i7G^ljI X \ 2v\^үjJ +A.APϕ +Q .9ɪ%9!7m͞$GĞtZ<֕#-9eAKxd¦}N#) Nƙɠ37~{6ptRԢr\@5IoWbg0.4= ,X\{b,[ι)^ nӪłZ\f;kך^}4M/^u9A*tH 3kR/VL?w G!h+T.% + )rᶳd +KĪrletT =:c盖5 pJ1 UBU4G[`̻n_mHaM:Jz 2WFsgNW|qg ٫4t) ؘ$)mHsCT| +^]d/QƕcRqB[@}+"ia q|jK hV6-?tv! פ"_eowޝ(^csv07+T,s5E۞]F5BceoH;8T#. 436$` OKPt͵r빍AvQJ3f|jyLx0 >o2̍#³.h:@ N }8"Ƭ/gnCEՠ![u|~3S>f,-w;w'I[K Qg12/˫ %,BnƔFG#B  aqC)u +\@~2X@9 ]Q&\Fd7~kx$'-Q6 sd:ex.h\< +zpdXb0<*!d7PIIaXS@= )vgd!@]3Y@m@ +X"||rC&c_XU~.sTdD\,ړ urQ )@ݖU@k/`m5r)Aa,+`K?VL\Fx E۔̧[ wZl;z+s1X8duN,cO[e=~Pq&M[x](< ŸZKQsB͚jS_&n~6 $=D@m2x&?/_SF J5@m) :wF3Ƴ&^+Ij(lAݟ6Q߄3䂖;BYɪw$; QWH!4gD{Yv?%ߙҒj%hh9WqM N*k;N2~1/+w@i (A?%.2G@i32PVR(O5 y߱p +S&9B_M؋/}Wd֖Iv1߿$:}oؘ%.@Еz=A6ϡ6@[%iݽ9R K-i~9oDR$Ῥ7~{ p&ޒ8q&owLi5Om$1Y{ X&X^_5WwYcƤVK[ʷBK Η<<]~*'0?IkH\NZ Cz??ٷQ%X9ƊG~Ɇ44l~9܆c_S]p}v] +qE\霵Lк", M|Ƈw +W45ƻwY7_>.G>>U̩CfWֶkmq`.ZvfNhbăY +ͱj\ *L'=]K|xoS0A`=_I Wm7iGxk0$u^JM$nc5Yxҽ,̻Kgy]ڵ1_(ݚ\RTaV ҰNkg(W\N87{ծ9ٿH/=>$Y 䭑KM+~ z^1K¬7c +3:amtp_7%}͉d'ԆrT7 257\uJ}ťֽwJ ߶^y]9W;&7zR,K}]OvQz5pU As)˶j8EZ+Kv, Ӟ ~h%HV}](v&0JD[rCiB )iŹ~"輰79B vIFa?k׊8. oS˶jΨj`¥ν*JPvjrb +&%b2xb4Y+ %e"B ʸȍJ=̍ hTXO⢿oR8XdRTCMU9FkJsWOyT,^'uS4E#(F}2ͩINːX,$&>fu{+l3O֬uSU4[)Ҭ}W||axwjރTًD6x=^|3Pu jXL,ș[.]cZ}{ȯH.󝽚ϲC[?Y5jOZVUD /ӷ[Vڝ?pRmٶ-D2!FmkP^`PƷ qo  +VV"^;,GuuU26BҮV_R劒#7x{Q8;;s`z_cKp&$rkE!sDxK{7p +k4}6} BEFg\;xfMT^R.Bt u vYd.OSJ\";;{.L_68m_@J7A79^i Ωi ߛ&VX`}QnoFXRj ;l:n$G&{-[ּQNcVyA +Sc>1u +ZoH,Hl̎P(01^6)S1 z҇^gOl7< 'T+51x^@pzDnK)TұˀiX% ăU $DBBhNg`hi]#SdoSFgN, ]}km}pCn  :t\u h¡7 ȴcY֪@z6ȼ 6 +do<[V˼UW>^A +KՈ/L9=(ǟ#Om t\Gy28U.ѸW^ + jħjLI)PYTi.;Jkǟ1+d@띋u%%nl^vo +3$%OmG) ޑw\ @khϵ=9L-jC[˓>"[]:3O%jڅ@}<{Pg6k+Z 92XL!.r:<׬J +b$Q([uVo]rs& [_J-fu4XMyMvp\uf[Trh̓Ry,*{ oYv_ w3X~=~~>4;;6>3<JNL wL^Bz& ublLg2&5aO+%5%Vm'bE'_ϟ8aI_o2Xk֚.@\D\5$x@|d +;LDr_ʡf(@VNe[$\9~ TpտN!|wM"5^PlJq4z@)]v@8Pj(hic0uKlz=ZGXϣ4PFL$u$9sܿ%I|֛X{G~׊zY8 +U[}5u9T*5)xbI /~c3/H=s~_bMo-NB?0e,㝤 KҢ+FK_kE|SYMj n46\?2]ZnD;L'lgC!PLX'^փb\Mӎa%q j25ך_a6$T?9vQ.JKnsB-Вm,;yzeb.lfg<,}7s8=w̆&㥮zYǪFϗ}Ή4 >w1Isbydlj.O2MҦJr5qD?)=]85{pՎh~nW^!KDy^@ZrАr-eJhH/euh!It"@Wprn( r"zelRtf cЅ'Ro`qSu.R.I1Yv.9㈔U\8ɂYM^5[@Aa=s![n\lQq~ÿ?X{ #^R=RmXr_vS1ŋirR vRO✮]D<܄%8_~p|Bdn0uq±We7;F>v-ڸ xj 2\ؕUH$Z->t#O™Db讅R\PʛwW@cډ…d;ʞ>6KU YF\(x((7#\($|b5Da^ QΪ^'lt)sƕz0Y(K-_SK0Ϲ9sfʼn͖=k7gV%iʗ-棩R#shS%YIsgB<|*9۶'P_ _-%#M yMpyݾV Y*W '!gUm2)"†+b:WjFTzd>$b'HaS-|mˁU@GcJ +46|,'}ƽT}L hjŤYqG1̕ѽ'ԚUC3:21m5_5CxN,NoRbJ;BXsKr"XVn1C Zx\ CU2;'O}H$<B8%:LZ18<шV;^m-݄H۹λOD2b;J 7Uz1FMQH + +k% $X io-.fV7;WgwO,U:?N<檱%>s9/,/(e7YOTآMᶂƗ2ސ5}ӟ>LQ@ &SebW Og ea) N]"{ئHZ*T˵GCa`!!r5-6rq +}c*4 _ P!]q'`Xg@^%"wkAHrҼYD۵#Mxl Gcyj?0ƅO Y@_ +Yr$bcY7#@ab{O-uQQ0yz4v%{H*`ͶA CGђáwu̜ _[5`D*`S`q XM5\`60 Z#9/ٸ fبJdi9csHpQsHxYOUۙ7Mpl IĄTbe`g)mSlkQĥND^CpSߜR4|MPM&/V3q?1YQ`@N +I;OYM'a&9i*aIM R%ҿ%OL?W$Ud @Iצ@awC-2ǩa&V#ࢃѭ?6CSY{& M*g9 i&9 _=Y7s6[* +642:q +(qXB~~褩ͯ Z{uqV\1@=iNH`J40}F2?2d?q&ٷɒlv%Y{(pi=j -򶖻܍ +?i<ʝrJçέ{zYX|#~d+-= d|"Ț ӏz\E|{r|I{2f?۵A 75pn=p^d09ϿSڌQjyU͹M +LJ/5Y w/RLj"L&ac>|φ{IO-9+tSf6'5|}k.Q~xGaznVNY`:EIț_@P MBs<N˟6eXd"}huD!=6̞L{a9mgՏ~߼}]?lgBw)&%\t_47Z+ل&/[`Pr$aSߴce劶}gZm=ndS>sNź?ZXzkzV= IUjJvG쉲F?6hEp7+L_@j0ǺD>oP`0H\ݬQQeWǦX7S{{UV_.([+{Lz_l[-›YUvWW{JAQF>t7r o;XZ҆IBB$db8vv^¬,Ƣ +.7p\!u <)un-rT=[ַe23܁Jo)sʹm:;Û5nU$UO1 D#.pP$!)0:? upv3,8v,='}iW46h[jܳ ӑ/2s6nmQHHMy鲛batf^+9f6@ev%qqSvҐ56_btˁ06 *aS WSԺ/)4)sݨRkYfXpQ5(="+zPR )Bvjݘ&3K.t' 64։ WE)/8黠qP='Ts(r4/VvéI}hJ/AA#ʶԪK˪';\Q V9M!&FySJGtWtr^Қs}`Ɯko8Ớ-Q T ho;2;˶bq +B%rchCp^6!и̯QkƳViN`}NKjZkEb8fvZC%OfMW7rn|Ǟ&lssfc/68 z¾x nqV,@vާYBYs;L.M t$ҽ|CgP¸J\b65 ǝXKya8+V}{ʔg܌=WssN 摕/]!f.%q溅Y3>r턉Xws׼o`CNP㟐m8{@ \KLlA2#*ܒb9[IY|ɪoJO˿>wNB'?P.:D-lW?U] +C蕮 !ԞB A8?^ҔXz,@Zd:6T֧/TN*@&@ + -˦N9oPTؒR<$Ȝmڎ0A{_vf¡]\jAq'~fh`WXj b=@qrJXv,:@(Ħ +]KSXXVy$KUROtª`+0`r#ihaVM6'J'9ɇW5a*=+h+!\X0ؼ tJa\WyVW'{3`c>ι<OSm0~)%D (nDUo^_WpNCi=7 Fq97b89%q*˂MD#DQyQQQEEQP{_tN$$?U{׸lZ4^ăDDKPLY?UX=_J&f0inGa|CbP-Gbi 4Y mi _O% `H`c8Vbc0)X;}h"3Ϝ}'4'y¬,zrbX:yEmz7-Gc%?8M#gdСg7v/ I/^))T,vI4ɾ|z!5㌖~+)|v0!K&pDzh\'N_t/G_},aQ}n  +\~+& rprsU)ꂔk n19=0InK>R@mw? +b?r`_o~(⍿gww8Bkc\s?TuRIL(=@oěQbyi*XnP~壟$P9MmT¡%CӁ0g߰! +K@[]{ +\ v8Oh/!47Ț̐{?l7SEoAa|* +v*]"1O c  ɵ@&]؁L鍹$gh7Su֯Srs}r57L + 8Q´I\ e޲;]RAaGBF !ݦ O.Z +JQ>6Ν;TZaxv +_ <|{{%Ksho=ޏ_y6r|n \2)r20NYԞAŝb>S֊z0Isl^f v;wu,?U~XJ&0,6x|?|@>xȜ&{ZýIdQ +ƴ;.śya/nsڳX?#Ä`w]8XUv`͊h8lzw9j/r]Z4rRգ:=(ۊ;b3R)PCbt&L2ja>FVg(ܴ𪞌f*uwmWfz;vo#QγxBh]g\9ڔFh0yl_ P{%ִsapg0XL!RW&,UdB{ϵjLh% ij yJYr"Zrƺ.9#od@Th>D  {1ldB;C/|aX(c975EwkC<f]HɡNCtHXN0XIHd"MΞƺ\ +3jm+E=UlL*OtY +Aj&a†ؕߢdL\ UDWܾlrGs6Kڄr|(X˔d w5֛+5dDsډ=TU}$L2 +a0k d@\etK۰SnZS!B Mb?;*;O#n&wfFi̬oܪUC4S֤M=RgeԻJ+uke)y9a~/9ZXސeh1'kr>wYZM"WTC ]`)@G*D޴|gsΎ-&Q-,0jee~ϖ8jKo-DQ͖A>dz`!=<*iUAj\)/X|2 H[sU'JUgn553U} fQJ䠽t\C8+k޿؏ 8\ɠ<$;^<ҩR,'.M.,Y-sGQLUuLݤtLզjDA VcL,3aPta8`"gc`Z#OfaA\)O-Z8>1)4%*eey˭iEOV {?fWL]OQyǰ((R m$ڭ@%/3xCx@c!iFtӥ9gw/S'ڈ$ud*1>x'I.&_(6L*KTL0WLVPLI'AÚ`-cю$K){|O,A{rS$Σl܁Ds{F0.v̺n1˙Q(֙v*iNEБ죹cH ޖ6 +>Ty'k;tߖ %:Aq"j9*B-z»QasP#Q2a&c'dž21^twX467gD3'<l[Td@FɼJЩEv.$Y{EXBB/DbqS0WGQ^kLQ]@XnbeƆWc+z-ѱ!rL_4hEGb̄&xvhKo`j%ĩ2 Vj rt; qsZ%Kb364ٶ&)c8rv# x *ԫBJبbi磒xH#ETxWP | A") +@@:  uʥ uH"a*hJdVI/JaZVӁJn 8#У Q +/ bgKQHqASa<ueR087 7R l 6w $ī(`i  Q ҪXAi"cPsDDDIВ F.1D bXb +f8 z amN PVdlؚVkJ^vlb*< j w#zXoщ"þ1@PneW\Bp s)H`Gy+ bb+\ HoOgb,B䗍0:W(X'v"8&"R3y< K7MK[&@ aqTCzBЍ /,@;cGte0ΜH%˱-zl,6fhWȋlRĎHUhMtzqO[uQ|f@.  oW%7kE ik*޿HQ2x9YS`7DzI-/H`y`OW08 ZvpX]KNG\R[RKTv&J!s#M 8zmkl'o:t#&=c&f(}DՆ񽡤YyU ժHdQ$aP^@455K[H(%d^5A( I\)gu&C +uP -th/ {L% CW̫Զ\@٣/G3匤ұR;kT.92vt|.rnwM`4HUuo{WLJNY2A ?bQJ/GeWLngغ|snvK>;vf\# + T$9frwI|jaӵhֳ@OK[Wܪ\] ivöEube94?josNm~%f5rAwK߆ 6\ ~vpW `b~`1?~ppR%As?zͬ(銧xю$v\:&ځgĝX<)Zj5,У #ZrJSO|2kvH(*eI +]РL}]V;Zi%6wT[uGfwbY* XeҞD &V^2_ʠVİXziҺJvOr - )WmrtɘuvQ/xw`uk`MW=ЩGd=+cɵo<,A[Ao _34,w!1|Ay$5ELJ$,o*JZ][`_`n +Cl6FRu|J2>:Q(/dvbjDUNe>$40w XSu[kh-3~/,(-?v_lEnXt6|~-4Qk`MuK4db)qH"49?AKgϻ548taS$o${57z&+z<Gp[nCWl ܫ-!_C#smSY3eWx^m"xruu˪q}[{F&25<2rG@ny_9f\JX_!_;/LiDy>PGGeY?|Exc{ɤqyt|iPq^i IǼ7bv<ӧWϜ$榼j +v;Suz;/isZbd1ZS IQTu!&6N[X[*vtu$G+6^]vnXcj2I*q}Ew5|6;ԽF?tNs sҲr[a+Jb&KLKXsUUAnG|;HkN >35ee.~M\~neNՎ%u]q|9SSYo5toy^vm!]eer]ƓհIpsK&۶"J!7,?ko)'ް!X/&_o*ߐG>zvi̮݊5`MBޜ%4u>;|*I8g(kOکDW~ +}jځkz6Jn/U.*nk y3f +2`FW&M$޷zh/(vv:"|0Jw8;Wӑqȋ:Ԛ_L,%Z0)Ą5r|މ.*N W |(~`޵/7!踶J|ȋ,|vC䀽_9gg_6<ә1KMKA!kCfYc.faSx9BL9--u8rRv:0r k ?#8期Y Vvkҩ Wc)wuUtx;R +d_֖c99N$z@KC*~xd;vMWZ-<Q2la!IZV|'Yd\vZρe6V[8 _cґQiwȲ~+ }.Km˔ۻ.Ln=۴9`- 1<0/OO&OçY̧sD(҆}wy܆I% K^fTQy ȝy4U;_*oR_?'+& OM)kۚ YXm~Ia1!ϰ Q&& vgm~c +nY<&GML +^mNR0}J%}_}Clt:bӶv\݌aīM^nyVz5{kj_We8Oّ꟨Ф4Xt|l:%n=tR"Wp6XǖyD22`9U1A'+\RãODzCyܧ"eoIf)^s9w7<{GVxDYj}1ߩטJoB ~OOTS 0;O+N}Ct=?녞ꭷ =wӬ^${y@ uj!Tl fϔ-{huX^NUE#;r"卵n. @敖Q#~YY> }p=w=֞9}yJ/R$~=uj0Ym1-sJQCJDNWC BWg*HUGG nt+vu?Q><dN<97#1)\Ӳ?|r7&ᔔfž=/C帜lj_e~r}5a%yJoaPr||KI?,;٘@ӱGħ#%8%uĬ&Xg'gOvv9Y4LXG{]Q6Adx#{L~>Bt-=.+zf;M޺|ߞMKkyzz[Jn̶z8Nr,B˙$rF"vs Yz&B#Tц}'xn}ؼ-kX3ʷtEWFq !hnΓfrBzN!`KhU XG!w/,cϬ<VKUn(MgtX(gjJX<ȠwA/۟X0_5} 7 ΞJi}oݪS=5V+&"?=fS)zFE9IŪw}r{]2vK-} g~ +|= +njr!7ζhŕ$2Mfw?o]HgngK_+sms{+)<ܟ6Χwպۆ-ms:jz 2s-O,.ӧ`W/8=V컦,R$mۨ6f_>GI96r=YnO%xFsjZ!˦?IŢ3|Ҳ"F6vZ Ħ5&M-bB6INֶ|63 h$ӴVwH3*5p6p"E?3~ B{w{?CNz?5hRXx?!ϰ QlbQ?g;)a\iӧhѓlx +~+[a}=Zr4m2_yTj&w/3t'o 7O +So^:]<<_rTFڌS0{yR/&Fou5v.ɓb+0HU*B'OO^+vݺ cI[s?]#lk$ye?ei 6ӻ-/_J+wҧb[L C{+jUfɕ[ɕ! =_ﶎkŅl_q^G -A۔ IMy U6])҇Ts +wg.VvmwW^N07~ Ss߳Yu#|ػXZ`=ônyM J&^+j%_>]||FObn.aO&O4yKJΒN6| )Fb :7)а:&Q6ŭ[J|[〵TJ:+#*sn^$D)ҎXc}ZC?n[}j]$ YTwأػV/6\SdznK;^%:h3iڧnFbmmʇSmA/:6I&t|k(:p[R+5;&.&?]9O!Ws}WXHjwQ: =e9(Q_9 >5D`[OiTԽj/}=I}ZBqr6yͨ85,TG91ToʖouM_O<ΞY%3]1s(msu;\A];QiKK+4R.Ǿ\>6}6,_g~>Q}zp$>;>3;[s7^FƈѢ|9ΣYlw茇r>,pIܤ{e1  Tq'bAΧ>ֽ=^lo:+ajKVSnnNhKdogiʦ:nWדr4گOEHvՇ} >䃠>WjӮ4??yafq+߻jߜsfIpgX,Vw9 k+ibb_(P_9M[ᙃ 4o>R&qKy1IFSf]W=ZH,xPs,oⱮ#vV>;GHBŚlIIEV\fҠ4.Zo>.Tlwӱe0ʩ&5A(>moY+Z&^1R,5;xV}gnuñȆt]cőt"mR&5D3i;՝!Xyrw7muC)+rdj#'C,>Z15L͖:i'%^|~Tk= ?7 ?-I*^kY EVf6fW=7]@Mb ˦DT1u)SQY\~|NF-x꺇?#SS4؆AqM,\ ^;-$rU֔(jj#506l9M^T',早hJxF=& ~Cg cRXj sB1ŌpzOTkR _rޢy K-vylaeFhӫ~X~)B=k1B7(DJ}A6w +#>Wj[q6Ng9+5s~xVF\[Zn#b0}m{UMG!TqջY $=tST)}@nri|Snk?ӕk~\Jbjo䎡̻$Km\w5eUSG3\}h'|*jpH0;$I<ѽtŻrڏak}6;RYo/UצDxQO2*+>QoASBz^딨m|d&e=v9mO`ku +宕W.zn[Y):;~1 ^|&1{,.tw>7mnb5s;QMNHW [ cy `gn1U8?OԖ'MzVlè2^Y{7=Qh8e}Z;l3v9o(}7geΥ~FyuT3I `1_;5D5@+ T +4u2 +zY˸>A݃fOu1ul،me볊>LbN~36bܧC Q}Xvzot@k)Qf\$sެυ0 I'Dz*Wql5UouOqMj 0=M MzqOg/0f)Ofw箔Nj'ўoEU%XR\~DtݦҾ\9;pBg_@Rw_RUd6Otcwӷc\ݽlB=l7"D25ONi6wʰgsO֦ U:#Tԁ})2y͊6:Uiew57qe8.j{]OvEϺ(s L=OA’o`+#U8Gfpx" k>*ī`Ǎ<}eDtRKʹdߢG8" Iu({o3=̵~(}]||G!)n_L}x.XIHx v'}B]a<^Ml^beWלiu=>갬~W}>}u/65))hFrC?O +:O+ǣ2ݽ]Lw`ShZG}-U\Q" F}(ĺa+љo]x+2NEbT}%o|' G +:*>?]Qyj~cRvM--z85*lVjIΜ7A\gg L +~#hk#>Ѧ[1ҍ>#}hg*~0q׳s0/vuK\Y%s\JpXG2ҝ^dڪv{4YmpT1U5yOz֧/vo5^{9*ʜnEZI-N7ϻȄS`o}OyRn.v~3foX6z"^?i@Pa%S=BoZaaMWNq} FJq_[195TDrv冸zMQy{٠6mgfᵰeħԻ!HNx>|z "& ˺DvěY)v':@`–]oV2K;N0cwA+ܛ/BO[Mk(7ZSmY9RcCL| tՅYd3ujc$MwM̆vWԥQj3)(Ds߾V'qƇUoe:cMƉFmD dJ-;ԚPi5$iw> +[y.'%ERkʦVfʟ\37=Xtq$rJLdiciߴ'!f~ݙzIܥAꑩ1[}µau<ص^yvEe/!A{ yuGWsqHR[,Vj][$V|`u)*dVcv*kŵ7Z_ HK[1nKOqOu35p+S&9S?vazu[<.iQ9ƖVNT ѸҩiI8:<|g(B" e?T>y?4?g Ia1kꓠT:I>Q ՌQ($ :WvP}xIpƯəҸi(t-[@Ipeo7b ^3 Mjw>p]\r>iŰ ,/Ob<]w[WO á2>+Y:eJ]cQj3wv2Ws׻a78g|l @rnXͧs!ֽ6~׮wvw66?(l8 =vLw}9T^lf60J#{˟ФTT0>a!lKǔ;GnVckn|9nFcEn,湹zG4MQFgS*Llu].Y&෾DX&8A]{˧fJ٧s%{fu+eTr~YX*F<6N,|0wPȝǹjȭl,9A%E5yY2 ďn'Ƨbۧd[SW6r/m+PmsVڤN'%9|sq\ȓ{ö'=u&>ܝܪ>mEz޼6&{Dbum'vR/6@Q[z݂WYIoϦJEL#߇`g8Y Q&}TR̭3.ΜH]JS=&Ȝ F2Ũ/Yʅ 4=c3/?x +ݴO䨏Qy hR ac<_n>8w8G6ʊ4R;P!h>c(StS$foƽz2N)q2ϟTФtل<7MħWFջ[a* AmNQ\0Xzu٩޴(^9\b {aHҮҐfh4SD@p3e`_qЪR=}kzuKZ-dӮתS]\[SV.giti{Ñfܳ7ș8py>݇2}fUKcٕMؗTךm+iОִR)Eim ű$ToHR0 P I%Χާk{gjQ7w~]I>sA"K4V٨\^5|4o %ro!Y#cX̗H5J&VOç?˶字r,IXMMebIu)OUY]y𔽊jA(]:z=}Mzn5g]vn)26(%Jo}c''r ^1 Q!Xp1,8.1]ކ]g"[D,$W#qIk=2=GIR@ 1ζF֦ah̢,6kꚢ5z^}4ў=<{}a(pkko>L<?B키|gά7jy>SGv q+R7TĜᢋ=xNkxOl^@XyP/"c;蛮S:3u2] `n5,2ۧl&/B_~ bTgGXOP5n-"+߾{|[P=xaZnHGoqW3IISzu~'#w6Y GR)% dGgVLÍ~=l}ʙC~zl?.D~OeLߜwǑ>?"ֲ[fE!\TB +.·EXb5>^|x"#V<ǚ-mj vauMF+rgz7cکxl9Vi2:s`9qtbxehlSrHbej+74j??"leJ|Toí7Zۨ|x]s!=oZF3T {T.ԢuZiOF$'97r;xG&?DMwuElwRcLΰBh6#;^ F tMjw/@Wj~M$2l7{[:zA}u[6]~j %JQ*>ۮiY `q`¼ +\N~@r?!聆ZxK4,ǞO-TvaN7ix翕m؟ߢ3.Sh +_Z_3nBNk=GYJuAI…l~HۮSNQׁT'?sS5m[#>q|unV~@x7XsH%>2ihf|LţvcwJнe7y&2^ρq)iw|f?D5j-Xm  gJm,*˰Z +'QKVу;zC\ݔ#swK=}~0stgT>jrb]NϘ˟ +{QD6(4(;(Z &fl.'_T +61mUQop3Mr 3+t[TqQ;-n[YB8W?8rf2u ơ5|DVvkAKPd6u̗g4*WvZA5*q0:6nHsmĖ?J@a2*sX'((rQZ:^Ir|fZq8˥`LX?颦(!mcl"sъR:[)ٶ;:YQ-@XCFYչ>1=*wuH>V7{6~$3D-"vA|K)KҒ\eL|FW= AдA| =]:v]lIBb{&y\vⳛʹ?\/xxqZ>*<HEQװH7WPoFg JEY(x;sxM'ķ-\orDGR.t˛Faci*|9Md#tI؝kFn- +^] 3<['Sy7ˆ5O a zT%1Ա#(};g>+>W8 vaRu/j]:03%m}Tލh[ +8Tuq'ݖ-9u5c*Nqe lo>_(B6aŗk*%N|=}3 po]_!W;7Gɧ4>kٜ6פҞl =>#AwaLsј&zܿ G +ـrrU +3Vі^vx7Y%$`^^`2^3MW>NC(12ۀoKu7ad.4'} Eʼ2Y ms +V?\q}jRպpϮ$)' s7>:?6~23QsagV`6J]n?cAi|؂y_6]4kϯi×Nw]iI(DEJ.zRϳIqǂ:QkvA7aXtVARWpƂh|]P'1x(W9N661Zb)WGv${Ϩ(R \`{Bv{v̓wjc]ʛ9Geߐ;ڵv&lG`9M=J2JrO;ڤ2 l~!(^\xuC/n-o]\_cJʪH]}!C| ڼT.t s(s0!A,UY3Uү/ z{񾙄?kDuGxȄ,C(Z_|n/_029}L +X(6˞nkPaTSKt3O +Y#a[@_XB|Tw='_I6? +6zM'|$nH m{Zr3`gyQ~[h %ʿP<XgxFf5~.}\Lho:{p5}0%h ]հ=kk6 73Ej~%.t }_8 ? +7S|~^ܸ +ޙ"z:\=iпB|Zk^(Z^ N7##m$tK|N[L[Hմ/ڏnܩ D|rBDԙ낹=EjD{oM=2:If)s"bqE"uǁ.Z]0#Ǧ1g38%Gl(Q(/ń;]+rg^_wjnoMISC}]6=vϡY}㷶PZmo[bMJmZC~ ϔ_/ʖEy5[H='BzdǔW/Pfo⣗S]ra.5~ ٺ/V2\fm7chY==fR =$=1php}Ts[ծ^^'A~חA13v"# 5?LO\8\6Yu!jlEs˜-ݞNSwG_f_f_3.ru{X9I`yu?;oM`yywyt#3aIPHAy|zhw16휲=D:a}"+£b"5[  BZ +{Y2S{mj,{viŋ‡e*. ;T&_pnEW(\UhnR+U&X&CS +qO]Jz.m8DʹơGӓ9 ߪ/? +azqB#t@) \Lq-rxTJo}>^Q}Fλhk9LkTL"Wtʿ5Q#xìBjnFoeP*(x(x%FfҦ6~yxh˽np@/æ'J?'I[h}ڂ @{WAQL@ܚ}bWsm}uBwT8 #Ze{,S}yf_]vJZ.xq9i<QmXB:yZ{?+>4EXajkGqy}tJq7ex.{656>?Qw}*1;n]| +^o%9` E؍R(8o gQ JJf>3NngaR[M_,r ilZ|M܏5E%Ejp|F+2Or9L>=(IFvt_]:aON^M,驁mSySǘHa~HXu + (:t>*grZ|✚;J@Q/Z>Xrb^LWae*bLր;Q3[ʕA@z~^o^e8JKibNoGCٸzb`V{I9g&+~_`<+{98:Z@'@n5a_JNaϝ#{26\UFVlv:e҉}S2KUi}L-/3_usr/寃"3”S: c +xI/k޵戗ضoYz{Syxٴ*-\(='^N?ʿfVFtFrN#VF\ely[2(oo;ikPscYZiദC;l"y#J꒹Se)aׇ+jؖSN/*̲"uY+"X/Yg/hm7:Ȥz<37f,z#jOz={ǧIlGS9WLvWyǴ+-TѶh/Y>zFl%ڽQ9jR'כGf&ぶpF9Ӧn+r#ӊna~%qɠG{M͢Giᷝ} YF>_YcXDd">tv=E6t٣-y5k~L54>u%#m{"N|KИԭtISyt*pZGu昮UܑM8LS! ׃}k|iSjQh FF]$3Z|GШ?B(v"fΰ3:*M~;֎f/Ws!el| ~B|0'\j/bq~3a"`7S)L*cRn/Rh^[acES" +f\7=o,hh4Ӌ[=8zpKT:z~x-׏ܾ{h `KY> [E:hJhgL޲u;gϕ=V7UK.⡳z +@;I vy+4SXA>D֎o=4@z 7 S@:/îQઆ9t&lDgk[p~i%8Th?CE1{Q/Laz=b&`AkӻT7d--|-XaBS%Ew27%^uW +gT+Q=l򤮂Iy 5XPZl|`ދhݥ探O[M_EF`.`[ w[NA6n^qT!b oX2D oDr07D0\5Ґ7iGh_D^)*:Gwocӿ>d¬˶X_#~t]D?FiirUx +>+y;A؝aqDA%kȿ$hTsnU {vҮs{SE$Kblܭ27IQ|[^?ڳxG5҅ӭ<(1g޼𙓲/[#k#f:l^vw|rhNLy5k9i|8N&?=ILH(Еל$lZg}™>N!z,{n=Yjjp}QWez?g>fgMr%W{ LG4c2>ie +<1X%T!nR5@Y~HYw9ֵ%9_MΌŚ9PUn-߫x .?X ,Y앾bU+BNLqAmT4vݦ%˦QhvOAd8֝}-'CoHŻcvs{ +{؂ tGc)5(DOK Mn#>-qS\V2az#sz$(-lC[lE}C; SHsun<įi;9 ]Nѭ̊E47wQfk]m5\.AbF-Ä !g=N, \,Ǯ([+X>8 vO7Wf_M=fkkZ5/s<)\ta۫ՈKr,HsYR>7T 7]zJÊ6Ql{.v媾o"x>iNM/Ȍ{,Ywt73$,Z)ZjI`ҹ| r,0sSV9](_fd5gQRR(D(^V]$H –ϭGK+i&zX1-w^o=)`$F%mdz{}cG;ָ˧ޞ㹚?{#%Q_8^_k宸jZ?XsO"1@9&~@_V%_ӜK==)og%SW[Jl[1Qϥ$oJlVsr+W|/=e;)pݼ+gdh8\!cq+^9Ñ KX}Q ~3)ddo-IuŚp79RK8eLQ8*ˈ6_0npϢòLo\o™LBT@/rl_ZyhDs +\Wljڌg&(J}?[\rPzTkaEoAtϲo|6IrN˙J voVNIԀJ.H=gw~F.{zMoib+Ơr:mr. Ê+nQ46bKM~JPSު{,Df|'yxQA᝭ZC}0/74& v(h^zߥ fQ$ tεR5%O~z(̡\JL6N!aϰy┪rm;b9k{vCQM3F?:R\ݏà0@cgmF܏qy*ƃX!<#vJۛqTZz3i<={x? {yum¦-`׻H7#tVX|1Hu 'q(~ދlZN=]QR=ZJzO67Zx [-$"8Cq8 ~ 6,(aA٬NZ?˛h+O8E+RRr _姟^_zfkT=).'S>JszrU:bO8|5i+誻˫&ơ˽8PgOc)wCI f?W{?=h~ka7tL*&̸$7RZ8{-T~vz=Q.7d2J7=~ sx=iG;GPW&yYIiq)9Eon7W3iZzF-V̆x" >XIPqDZ7Wnv䎑)? WM%.ZM\X}=^@IsD^iIZr# ?sT#:{k$#>'#h=%R׊ec7?:1^ؾ,l\ٚ0=*[^؏.hzjdrڌ +Ε:|0).˨7-=ykq$ӧ m"u(JvVJuڝ3m۲Uz'u(c~ӛN?#&k#*!X#!5T[4koKVH4 :]6f;Ò$/Bjbպ/}Fڸf0{?C"4,לچr! {lچ^\\ghm(-Lz_}!} 2ΰ(Bui֔bݵfլQ~MaOdҥ㮚<O(TlVrmqŗѯ'nu[?:4te}*@KɿHԉսt-sT:VO $9 7 t[XEXƐPa'% K&R +(!ș &V ,^ ps`gg? NtI - yl;,4ݲ!&tJQ㲄Fy ]!ށ e|<q%Iu2.VʯfH @2,UۣƇMV# ] _oR5@ F+7хo|{)z.L0?x,V΄cڹp}U7=M y QWvq`r by"(ןcɎ)cs129Lj`yMz[@biW<9 ׉5(npMSH$*#hc?*U/6MgXEV/'uP3.3u5(MSJ,5?w|#%]@ BTQgɽ +L6Sn]ꋄbõl9=◛R=.]>gճoC*:.qEiT$_ + +:lR0"o_sSYsm;WRD/:@H-]Է ?PJk)QZHwfZ?fo;ؗ@a|L)n^n|m9g=m-?eq˵Qݱ 4:~K-z*6)RZdrV%Y4!Uxgh Q lt,[Ů}4*n972^V vC<ԦhTa!7sbqkgE2Fֺ٪NZEeH=Cf2r?w)=aWuP(ը>]A_ic`p\X3{-՛PQ'/o׭/b\'iB9ܤ-8gT~ mHfQEDb8?3wE?3:(@vɼ9'n4M1 m2DvxiDXEt/ ++iRf4:I8cbi7&!U'0W~}}<J8'esᳵS8UOikMF]wp~}gRɰp7bh}ͰK>Nژ1|b)|Q&o&_84/MxGb3̏wk 2b+Lq^ d&\_*'iadӉȯ)}BP^qo<d-P0ugE33'.!/bs;~j6|o0k<dž4%tFן uy/TaE>[44cN}UƊ]?OZcXL5kB;ﮒ`Quҳ`]Aǖ{ 6|M7#i%6nu!B2~][+޴Rgx|Ji+)\ T D={+xON}1niK "S8#CM,{`dg7W+h3Ym"s%:Kt2X.,*qt) ʝAb^vcUu;u[aw>ۥP0OR0<=D=u[x.=?e&&@[W<r}R] +ݤ7Nw4u}eK"7xemtkuC?Φ&R}x,)*. t盋%e\3A/&U)2e4}}zxl fHX;scNݖ!]{Ȝn,*{Iar&oKsϝ:+ek4h*P6gw8mw4nu=3(+Cy|ubHc6Ryy^0SvƓOŐK=+2S**F/P+/sBՅv}ae6h_ݴ-4BuW]ZϠoinr}5_#E#$$\BDhYcP)3bԠ-KF{!>HSR7{(a9/smu_QoG HZjVjhaͬ@trFuS_jb!RΌZ!F kR; GW]',\!Z +|۸' tRפC^;EgR#q~wu/zHPj8_UcY.^EQ:s:kmͨuoq:Ƹpj}iZónn=Ưz';(U\G8*đ[}6V)BIR }?0 qYˤ\9 +aqj$E ~.'f5:kf4%WH)hWF}W+ mTK? +sw +La"72_|(}, W0f \=e 7 Wi @ rn&q DŽsُ~Y7p@ZzC { @vRll{4ZUz^<TAjA6,@pv %HȨ2  Yʑ #:5n6@.L'Y znň3Rӑ*uyP$o? 5/xW@8$GY@Ƕd B/1X=ȝrlr5va 뗆F\T"^r@/(/ݨr%CzRiCYa:@Q!,d RMظ|f!EB[=KV'"F,V~C/X;uP2gr~atȶ|C:e=3>@g*hUWfd% BTa%ؖ{oˡs8nqqЙbU~m r 1T U2y=sZf@B$@:nA;2t.\12f($2Oh^V[5^ۥu7یͬ{d`H+L.tj:k~#)Xe=qfVfHYz&HltI~W@$}jVo\'kys!^v"YadFauzwwTvҜZ.d|E4Z| BQs o$=`}IlIx">@WDRnkuY\S7tUJyQ.9MBLna0Z&v2o孺Qy˟_s0]@@fCKΈ|PS.o,gUTj1oXQX8\: UQKT +N+E)ja mNFғj X~2b-M?}"A=(/|徒yیkw>hh4 65xʊe툲BxZu}օ: lig#A_Q4ŽsZvH&v>0,o +_4RfF}x),e[*b]n [O䨜?Ԧ7X-k9nĖt'śR}Nv v;[gUe,͛kj7Oŝı$_arBP.,4[ +A_nZԶ[oAw5skV-@(U6s>wG7 +É/\ jsa}T-~j-ֳyWrnl&ZCh1O`B7W{NO,=5bDvw~(ưZZܫh%Y1s]]_د3'Be9&i[5 m$ky h'Wү?jpwK/HjQ*KeUMu[Tn]_\O(v^ut[:do}=9],^ZNF.zk+-3yPFYAA| ҦVϐ4Cb2;?HC;1's+ЕCPO%褥%q̴!,E|6?IVԇD[i/\uO6Ľ_G1G2}\;T;@QwۂьۂImmqPE6v۱ohlt^"|UvNTRCf%+/*K5,]߻frJA\kQY_@ܝ[#bJOUZTƑ}-X7:˨lQ_0z^MQ25N{t<̷t֐75zk(wnV qιCE@ЫYvO n+d4@I sBxP+\-6K`GZ@FDA+C6s53 +`ip*Df.v_=+z@M@qPX$$zG2pBRוD/R2 څ6@hsL꺀I]ZgrHfc";|\MpI~ZF]1H}Qqs6&gR"[G9Hɫ\u-ҥ(q +-Mx\AWzjWC +kEY:?qhI$mds&1V&7ZV-zܾ~tizܟ:1t"O +-_HlT›:bP(v@.2ޣ7t-SMOJsu[ls @vz,[Y/ٙ_&[nGthT[ݠw.ʕCP`p<.@4Qטve dyesc )y!L]bweWn~>';c>XWs{|PQx;|Gd\>2\pyw4? +mfXOWyk*b,Dg`mm:j& #obe]_SR>_D>=뮣Kh'v67>Jx#MssiW|8%Q[}\wuӓZg;0*S/9lxK>{w.ۻ#~jNǝiKm^fz L5k{_DC.H3(cD;~w``GW\SUv̺qEtnx,mղ퍛}+M@T#) +MN9Dx1񸃎"si|]egf{~#@n+2Vɝ6Vm{y(]0C;hyY2ȲZEX2,'GbK4Pi\x{'Ix>gFgYܛxm/ t>nXq@.{ޙKHR0Իe.-B#Ŝxkўs +JF ML?? 7pfz_NGb܋^7LC^D;l>vRkwIfJrlǕE5F`y,yU=,gt92Zqd>:6;G--VHbk +ՋJꭳ7)0_Uptjos=la }qvq}DI~e|xo#9E*Ta"wAFbC&EG3"8H +l_v<ǠpJF;j?(C_kxZt8Wn:]yU8mмZ1Z80qvS35TĨڤZE8cvΩ+ '0s ?;q3^xP(e9pCv:`N˵co]%˲ލ=D\ UnԫMu%6G묧`)+JRz|JH^@7Aw$S.|IτFOʍx<7wwoדz-nuym\LMφ |MN0jg~lgvJvTdHh iiRe0M{"߫7>rŇZ9[̹j&a\Yf网9~پX 'Pv=nߖYe 2܊J>#Qb2߁HpT^$:|F'AR̿Im}%R]^KуM+z ml'+c=zA&kOO1{;ƉŸgzoFEߟP~Xϖ2  ߑtOJUO_DKOއSC]H+ѝ[)oOGӚqQ5՛$p^za,ԩ tQXNb:a6ÙqÙ!~@#- {~HvnNv_R׬{gk.}yϊm||wCS`q +{j(dJ6SMBβɱ1e BR?'fW{gCB#7} Wk YQf> 9jq/[ +0[sijz;!]1PÈDN+DԮ t+z,ܗO_0'hŻ\d=( gZ-^/4t6f&Tե\*,L{ +U NCٝa"~oaįtmZ3>#q;+k(W7rxV_T33ⴾt Hsb8'CX1 0M0wӳ5 VHvٚYMg}z}.(RK&4@haHmz./uv%Mg]" J7jc)+Y޴i\Js)0ÿTlhۻImX8m>?Ͷ]+v,Y[ү #? 5ju²4.` b< `?1ؑ2Ypy`=Xb6V ^sݸ:P_ܩ0zN˧rKFmKpٖ8)-)ӫk2R\S_t +9|vzFts:\qMG=J.ǡ{(H^PDvܹ|ͻ{(y~3," +;Y5*Vt!;;VR2?UIMG-r [)gh(0iPXx0n;J3J[utU!M0j[EɵF4 FgRG\+7?E7Ҙ8v}Dn7eKYáWCx+Ci 0~>b.S%KV)KquM\OK{;Pd=*W>[M^$A +ܔ O~eoi%"-t]`y"My䍹>6YpN &'vA>Ce x V@4~y je6At{'5Zd̓[g½3: Ow$M>bh8h4 +N2* fI?2O1hL+?#}^W'_ƊP6~PySX@W +VDYM.TpW @|%$~G +~rI4\bFٷK9ȭBDj*Cݻoiǃ`Uu `QdLeyR%7ZLd" _w|At6.v\gD'rώ OcK4Tx}~V ps\+q8_$wzaSO}б+PdtUҔ,& տ/m}!D(yu^Π  UP@3V?Uaؓ6FE~u&De:ekdAt QTk4gk[o!\aa$A;t"%4Q9ڀ +A-yP8 P^xF>;:f4H}χҼݾDnD-E}W70GMݬ +l6 v. 8tGttb+%j;WDgqf^L|3rztQY_Gw!:T?9.FTy3Tw׊'x"+ߙM#=`̾ 0ٌtQ-gouWB/EE#,Ѓw4[̖v +<6w~L\7bXϐ!)染30Ӄ4O_!%/E2{dAHLV2KXfSftkbg>v+#aL%GH~MMoE,rٜa,Hl_DL7zmC^[^it$;,t?{熉ݪۯ{n + umEkry^;.ȷ]M^rWxQO~@ %7xw7^zX||9w^ECwZJ M7pÕsVewf x Ue)sRk0YˋNϢ +Tܼl(9d>BxGİi4j<; WJ\kΉN8\Oa(\|w۱*fX]װS,|{/4K5sFU9c{,szZ1_^nyׂ +{-qI9, '.dZ̓^q ;LN=|6iӛMe{+!*sf.&nd6Og-Oq=MrLw2WehvR: +U+NT@klY<bϼ rw[9XQܮ^:KJ7ZRcTg>^8r4sg)2PE;1J+;{m<崱h3?Yi&m$7H hQts7q/MtPԱwHsD[Ibi@]{!N6 2ɱ辴ZK]c}5GƏ:fLnx^׾9f_lkZ@vrO`٣p-gi>uU4{zyטV;̹̯3ZkvjF9ܾ8{*I\}:/!\gadn]YIt:`k\1P$qˮ>8Β57.qJAihHT2kY9j:\6TVKf5+'(/9ZkT#Wi[Wu )c"vk#n9<ŷ{gMOm_xSw{ʺ'SYt-K*hyzJ. rb;7¨@cC31^\H V$ +P~ſh^$8\|e"+=}z:1hd63(Y!z02/Ca%Nj{Q,Nbѯ\Bvy:;hޱ9@zV!ցȡڈ ͐r/\6!l0iڒn<]!E=κe:Y e;U鬩̳QE\+%p~Qc}qӢI3wPzXLkfQ ONoyPӫ:R_!-ծLjng-ös<|H-8[zm:H9 \dykK˹/ZBjKf\1U_ۖj?ˋ\=ΔU7Sxn՟|Ϲ07@Qm,CJ̕@T#GCAL;޺kՍCWvcix<꾭{%YԅW_vf~ŒW>inڠpnSulk9{5k_ԍj*Er*ל]VvחwVV'>jkvm/$s!E̚ȍ٩v!2c}$F?J9^JlQ<鬵;NzD޼Fdޛ:*鋸ڵ\X\Tb]ڙSOٷHe_^UovjK^ϝȈ-7;mΜ)C~Waeݺ 0kY[WuTWhp)˝QT&bub[} щ / iA1% u{h TNy3%C;ɏ_\$\ȗ6ȷ[ԧ=P3H.9w?[M_| ?x H "O-Ae߳_{I():` ?yWA>q_@^%lSA^F\rc<)O}c ++,$:xMt~h Ս.zL0?1|8.*iRy Mk:< I9[*FRt"RtYiT- x`?t 4,2^~4k@A y4jZ: ;y [w—2jL ϩ5-=شhynQ} +e ]dC~9M ]d0^54~wu6F5u?kTڱ"EEUzZl1.>wj1oúx4p͐_x*k1\Ph@B?q92bWr_-jn?30hw}9ugkɷ>o^Dt99=tyDpG'HԹ1bH*85&@P[ο.e5PixMn=mc߫XR2T"-9D)*|xF {g\[XxGBrEgDwDhtlVXZdѽBꆴbGp>7dB}:;Mǡ oI'T(\ Zƙb}{?!{ y&{3.G31G#^}*ZORKo{?.{d>=l슴 +J8Moޒųbnor/Ve`ö=S_0rPP%q?+5|J zX2 Cl.x:]>Bo"N{WpBTFdnɸoPPŌ<. m1=C; +k5ܾc.#u"p,{J|g,6p߮ ^xն{݂Yp jju1-XYe'R +L2GEzK'ys"9_t[6; l'ҫޟOl-& eFOƧe[P9952#^6n<g('?Po|VσxD d޵^ܫ]8JdNP!B ע'b[JQdܑUW?#֘.U%ŬhqڜYD!1b); /JY/wopq~ mlG5Le/_H11{'WCp=Fph +S{^oW#6ODCf٦89@(޺ +2se΍fi_"SWxgfثدMln'D3aw,"͜A| +mShT{3nuۍkmCYXl$͹>=U?٫^}+Sp.6-8 %>#sF{ + "~6?0zܱy ?O6m'T8_5hc!utfƚ? L~'Mw1F +|0.Pjq[\ +endstream endobj 30 0 obj <>stream +[anl/آW;zݛz-tg5m+Es̎^ ?!iAX5꾳헽W_B/o,BXI_>dYvڳƽ TR5;|YvKM7X;}1[WI4dt) juY^p?! Fˇc5ThG +^3UNUc%ѢYM.N7N߲#Dl(שw+.ԸPiaGMO|Kg[nɒP72<]:cӳrO8`;P::ըܢ3xā5ۖcƀ0ލ7sTא1ez_+6v;jGi,eOzQ.OҹZ?V+ +]'BUHjn?a3l)e:ҞCvU^~k +_R_a;BZ\C#k{Ho6Ug:AAmnUG+U^*[.Ԭ[&>O4ff_L1D tds*sS{-Ph5`8=Y3P!U*ѯ5 Jc7־;SuƛȦquTɌ o }h?ExX|70K0ޞA7'゚YH +mU} 3ۇc=Tpk!c1!`EPPDL;kӣσR&Y7yl @eڱ.ZL:96EXN때 L'X'A_< ->j;TB1 pɄ]b|l&:gt; ~oQX@9dPb7u4xc7 +}ˆ#?w7}/7ܺ$s(IN˯D;z>QǍJ<2VŹGpZxn裷2J}Ƚ(gǻ ߉vNCF_HO1y$:7hcW;Fd=$<#/DBb>yY|Xo6u7OaCn._Zޘr쬷}gZiڥ[R2st +k8sy/$ճU8uICS}@<_6GaTͪswVvtq}^E /oXRclJlKaUҝC?[)Gl+L!ӽz;yW"Kw^4-jl88:1n{0}:?4nwpO%AEGSGI3?7sgΪ_H +^Lt(1d<"k:>Rs7|yQG~sq9r-`z2;4?jt5]e"Sڄ{[XonqqFk#t%{iY I35Tgk+rG.5_;n~O3e|ȱAkZՍd9ԇZc# e?j7}#Q] Oy1Xiڅz]7F8:7??" ~u:w6GyR3.={zaݙsPǢuܨu6R;Q ( X7ǗtBX^Ħnڒ`%ɩ!$KB7) +d 9Δ{ט-^6?A7ȢwR6r8 +>9WHhBX}5V($?e6 sIVUcsΎհyԏp=Wb r%Klڐ@E_sҞσaCcv6ܷw9=|\sC(RVc\-e[ܞ,}+}:oۻq1z1N3TH1h˫JanȒHHH4BsIs}!n߿|昞5QYDY7nZs@pDw/TyQ|sLcf_jZ>9w[jѽuqMWƤ&U Km=Ԥ9T!Livm&j;ˋ9)svi/6 0-)ƽU/ނTpRhsF\>7NPZǰN˻i*Es g剹=[Kiwb +jetRI1G +;=g~xgWy3Cjua4ĄjmwaU"Tj1)3T&,7Ru2wt\}{J MHS&r[ˑZ,FՖ@{9qN(H99ȓ` ՠX+Zm%1nY\ĒU4^O~Q}BSZ.#b G0mޡ Ki3\ }!zN6@uo;+LQ?xrWb֑`_xInJ~*]] I3Yt-#אL:⵪f2g,^&s7e0.^!>$?֍ޕ\)ht}Vv;N׹/C?+ڰһ]ngl0¬bƱ54qArl, jA}4g[Uj9l{7֑ mCs,Lu~6N@Y<]څ,A[d@ŭio\a/'5vYtn>{< +kbu~ɏF%F *U' fCˡӭ.A7? +TCA״=JKvg-ۨۨ K 0Y;RiQ=`Lp=2E߸ 9g {L75n_]`ԿԅቌA^8{~ntnC sRaxFMJi_D39y.5s Ba*fPR3%lw>T7+q8|v7ѰK&Еճ]١ӃWjӚl'fÝ9lfEE:D0;z'kZ GA zժ"TmZɴGb:o8dxau07ZA_x1*tLm{oOR&]).O[u5K+=jT7'|kZ.T3t"/Ժ^drc +65zvc}6S.rZ̿R~I=~|f~P? _Jn.pb.؀^N&a?U=q ɗ$Ao#.}=u_ZVg"7([>nj7 +5t378BV,%ffШIS]UI1IW?ΰ%&C xbT[]Ypy1:qw*yJkCɿwJPKPCzu.sa6|{|3j%Ng wg>vV&-;AvPhȶgִl8qO,?w z4COq28H7^WLcS'My[&s=n v5yq^xZulN}wa^Z"}q6?peb8;yN_XV2=ERw$:dcOsb6/1XE sK16\uѩExW_yc΃lk!|㨏YqCYH'{SE#FuO oAoL/^}'i䤚Զ۰ĵ0I<,CgJ&{3lW>ŖU}zf~uW҈kg͠y>Б,>Oq +Z@|H$iADIrv~iLD!A+}XKWU ][ݎWX ^ŒoRмo]7'#-}&MbnFȷCZjJڸ+j}/)fWh@qy"?}+٥ڱ%Nf- ;˘- +,[߸ڔQkSVr/}m/ +ԏIuў@z>^%3 I3UY] a5W2|Y2vyurF1f| Tk\OjuNYPY;$EeAã<PΞ<k#y*c$b(K ?&ѢR2`)P  =8X> 5OWCvwo--o6j=хV͛\squٓr=K\E^ׅObG9cE144p~&i,2'\d 48'5~ u)ZAju:)*Cִ>WHg-gJJW)7 o6-᷅H e I> +Ѽ/y I$rg[G:CIZ:lɬ +mlMnZnP#v.2A:!³*T8$L8g8⣳Ɏ; +q_5KImVݚv gO k#^s7mţ%Le e}|i/pzbUù^Hif3|eyYa_?]63ڽ蔸Jȕhpf'h|eZ?i`6nΝzC%:Lgw2[A3fS^] Fo'C=N[m}z}N1tUQѧ'l^Uy䏂b9dLjQTs> Y'.}|g0twBeh*{7߭ÈnXT(TuYz D[=[폑η|u1xZlǯRf;hb}|4$߿ZOxRۮML7՝p{0/Z#GEhޓcs" BZ-w[xئ¥GWL v]|%W?b8<+ɒjc{_XJ#8̓q=rOv,ЭZu:Nڽž4fkwfZx ǘfdݫͺG:uôBuA{jjB|i լ9X*v܌Nu3̖Ԙ2m/(O2pU Gw tR(9D~,G0,[jךUkvh +֙-[Ǡ,􅲆Dqr}`yUz|jo8*犗Y+-<_0,lُ6 ,k>C:LuMC黓Dow=r6eJZ"XSW +P +1Ĵ28岕Z&QIgoiY,\ઽ{es[ +¹ Ze+U(3-9ndXO\\kƍzk@mWR-T=-޴\Ufq>X +a Պ++h %f(W,zﰿx'qTv$߉otz">G+5nсok9l~Wp϶˙ZʇGQXaiW#ɩSW% rZfPR䨥3i,TN29$4ݜb3n&x OG!kVՍ`+55Ss3oeTNyś +roί&NA%0Qv"NBdù+Z YxTuYv~Np-cdmQwB9++֫_ O3_`Hsk;b ÄT@#+#OB#`%t/ +})+@ 2H&=覢npbpTiT__nNA/+&شҋ :N@ U'uQ9_M'x'K9FDN\5.?PhIwu#Au"_v}Lj@8 ILߐ*?x>[zf6$7MHDJe `30]< +@|"K0'P@ˌJZaz@\/FqD8y8-`|E\p?b&8@$:UhX Zux.n_ + +r:U#+mLxW4 $y9?:o[_ףoՕ1|_ܶqbR%`}4  S9HM+_H}йnTL6)c{2"g>spݕw03$n?m|]7{LC4K1Q@y4Q9N[w`)2]@~:U nk<;}-WɹWý/APzA+Zo6Ѧ7d.or7ήr2dj\$zp\oD3K2[=?`x#rt2.fT[Nh7Nϻ^!ZChv=xs^ULp0R7C}R% +hRw: fj6fQ?_A +3} s(8×ƶ?qO.wݺ7)A`C}27"Hݮ빰ʥÇ;;0vsw䦞I%^XQ@ko?0yB +CP=7U67i]:E_H*h2f9qjWa3+> T,ZCQ/ve%n{siK i~rxvj Cw_ak=er:嘒0%w: +ӣfx11|= I a&qz/ij'XɈE}N{tvwӣVQF WvadNR9[湚 mkpיcz|>Lq3lc<,v\*q=Ƿ8?/qMfdtL==w%rr=\E{tS'}g@U֡}qɮ*-;ό5sjD7y'~xJ쑜w.ycUwevWY$$>rgcy<#5~cw0}w9<3R~NߚN5a{B733H&=-Y|ȳ>Vf}]veme5gD@&2 yC \Aj"Na"}VGCx̷y'Y7TwIuM;7L>@Sn"o55hHݮU5W i[Ңo݅p!/O9SnwǮ;;R;?Q(Z +=av5I~:`؟+([+]ɰӔ$>&E\ +\ d +dij~嶽SUc9\Ulׯ"xIknݚ&1!Gt(2M?klo^ͻknIOdZ:=v:Jmu@Xj2m̑)_R(F/ootXwW~3GȦ/m.LÏ_a>c{ 489ϱrO-M>PxEPq`u|[iD]k8c~ಜӘ4vw dÁ'/{nOLyy}{=A{3N=;b$ uw7nk8߽ +N;:1R\Cx}ƲEbUs: ^zZ,O{\[Ovm?E%-pc sG|kqˆ?z*qk ˨̓ PVetR_!RȢqkV9{;[}#֫Sj kT_hEh]OBO| ]tvFQl4ΞWcy + 'anfVl-Xo֥!7az[^dA[J>B]}n͏%2EE[ɤk'g͊imj,tup.Yeg(W# .&?3{UO+QcYߥ}=Φla1h$(R~ld#aʕ ʭ ̭&m4ׄD*῰y5N8? ,L_zAaV5ߪnTuÄ~GKCk4 *vj9uC mq| {Hp .5Υf^ t^*vPQ>"Thg`x\ Fn3!CNds N!U!߆4ہ =Ixg,MNz^j ئ^7'M/*Ri $& +&sRtQ3Af^i|]%R$'? d=o#} ?` HUe4 m~m| JE I?b1IxşS6iLp2O~dߐ T L+:JͿ[?pCJt) Ǥ۳ 1\U3U+'Ϸr N̲ j\yj36*y22t}Nj3py*rH;_7D<X_`I=OKI={݅":ɧڦPOڦm?ED}ݬY|ɉWmtl\$I"k&:B?_-U@׵_D|G uTd %"߷1>s fanW8JP{DceM,k]2\ WR|םPqڍy#yk KFX$R + +0,%`}[ll]1-3x_͇6٦»1>}sa^fʹZէS +6Nm;W>6='|Y*;;g]6D +Z%:/,H$ zYr'A{7_"ǴqjtQjw3='vYṕu޿դcgUm~۳:m}ݔ +KeM( +]>{aEy R?vѥqP!Inș߻Wrx>?Ϻ{gYIEv[Ӎ0<+ʪk`4`D̂uXy|M9#ޒ3WrT"MKݻVε5Lw<>'gaN˕v8j >pt@OWPY-F_|ڶor;|8BD +`xjW15 _En=9}gxc.Ճ2ɹ5~ ɢZ1CuD(g-_ +˪a LWkՌfxjlq 1VvS0JOSڿ]m۲7ic>z>jnUFqSQ_iv23lqͭvP.'֛{ VƃQEg?ܘn'>'39T&gqj3>;z#1f+5;z̆Ѳ*Կf=*([xu:9|zf¶o$m*y"̽q)9cn֐ufrFbKGDΨ~5zQNTU@> }="" + (؀wɺ;@LFČIX!R8ʈ0Ckzr$k +n5m.<.8YZKWUN'= p~bHP,a\C0l|S~Wa|0h(E $ @2r/{PX(c&E% ˡdk9ak0Ibgn=m)x$b,['\w|Yw*e}g(;ڂxe:(zK*&7B-aJoKY$Q-os]-ΈJ?iǾ.u"*~g"|s ^,?Ζ>:DʫqJBxF,t|O ^n+6!4f?ridZcG U d0lA 0@ Ab_oHA)} kiJ'R9~Cu⸁[ȦlћJrsuZ--Lh`]0}k( GH7!w9k*%qFlQh.?=9JtNq-ћZ[זWoIкاFejBŚ.=~g,z_ 8 : GP&p()9BcFvu 61?q>>+Vi]gCڗӤMCHZ T1d +`, ΁̫QyH;< $AǔԾ;D=-Yg}0az#IM}[2/ÆoբAPzJWSAaˆ orۄvtsv2u%ctP<C ]\~Od.N[ ; (wy.]{4{ؘN0?V1`LMw!@TfWޯKs)UnU7jqbWV?-n\͆>xC>/ա `N?j[R.R޻]tZLIX b\d_ +azWVѕ׊Gn 9{x^Xih2g兩]9}QU#|{뵬is+腳bcoͫpv?,N)(kS9؀S +V䜒|l$ Mnn5C*ݿ +vw9ŷI }md~hoQ#^-W=W3§؞ƕBGmCMLwӟ!%w)1˂[z rz,g=WJ_H:`N;"WsoS-X\f0',V')sbM-эQL+Nqq5) h$J-?iB~wşIxɵHQw)t#ERvG#N^ +,''2 >)7k"CXX_$?0=aW_|Ti/g",baE?l¦/ YW*RAM91k):ƭ]T +Y}h|^G_IsöfG(>W v>èhzJ}ʷnbJt 0F|~|jxUMKQވKA$IEIcy}3хM4;jF߮]WS[˨mO8X"F//lK?>rݾzf$$ b/_"}4:VMW목˪?BOI^R吽t?|-ʀߨYN;4>W߱]ܾN&XIQj8k0w^G'u/U|[_v^fv{>G":W] _[9IF%uuwon|:WXB8ٳW}tnub\cpp-ton2`²kf;-@:Dj{jsʏyq\E{zQ sRS#ߓ}ӷ߾- Ďg__=T9t/D{ke_߻WgvVY0:Zk=Սw~5/~KaE3q벿DUrtNrc''/+l_ [˃pԷ;.}sw0e,yk+kwֵ7cUmG%`0Dq X5\9 Soړ9`>d'ɲYO#8Yfn8MI ;dG4ۉޚdyJ[Ofn9YX-,jJIڳ&~BbL&YLe'EG {^GG[Y(8K 7O o7ɰd)ly6}'G|ŝ큒7}8OwY~cܷ+Z*xu}j`2ggH7ϬUvr!צH`8YYhVi7gBcf39^{4S&5%x㒾ϰ3v\}\^.bxUi;[ k=74vC.UUD]j\t*9裯!5GYqLܤSܳ9~gAPaKRrʓCDvAEh*뽿;=m~q᫞s5gѕ{0ʮ}^oqC9 ~%7cZ4ͱ G +$hPsv1ubJ2dP?h΁V̫M)qA]ye,SIz0AQ GH(u'ɸ@|ܫqzv4 b ~,3꽦7r}z#䆎4sl +mlwZ8Z?ퟯ*j_u=2q/e4Zgg ߓdr_%Uiyl&@.uMqQ&D $'$5BRw:"Jل]<2ܶtZ-w052-sol FVӚ2N|Wqmf+M k'DǗ~%*)ld fh ;x0q a7>Îz«я Z!8-Q:8[5땚(xh|TL>w5k"Tɖ7@FZ7Wy*U]#2; +^v\T>6|[S^h(li`2'L{gkh(;V+i'uOYnB@?ա09=vk]5;RDktU}\`lAۀa)"{E73M+m36qug7-*-K[u#o^OlQԫ\~A1[ )Ш-0I&쌋:AC+"wxԀ!PElyw{mE0_Y)$K +xIw-j|D]GVNqKT,KTFNo36 jKuo޹&7#:!h _di3{zwIvl'p{k'={("h[ ,ſzR9 +@_}πqwfāpJ1>UMY濰68Stٚ7! ~Q3a:tL|(Q%VX\l-}G=[n4.":QצVJE-ح*aUq޲99fÅ3g5 Ss>dUǼ zmEoOV @ n˽vWh.)>r:sܪֺy^F6xlt$VW\WvW> k1kõ]7cgcMĒ4 ,MhC-͑, +Ɖ;5^g~+[Tܫ|YK3b~o[JN\]W.Wo%x%E%1%vH:͹y);ƗJݴĘVBR7 ҫ{>@۩w_vv|P[>%'KţRB(.t sRR> +C+t3<W崻{^ _`eP*43A#$y#FMnHRG%Yv\RAQK!LKo4j{5' + HE =wJR> 94ܧh^@j.ڂɌsaB!& Cv,W{6QV[ sA(VSBRo7GK)Y֘lS.Pmw0-XsCk_7 Ic-݈ld]j6/Uc'pYlQ.!F7U)~oWɖq/6j)U!kA'7 oz>|ZsȎ?ωq,8&zc*cj-Fn8{NtNSq3| +QM;)8YKy1ǔ32$)L(;N.'/N(NP6'U$ P,wy#MԥSxЕ߫oNQ:媧Qb+חŞ/B^LJ?+bG>;rO.:/s Sqqbh(8NU-ۦ)kW +MK)~#Nb?b2VW}6Ѵ;Yv0LHk﨓WZz @kh5=#w*Gz2ӎ!)jʏc?akԟojZO0bPUpX:t84-"x}igOᇣpS[?~۝&uG.$J=`~&@_Oqi?>_ )٨iRu hl;GxnAָ?芒 &wܳ~[,O=/\=t줸$?ӧDu˻ҳ.KZ:ʳH8 +e +m5~ִ!7wa!.%ZD.٢q +t/2UѲ[ y{tiaGilf=wIRW-ą|4` A'' |Yg ͜ӽtJY^?\4/k=\F=;N:$kXv݉Y`Xno_$[+4Jm'yG)՘l,P<a |NÁ[3pK/7NޞNKӐ-M}[ss۳f^; ҺZsyeu<3e-+_$|Mr_:c)_$=ֺ@ +VEqCkSyݢ>0iyύjOe8{_ͫϨ:u\FJEqe^P4L< 9iqIml݈n Al4pdq*%ѭ՚Xx7w͗ mSƹ7]y}=-h*qЂ9m™pV5;Q6}"l|b +7PղgAVD1=vBBYi 6\Ώ-G-g\^F;V.gM)f,iu~d׵ tD ¶sBgl%ƹa;9]j<]dxvۡ}- _1)H3jY!LWm.8PKlRxxbʝʞg4`H46DՂO13xW)hHSN .)b٩tTiV1C'8Žq*\"~l >rڧpSs+y:P i R"g]=eɶ̉^OL"%'Z"=d^3:"A"02K'Ǯ`]w۱{s#_ 7UD߬溝h5ͪ( x徤W02 2t +?$j5mwTl}nJ;uLϽ5?JKݭ@@\UM޽鰟ƞ0WUW|.ZVFo߈~j9;u +QGC<3lR6Qb8: e-$b a@evIpRrhcijW{ <|3N-}3ӤѦЧ6ɝ핰7Q^&XK} mVދٹ7DbEH't~\eUY Dºv/-@-{ D]Nټ';/ v\6%i k9t:Ye ڄYCBk:kiː! YJ5\%1*35kE\ -V DiI[97N6_v~"0FnҺ-e9OwM^gXgڭȜ¾ck ɍ ^ӳ DN[?sҾ@b&:r1j%4yZ7|X;u~SϫH囝.I Fp*`Q[رasb-nHhDW.pO6CyVU&% 훘!TU;7l;=’1;e |#KONV;?, Ä@A^Ge {/#fT{ ], ` Lk)Oo[{/EYIRF-Uuv::Ahya6T ú֭y>9*xemS!L'e;\a dA[MfI7\B+]px>tQyb5 w) }lG*ZjB%*@"Q?oj9SCayhlH=h c?;jYwGvbs<M,Gr;*ZNh .'bv*&NޑPe"9.ZLjA[Th]g6($|N,tm&.ȦQ޳9.u+zmh>N + "m/֧X\ῠ,/v/r] jDPSL ڣADxv\;59Vvo=Z}hzsj[*7 ? TurB +TjWY.My?o?WuRxXdV[4K YP~u6\ݷ6lgw.=1½hW&-9:sK,zTLxT6apd +J_V|_Xh_, +uȗLc^a$M~AJiG7wWm^ުއ~?܊{Y%Yl*n*ݮ;BnH[nHXZ)ۇC@@W|@"uscXUڄ\q9md v G .PfM.x*ھxX]B[Xعa2xx)q/``uRuw7Ƞ$dA', -qԕd!ˬtdi\&/bc_;M~yhQy'GrSl8wʼn_0(U!??eğd z'o-`~H9=D*) &2RU+HV#cWL) +'Aب*a+{4X{5ӠU) ZE5UbIiBʢORqvMKs5%xzoћo:'nPOYy#mEuUMY7qX8]PzWe4z\)%.r{N{v}__fkkޛv?i'3Nf)Nk;N}$EbdOcu,4^{uQu/yG8}ivu} +c%4.Pe +p{q PjSiU3gȍ?ğ?`Tsk8^6 L:x8O}.][^7Yף]|Mys( (J^~#jadl8崇^aYH 3R#0'IA& vQ|<f~kq6s]|X,s~I! ztxmj}DwVka o^7xL͔ҙ'b$aU_|#DܻB_/|~SAqԪ5La$I[BRݗбRh-^لvW͋!k~8/pmBI$w^/ϳ|xG2es{ǮŎɯTVJwqĚ/(rbm*[m 0q2Cٽ=[t5OHM~m|WiR 8Av{{mnwhVյs^?.İQ5sOk0u9{S ܌_ve(ͥ4=x -T"hEt5HZ ?\r\'yîx-d{9<ħҾ>[I]I7O29]h] [A,hkR*5pibK,"vz^j?U#a>*sr7o 5RBμ0R>\_nΗYŦ[38tro*b3HhX2IτVאjњb0:ŋ48=o]s69/w9s\T{d2 x\: 6U\72ұixv> ]A +YC r=dڴ'ҐQݜNO]Bˍosh ssY7hELee ŷ>g"c_+stEw'L_͎@*6}hz!A͆,&q۽#+w쵳o!UVLLuiUU>ڟhxhZExa1Ʋh_ Yv^W^3q(NjKnUp_\Ĕ'+0f8CyQӸ\9XжL/h(w\dZ!B2Y&"c<q mjptŻ*s李 >7_i51ٹ}Ș-ɬ;0 Q;:s:Tv#j/y 獠ɹC`:p8ݒͲH^_])qd8Z/p3w9$Y_"n#Cƿn7!(AԂfn=qM?op?xo ~K淾@.{i5U쪄=cuɯ\۩*H KFyE,ƃ6k^,@=wô3];8QpTh~gSz;0Vuu`9mI16djmlobZ߫un]z4Q5;)kU_|JuT;uX+;\;p46[F>zBoRz.꣔_p?0nJ?Qm\C+l)d*jJvyN7wro8]9sehE/w/Y{=HlD3*z&zx}0BQRʭwCNi~(]=1ݍ+F1̾mp/}`6֭`Z*wl(i@v+7 +5s'~ "rUL-8XNzJk#L!+>EmQE,Q}މ +w'!? _=R)?!"{_zۦUerN&}ݓSuۇ;:+,㩺hkNAL-0d(wwy9h' {'Fgq +i@@?t[U%غ}kA +AEkD} >m6 ӔkLaA g_6 )oUɧɏO1&V'IWRS$au#I8b +_OROr + I"ĭʧu]6!F>ɻ=Iߨ RFie%ɧfπ ӟ-Mn W>~_7+kV 0 ]/NO}$ u+ؔ|%mW_J$q*/7S<*N`kWb/vYqMc!. 4\EISgIӬ/wp5RlYi[5G;Ӡ\*$Q_O܂|n'ڴsTgcRϒۇ #7owrn蚚_#i{xqkg>&{ҬlOl8_@u}QL7'ACiͮCK0VxɄ<|xTkW =sdw.AIQ2pn )Q6s ik]IW3vrWFܑtNz\x,.ܡrW<:{S#.;*i[,79{YS]UԽk|ӏ[4o6ͭˤuW󳵪¯NsdqּTM{ ۏ&ioh~ݣ/:x,?s IFRJӾ޺%o򯙹9 Tel׼w^vzcaowo/ +sLjw;ߞ3s])ğdlU:;޵Q "1 "3ѳ*V#DZ\is<=O]"'+p֥(ByqS^WnZ+/paax̻.-LO6D ÷VqZ\59d|06w_kV`5'ٍ,5w/Wi\VFn88%rVsGw^=n:5V۱;?&v=FBhOz=ko&I2sr/H䩱6FoJatBER<2iP{ffzתAϾ>n ŞQfU:6vfr4crbHըLsj8*4 Oq1QL6ۻXC [㺸$鍸C%_Iȋӗi׏&Qcʜ)ּe` +KS-$FRFiaؖaVip`=:LHq0l4}.ڶS\ Pls\ہy`fhs{9ٮjɋQeTIMFz C4stRFWsT +̔=Z)]<F#\I1˯ #kK#TK<ވ}!cy\.9 +9Bl>ߺ)tKw +f65s3222,5BiV`},hՌV`~v&Ixw\a͔ͥS弣 -SE=}~/|zU<v+ݜ95V)WxnKkta}OZܕǬ9FyF5x*4خa y{"|6lļ]B}ߴQ߱X.&kX.T21T~5[F:kұ*js22|!7n'8oi]9w}`W@A]&5.$;G$<*-xt +UwdWHVepz;mE)Pٌ`2X2{,~, Ej-H t$1;rO (`; pփX[<?ZEe%ܬcїr^' [ALA֎ltUfqNt1emZ&Ԟ[wHxsiw M^ r$%v~5cP`ujƟ*:U+TϖUQSw*K^TK_p&L_K/jvCDu}AL׍FWȢhB; ^⊇)mQ,EVGЯ#&Ц>Bs"7Q;q.䟵|sq~R&iI{1bǒPg:c5~1 N9+\׈^y%BQb^57` "(Rl0Ů 2_HgB (GJTꙞ"/R+(;@R5~s~qF۵yի2snzIY逫{J⒭0\:=!Ayo=7<`2p0x xcS)1o8yf*2?O疤\ֵ;p7ObPi.~f'QzmB@6I9%s?$n kq))@ ȑ8Z? BΒq{v k\z,@9 \bo<Yx٭/OeNy*e6 +P %2tЊhڙ4#ߨu[%(Nb3O9ƥS;<ֵ])Mf?:/'fij \ɽX򷈃g-Bo`wwcӚ);xiɻlIҭr)h'Bv=3IVb~KKa)d/?_WoOvRUwJo$y$ax4g9HO3QKEkoOIRܧil ?qK67UvQW~0ATKFk'>N ;&,$hKC%mUfIilQڦ>*vBkQ9pѦ0"UsJTUk8C{c?ҡ~C*u%) ~ }8G{?v].I24DPU+> H}[~6Rn|%v$f,|u*vI߼Чs^0@۰T}W(Ix>=|AW䣬ih>ITv46 Z%I_Q__Gtsoiȧ(=;9,}[9mm㸧Éw`0%򼻻=^[]n\oV\/χ*0GY}V/<"Wvȴ}. z- +,=PߢT:a 0WhRlx@%zqφ1zq#X0Zl>>ԔWoϲ] ӎqPo<"xD=\#?$_%_,W v7ḺN%luonrx]ܕT`{jl:`Nܗ^Gao|iYe>+SC~;ϊ4#8~?H|הlz}%T:_h[R!PZ ҫi@ya-˙ϛ{T 9m7};rg7/:L-VR8{:ɘiS}滳Y ӨZ׹G0YρE4PMG=$I{4XiYYO6Hݚ_%sN؋ѲF0VQ fK)H칊b.a{ɪ-B?|egAevC.n፼#[q#KkUIqU[Pdfכf=G|:J%2ĝPwIKHak/4ÿ́. +Mߖ.]q{ch3R":2G埳TE\M[ઙKj$%Sgrpw2F`r9*m_z˺ +Z!hq9Bw`eQ5U% 푒zTLU/͵5k99{-%YV/I +RgIp'oo*[7Q/ٮp v-2y;S9Fe 3i; /1%6UȾ_,n?9b=.AB:C3|5 I!GMgvlwwܠ9ds>A5/l\ӟEs(t8cgh^},zVnbe=o*WλT==4I/iDf +LqtCYglnvB#w#ìETS oFbw&t_MgB$MY}[d 4kY +4h(2m+e+X;%䣆fW'sB_ĭsyuuK]tH6ݎVЋ:gMK[ uJA@r_`2叵nFsZ $4y:IQ v;rx Ss.yvA.uKȵQ6 >uc +|( 'tu{ yB  +F5 Le<ٿX+WH>D;^6q?8 <]2}0ajL@Nn=$y.D]Fn&Mnx+89y-"9- ٰǍ}ö?h^|BV=ȍv9DvVNn -yǀ0szm{tDݺoO>VM= >I!ւcd)ԱJҬ:W̖a3L'/{>_8*W8,*\d])eq䐊He<#vE*B{jSc[.=t֊8!ȆE~ {RnY71 +G)h3͆DNL w>#N+i߆`,` +-1Tm)||^_J( ;' րK5;cTRÝ;ߤT/iq <ݯ] {c O-u꼍C{jGl'R5G>_4Kzs4ҘI~ vm i$`C`}&`C0Z0ngݟV./jP:Pi07|XF'nv[v1.P_:<4 .>J+dJ\hFoqpj,{`ѶR_s:&&&7Drf:gùoO"ތ}gյ(,'b>lD" z':-oZ{bލ~7TB <(uv`/ߘ8$mhp:ۘi)_3.Nk}ٮ7*|sm r\n$:m"41LmN:trtGK,(:۹<蜼3/=d7eF--.2^p+IE&Ŵ @Ll gӤGŦ} +RGL (J.$űl5Q{*lĎ9vo$CԞh:z)k{~W2 9 AMhX).. 5g7 0iT/E=L@,Gҗ)xJy%zۮ6^n|_t no~DJSP {:s'E %A\ [N37,ׯ#'b d +N9Hq_ًAqTW{0?]"4k4) +*#$cvc&i?+i8nMX  3q[+'4@_,E(W_u`ֆ)@.9|KABcD05jbGtJy(n=h\'|4^xG kY%_XX1ljg_?m7 g%27]$#2I Wd-=I% |8`I׸+;"Fb[!fq~ĭ +^f +%c͜{hq^KY; A3 ߲ >EQ \Lu"l-^f NE5YC9hzjC"d +TTSyMlj~-~B\ _){?Ow`qɛDӳ2v;4D4s:?o^!CqР4H|T5SyD裇L' 3Gau Ssdp@/>9?V2#"]F6L״עd9qPWѦu {|h}P748N_)[s'/9? 5oHmᅪfP?p`wɧaPiɻ4$eFicaKhAvsɕ4> pڠQyϯan_q7ήJhշoo\]ʯU@NK Š!_~coqUPfJ{u\Y_pIDyY$=Q}Wp sC_7zu~VBr_B~ Ũtyt\$7h]7. +Y.;| |PK~;k|oW\#q96sw?9-bWA ـE_XMSYݦ`  Vy<|pqh3v/ٜnf*lCԩK~2IKqssc[ѱ$'=ŵs@o?G38w>evj6pZQ\ =3&ΓIɾ8kuni8hE@9GkfL?x\sDx*UNNjh>νy9f+ҝônԜϮU\8ZU Y2}j8:SUW[gȝ^{wodvC9Pm'CZ{$YD<F N3,'.0쉠ׂ5w:5_kƐfuV'+IEI-N古쯟\4i̤*ڊ;չ^O:BN.mdWAh\ {J3<9:[1dLx}Y+,5}OIB>=倻^16R,UզWw#6B !Cꁷ)RW?4=9= 礳\9ԶGq[@elQJt}N*`/MLlņ ^0µlNma6q9ord`e:u" +3Ar<^M|%4gI0Z5ԕ_TFTs8(^?e T}l(#xRqdҮ +ݚK!Q };q[6%@ܝk-hŖ2h}kε8|orzmv!% m07(8b(`4Әj~ѻuC+:p,C#D"!Qc~` `Ig(aS%que+96nlcaT޸(߃Wwp{RLh%jitڔ] +yo߈H< 'SK@77E(D]B欏r-q.nW]^0jcկUNiӞI Y5ۺ9ǒM!GfqDܟu}'0AqiyIyc'1Z..B 0nJ<gn>;ֺ7+en¨!L_䦜R"~MҭHginZXWcX9_})nSD o>R1N^t6(-+_kԩٶZM9osag2纍xkb^u~'-!~՘LC\ +vcKlX)!K: u4ּY؂.yl sؾCZh γۚ}(E{arr\d%ĸ]A;6%WlFИ)q&cV C"dD&O' Alrm{D1`Dٜ4 )'w\ضbrk.hzcPԷe}Qwgz뵖˼Bk$;֪e~$= +fX()GtZоƙGf)p4imTT~=;SP;J,j-cSR6zV_1[A`:f +?0mp5 +1̬ᓤO ?0@HSDP r]YA|USiU$VVJ.G(;LM)q.Ku%,zZd_s1żEv~ ާa/z -Z%'0]o蹯bKwkq V;h{ߩG f],.^vBBeZajAdv+,[;%7FSi@h5_}MA^:81:O1nrewkdi=4F}`U$ [ Y+3+" )K >`s?XBGS< S~bm1b7}q1l{z^m(7QZXav)}055S;)>89PG?-$ۇ')N $@)y]@'rUEs@g_ѣM1'&x\\p .j4ŧR/Wa*W  Z2[@ dgsl@ +@@W'^U ;{#TM1N_qmqT%S9h .jXHeӀKEvU/kzCc0Lf}6Gx gzJ(Nx΄D.1p+*[hS]um~^^EK[p2l Wq's@D7Z@޶ @*PLc PP_(k͖5)&@MRO݈ĺoy\@[f֩>cbOo0;'ޟ'n6iӡC9gIݕ-߀)ӿez-䊙-vULȴ?߯g1#[d`x] +A +abr[u>лZ4+Ÿ&nYf.7Iޱ.7^:p!⛿u&~t9H<3*kN{}%|{uʫ< ..@s$suTm=oUz>*< sqɾy9lIl)&_&n>L^[-ڀVTݐ>$׻_\/sη2K5xBy缽d46t:UptTSMӘdF,j7^b"!9!#ܘ=0yh1qxA#WUN5丧b ba?;ѴP]m[(hCGQ6d@_H%mCˢ,Qu(ڳ aW$]ܪ88aSTX\ UC/q7i~w3GJ;rL2i*)q[^۝0[`=_$90ZP|x|*݄7mlI2\ɢ#EmSs6^k<+GԟGr5/Q.umE\]YVaL[fu:|kF8Ewi\0KX![W zm؀ɲ-u`SkqY/sҭl?sUw=u7-sSÎa<-Ĵ&mh1ZT(9MT<%`֍vҬ^f>A'Wh8+>(dW 3X^|̟&s)&֒A?w<ν*Ęo?`1eU HZF/m>EWx>>m}`!2JYTOqAF.{7Y={5hG|0ĪO/Ifd2̜vȁ͗!>bw*[۟A?G~2?rm<$1qWb: _N8lR(eroֳʣkAH17f2lMibO鏱2/軨Kֵ۶B]u9ݓv{(M̖*OAzǍ5$][%ej-.d,}6A]f{{E,B3 i{n1_"=ɭtg36-]6 $NxD}wjR4`|63.>,j-d{.&Y:^ջ *F\10^ 0Co'Go+̯-w?2ݧiu'AGt:飋>0pi-H?^UzC +#}㢰.s$ ֵYg,7.כwnD&DNR +uJ}ik/2 8x@au\(-_ {vXGuF+WJ* +ׂQT㜓ͤYm,.r twyL٬W[繨(@'D&X@Ql{̜s2F ʊVI5]rJĂ!V1pGr/2$R;S<έ$7n*{6S͛+:ՅT&l(Zd[H ȧ7[kajvԚdj T~4ew +Ixi/Iؑ8$n^gM@/bo%rul1U\Z,E 6b^dplN/?،3ZwWGE|wh  R-v=ƾ/@ȿOўC+@=>;;8KAa5]bwFr3+14WO==uI֔U*;'{HFW(h0? YlLa{>g gOgGln fWB =U6?X-6KSA.5׳SiE/ dOa Z2Oi^F[C0ٮywyoޜ%7W!e6ͮSwj6 r2mݿwY,, +ܽ[o|\ܚKhR<;e^fInn&9%ꃴP <_sILn j\8z%Y"o]jfBLS9/դ.BzR݂(Ѳ w t~p~|C>΃ڳh6j܋#,䭦(ʗ[EaWd1Z`]fKAA@ #-)kwJ +ʳ:z1f`$vAuաCF+u=&/{KVzC*fv}[؎sg9xA܊0x7 nf"M'ga=9l嬷Tr3@LӕﺢϕzSmEI[7/gR *Wbc0 (~G8VpĭYh7a+bUȍY=uQR|/DC졨LC4Nۻ( .\9ZjJš**.&_g* WolNvl4,=xͣmd.|+_X r_HOjk8xe|Dܽ@XX?+͝` GUn}djRʳ9dY<1rC3]BVhhYUȰA A.MzҜ.%}fNBi=lT/S)٩eC?Ag: J|T xR2$C1WWo~'Jp4MzKn!G!#)xIxBJ€ހKg14,Cs#Hk cD-a ƈe1. p8{q}x~kN}j岗z3c{KFzȄ}+ hB%fm?R/3"[qwG@@3.#1s kBA케@9 q孀 qKH$v@0 Ն1.1P~f.{ז'+rC!Ԇ-/TFAW}ڎI؄uUXU_r JsAB@Y^Z\PPS T"ZtaʂcS3?G%f3^DfFU ._q"w'ro"<_1@_@#^YZ"6rNLK]@d7ګ,U*W9ʑM}>%Ɩ $5?[%$7hIp)~O 38T4alr{D_V?n 8x (oq7h +\H1 ͙w\|GkcT{D`ͻ:w7Tܲ:7*B ^ȹUgvJN>ڜ&9 DM$Wu1|KOLoqe7 oI(e+ɹ A3;'a,sO8ha' ECjc&L' PA|c?oRǏ^( aTumTW[*sޟ8Xyj;W[ >D _su(Ka%g̵˴;3n̴)2hnf x>[4_LŞ3v9Nާw++)cجdZScY9B޺󐾅͠v:5 }OSkȖ2<7ip.G2>C*yUymOga['_S_k0,[)dkScV,to#bPk#{U7U~R.*6QFhm}4l:  G uJ oamje }BuȯozO&qWOySgz,pE'+Zi.',ݲ#hf[kY5zGNþ/:ҫw wiViw`v՚VsG˹5G~ە0 ʟ 6,(zZ.q*װ!Tm;aC_#}7{?ؒfuM(rGw"fA~H;JW˅qNT3$*Z,Z30wVgb6Ūx.b[$^E^:Z(HY2yPPMοiֿXUרCS|D}.&zt<']x4#՛RuY˶D *M&on]l3x|-+ErjxSTxLd^m}st>-2DYkqhͪL2lŇR;[n-w5j޳It+d&L?+>jꜽgŅѻB|hdXtQGK[EQaگv߆?s/^,Lxi< Ej<Π@Z+3Z2Iz%]qVIn|nܲ1R:߮$pXɊ4ǡG2Wx T?MW7}GO6"CwX4m؋7becĊAEGBk\nM.tAtІKeIlM?De< z`/EVX~&Qgp DzUCZMeA֧ZANq_QnćtSRjd{M=nx*f) ^T#9vén!fCU.ޗ#1Ƽ..*rb3YQ4W;h\uہ6]Ԝ)?ڒ8+:N?.Q[xce"+,?yX7֭!vndl.pfSL=o3䶳1ːj!g_3z4:;{{)Iw*ٳM_2tf9J]nle%Ɓ;O)nf%)=[yef37M*.y*F%T%8 +F7| ټ2I(Av3T#QRFFK ;zJO/x|xӜ"ƮDʫLN00o"|WvV@焸l5^W!&_4!jj>LKUQ~[(yO)2lDR) +ޮ FB&Y(Jy%:\6rhJ+u;V.tx<>2sN0*}z`ܦQH-8]#!G>_/s5< uOgG#G9iɜvnF`9r=/H'Y3 +Bdf!2(!]o~}ݜi%o?c)CK0}^7bتOQL2bʝŘovAB&ƈVCOa]zG%]lEeLB#TRpܙ~EzRxT*`_q~3ZX<,9sb!Y A):OUƂ#4]U{AJѹTUS9.4@sR1N;@* HWbZnd.qm~L[ĩRWa}d`Tq8Ғ hqNW =۾͉zA!PPMT(&˩05Jj P>Tug2=œj1^1ڃ:xmعfQXFБoV z6 ?3xuB!UVp᾿@ؖlʀfB*Fň Z~0i L?ci@?/c S Z:0M9ΕUYtI1 m3X5ٓx!B`xLXŹ&(@'X+a1`{o38켴w;4\%`GK,*~m+;"pw'+`,YťnmIE̵$d$tOaBp#AdcE>KUq2x{Z0z]͘[$Y"UFLc6 xvA@2/UW_ +"""~Z_8@K@ G"g1&6Jkޱ|e;Eye;WA[$z~B)AR̟2ل@/ w(Q (5g%@aPs(fń2٬_NffkuA["SԡQ*Z{k/,+IUm4#V;u ڔ'l=^t-O4ߐ[2;&O~Cz"Om޽T_'9[%-{(A6M4?n&}?^?!nTڤ?Ff|7-ﯸ?O( +:-z-~h`an+?C~oΥ]=I"B\f#z[֫}C9'VRPo +xJ"r1zGE܅ꎵ30>~]C?`1v.gU\ݔYL~byccqI7y{ u>9ѼPFTmbVq1cC#1w-h$R=vf<8]?%zNuqqs8mt|7{Тt'U|FDc7K/G- 6N5hc"||aT0BC/O!z]C{S<8>͌3)sGHjL-̨CO~"Uve}hDRZe[&*[3o<<9lꕎpAh}O:v ;3tVPk؛wn!h 9ꖉS_5j?+`VVU9}Wrmu.]y~eB.梭M3^+BV+}Xg`_7oДn +Uw),.LWyl-ݖ1åUx 2]qCqLU-y}W)piBvevJ]4f7ɘnTVJKRH,m/k<(0M&.7@Wpq3׏+8kM RZ\=K^7K=D3 \*kFCsQ]ܰS]Y +qe@!+ܸi' Vt QGU$;.^'@⯭Lq?{l[;iG"vvݝ/T}9(RDt.L1ҋR^@٘RF#E\ޗ-FnbmIf.t*g*$ X춞{QK@xv(AkcAw*yAk 5Ɍv7t}B+2λ -ƊT6m̅y<4K\CDpEL@O0зh-^ϽNhê?.owp罣5PnS#鬑 I;1HKgi!DYi~T@egzu.j\>Ly0XLkbVbNg1f%kaW bQcZw*Ws(V;e Y+HG ޖˁN&N/8}@\ʳTa1gaDzTi@m=izxVP ߩխiZ5nPW[}j^T3 ս5OKz=䦹+m$vAMNly}u^Z2fһ e %ߦj;ĉJ^Jvz6)B^)s#Ŕ0@f3Š +nW [(Q, k@3yv!]w1!jqˆD^᷑&;GsXCa^ȧw%C!l!ko9nP%=`Ǔ7`:԰(c4(7 >?dslnf]T] (x(~ݐ׾v&ZEc锚3҇& {S\ĥ子ob a8{-tvlqtDBEG:qd#L[7v`?kOwehCb49`N){Vv*Hmb7 +p5x\H%>?\MVU@Sq__+sGE_!GbXuo2 tuKOjOcpw@4TfZΰĘQpH=b|VߣI/M:"0T.ϫ23ET.7 ʯӴM< 13@&xz8(|q$l Im]tm_nl{Id+Y9j4vOFOE(i͓ӕ=p.)SUJ!:1<3 +l/z2T^1D5w P1 +Kv%5U? N47͛+w1@~b>̡!{WteUuRRzroG);v O ̌`3)&"0+t)U7<:qwC1"pwG~ '1-nc<[_1zsHexQMk%8uvH?&KE7"z7A?G?%&bj|*.0\@ju "ȡc T6m]r`bv3Zkΰ`VSL]b~+&s&kU}oM 5c zH%K\9dM P" + )i˽]~;وaSgl"&=sh߈&>c_??OocT&sj#h|41P 3xh'_ A"GO@ӛrv|:,q&%8D362[{մ;;9gnn.:. jHdwf\[qbADIF7,o!ty?F+*nnmN[+>ݜy|B{>Ι7'̏7{w8k:?-MMld$ti&y+=>[e[Ɏ.[1;;t6f>Σ<<aJGF mGF8L~3֪TKa\O>VK$FvLxOUn.s$= +so堭`sogk_zכCW-=\ܪvwb2f̸5=i` (;YѓjJ1EddmL u*zkG55,|;sI,n"4)5;3. 95$5F +6haHkL-IgIwxvo( ٭C;G)xwNPεmݜOU}u_Mk87A0n.ޝkzsjدK&>fxOz=1?Rә&^d`T>(gpF(vǥ +z>{<_[fJY%4H#T΍W ޕr {qWZ41q-=|R$3^`^d&]|L;.C9zgOyM}ŀZρT=Y52RoR.Uwufm:~KmvӁX`U).+P*糇 j-?Vg䶚iDo KwΝ`f^Gڶר w΃䈩Ъ%SQ2c9t߿Vst"OpxiySj +NCYccߒv [K{]Mhyys=SKF~Z%^-ܻ:+ +A!Mϻ iE;8k4_*j*cluͬɦGn\~w+)(E^#<~V +?:ٹwr<ʃ)S.#Uml)-UL/ЬN3.F Nȕ+;܉M$CmvA6Q[jrӮjeADD6UVUz÷;XΗ]lJ rLƬ=0$SkȌ +Z#f߅Uۆt~,SJM&G3]k@ ckqhN'ҥK@K*]y ,JT̋, +#.F?n `Ek1kU7/S2U\<7hU(.+V 5 5Lٺ]6֤pƟ:P%_dւM5M~B^w/DSjYGI<ƖKM P:p/6\Z[Q(uW젃Vm/R F:,KV&.|:=k'sVxy@>DfOO#y &5 H>M +!T#%F"i&"ް/L+nb.ksJ>ʿ+ y:Os9dO߁Ko{t\Hj9Ho'h-"J!/05lAْscˍ3+'׾ʃlcďAv[^/Y@ڲC| +ac+p١iwZEف@#bLKʂe{B9Fw|Rx"pDe17vVhl-WU°]^v@aD6 |~nw?t/f.iY2ZxҴK@"1oK~bLLm_v&T9__J=ϗw<OY +eNC1ZFqh Pr8a3M%',ZУU>7n;WSƞCd>zo0Ս9 ֜]iVskk[9ج:-ob.Q։{hGH c*le  䜢7Y~]Po +_otBr9i\z纍ndo囔/[(l-K(!y;Mtl5}NrbgΦivݡ=ܐ&TV1DRmT!࠰Sa,y[+B84.-sl:adt]JᾺۼb7 ߭0ZӒ:2?iuxXT2Gko"bT/S1i_:Juo0LV%`k!Iq +( ̳[#L<%rmBQ^o&LetLYnX辵#] `@fvR6"RhhRo@nܟxl@nXf@.k>#I@.AX_9Es!sf #FÔ3ï|W?4DiYxҨ]`A鮆;|P5]ѻ/"F|X~a#j5׉sP `a1?YJRf=Nk9ռ%e(yHzj.T 9%` +0|<`=`r0QN&?c@,`f0UB;L?WGe(=&L +Q*RUS'\_Bm_u*_9,2>Df`xy󮀥 `K`AyvEb94{pR>CsҮ 86ն8?*+|)٠C CwojF\gA$Mg&!kM$`D>$9khj<@h7 [IZ- ;f<7Ȩ GW1koKy.[ +E 9 HRA0skӴ]T F*JCHݛvP!gt"i+<1{wWM_}h@vv]p(] -b<o (((A5. |V͂W;oE +py—nFL4K'S#G@7@È{b u=h|:F2@J$tRcMDDHML$7!ɾ)'!)uvT~2p{i؜j_9.n77}.^ur[, jH޸.WRzd+d ĀT9#O}mE|/n>*T[/lݢp>A, l49eYaWݟ8BHz $/lOŕ;p]~RZsoEuxx:S&}!}zë&V‚U+̟~빝Wz6NruxǛ}~+;II>WV>ǖUsZ2׏l(zzѴQLQ{aWz>a[8+= > t?;IJ90'_5TTg9ټǔp(15Q UO\AJrzvVv ,"{•@!gۢf/&*e̞pNtc)\0 +I\ JmAe 3a?aOP֝Iw1qG{CmG5FC#¹9wi}.o,TK{Zj}R_o_.Y82\}ƛ۳G5X~.ߙlz.ծQ nnJy N3R)SҢ1,H.۾בU WWCm=JMWCg9O-car_EI.7k(14=` un@S[V|%SXe,*CVUTCRF46@_/˷XV1,fV#6.MQdezre31;LR +,8nivf2Lʭ6)~[ۏCգJ߿=_9::3(w,;Y?g6OɆ6TdR˚_Z_ +jF!Ø,EoLvO~d2Y`7Fz`LKe@cLra?:=^=խ ܳdӚO);hquRE: ;-p0mȷa?w\w_kpc(90RH)cJb>t‘{f~;:טPFGeV/Z"_ +Z__Ymf}2Ev}_w>KsI"o93Rگ@!L+ͷW +Z~LՅŅJN@%g${K>t&vgOmq4xI@*R6x+tfYBLl|s{iЧ_7XHG|h3Sh6l6mѪ$s+^*rc[(>b-4-E|V0e;v"KJ٣9=𫪋FD_63ͅiIк h#< hOJ84T椳V;mM8ҽ22ڽ-s7ӌtzޱgfaX= ju%;;$3{9ݞ2py S@3y62cybs^141@q -bLW}Uvh Su\=F g;wpn܇:6^夎0>̉`/e +/][@O__;;+!R4vAYcZFHg$b@&Ju/˖E~ODnᎄJl`<1!ɂۋ/79x!nKu,R2D72T|3eY(MDe~w4 +sN5FymZ_̦Tw(}V!F/YF#z>oJk):N)/|^Sii1jmխ57qc#6ZoR_YHS *zu2`]#?F.i: !1#2K+ܩ_N2lY]4s^CrrjOgԀևiL+kޫ FVl;22sIU@gwݝIw{4wRNB-6/$;7QSs=Z/=8hH5D{e(7H_vхDDX~H[`ޠ*ȅL+@$]˜OXu"} +6+AoHX%\9WA1e=7[gH0="1 HA{4 nģ;?/V!W?7PU-:SWW Hߖh^ʨ d^] B +5dҖkts-JL@O PĈweح2!(gb @j#P<(č*y߀< W$3_""N\E}gU؝). UB{[QCf"tzx~2Ÿjy f#C%GJtK.l:XQnCq 0N[8#B*1CrW0 <~eb ̎V +tfڞhgI +ig #ZI CFz[yk1&FHG;mc_0g1M1 +1᠀ۜ31-M+ ͸ObT,z8w/Μ6WG?l(F?ì94cPb +S&xO ^cn܇?G 9?FsU +%B]g@nus>Ƹ@@FO ,Pv8V4^^CXDUAQL@Ș,GoD`?}׭Ȫ@;E .h?d +ēb|$vx9.Y@N=R Wb{f"O"6?/UƆv䵓?]9'>㟢v?IC"&v$`󛗭brY\郬~݃Jl9l+NwS5[mhh# Њ7_ ~8O$!ɾm)ɖzSVǧԖe[{@ȼO}Gjk6 o躒2 +C}li8-k $!OY[B.zS/q;R+!]`pT >ga0O4NdE#t|7gc?dj]G[٘oOO7C$ |r]Gz/. Ve/lx,0rO^Js7sv.J%m.=[jy߻N=φs 4hs I?oB&$s_F޸ %C2>"Pguѥ e3/:d۹)m'iWfSf}TÚ> ε7Bk׾rB=3\V .~IdC^ɔ˻ >]+a7nj8E"{0w~3ɟ~«3^O =ݡ2]h +IvcDUyX !j4E'ђ^yT&(_J쨐6qe/QzUhGyޣ>DsŴ&.پrԊO(]'6:\3z8?no=~fw*yk xjxk"T^ +rEWyFlmI]nQ/lMH6! "XD jYCn;Bh4ZXdp|r֖007ܨ[XUGFPjyʶ*u|w`˷<1"iW_{^%k29.!@ kwmn!VsZ*Y6\=$Ϗϰ0\(o.P9=۰?YdˏqiRKoJQ[-zjZca;4[:δkxCq1cͅ +n]FA[aڀFqu)Jgp˷vWVۏmticּ ~*"uBMafNA:e"Tx]s*[! %pRno֍ 楶L1J3mblsJ'fEZ^jJ`zGVo~1uR8RV[kErWvW,t +ۋ^2F2̟?R#/{0}㥹n- vwu|hSi'ԅ[̨6)QUwyݭ)T>>̢|B yL?|%<ǵ=F[5ܣbsdjDVtUﱶϠS IJټfgQwt*j갥.H-ۡJR}ch)Rl}.ܳ_SfNbOŖ}G|4;Q>;Z3gyϪuK[*KͼQsx׼8uk3Qr\wt~U6PẘmrrKY<]c)[Z=DG 0L^^x&}B^$S~kV50~@l%A8>C&b٥u_|򪻾نedO'RTIDSNr[j惶[|@n,o`%'Y@6s4rԮ8a{.[+춿疝nVz,7)WdgDJ0u*I^SA7'1c)3MtwwdíΌzn7U+),-ɸАP.Gv֎(!A؟uC;Z@{u}5*ęC{{ ۹1nlI"`9yw*.q^#q3(rTqtWMiZ^73=$lJ}F_|-#w!p[vFC{`j5R-ES9δ,.(} E]Z~M)M.}"3'S@.fD>C8>Cu"]Z, uprΡW钧q S< F'E?{UC7 k[|nGC_@usʷ<:_Sb{p񙴧p.ql+DkF^ +J9l-8v:26l,spzewPe a +endstream endobj 31 0 obj <>stream +(p`PH#nS-aZIu9 'Lɑ/ "GϚR۹[gk̭1Bݼye#9w08]eg(k7 ;lN"fU4υiSZF,5f!b&wPIn, EBb#N +ٓfpܠlTs5*2>hϖ^u <ׇ>GF:иEZLॴ tm~y۾fQs+i°6ZL/eUth%M>k w$Z31t97<z|t0Ri2媄aզX^2&'dL} .R mXw'\ jy/xFy+Gc}~֘#G+cGG2)n#&]JڀCwz9GP8dDR (T;Fwr\j\lv֏r&nBPVU}faCNNX/8цb>p}}6ЀPn@ZM5JhVI4fclla6% n`3Uj|tڕV .Ѭ i3I9OI??c[yd[j3Rq|Bn+VujP"lH1|(i\-@ GH=F>1rL6 SdLjڀW n qHƅĻzpwRXD!kg i].XF:ެӀ,+>hZ1nj PFꥭbLo:HCF2rPPPcTz?VY@T+#ԄMM< \#f/Xs_b{#* loUX3F6QŨbLc H`1> (ͨnO1DR[jpKݺz*og=zU%;Y/OIYC6n-:w~ ~%:.lc;X1)6M8'p%^>#n!SƉ1x㯥]PسYOgڹF[%4hklxf#q'8NyG q:p& z»!Q5S?4F;ə*[;&Yx fYo\ΈBRJmZ󟈉DOIpKDhLj߰a ʊ,%n@x` D {>WWd3Esc᭵)'Hji:i+7h?⯸t?‰WMLPew#'<^3@6j3 ߝ4"ZcU" t(w5`ѩqeCM?'X2ϗ"e,=?E63]oߥ$z,IԆA82~pP!i- MV0q5pTv"8Zlpk[꧊a"+)fQ1p~Nl5-qX s6֖ʇޓ]f;4X"لٞ4p_?.9ˆ&RVxn猀V2#axX}j ⑳+mG+S(Y޷L{3u듁|϶ +_>Q't7kҘObri|* ܺ9UlpFzJ2Gv@}-@knNv4WX=fHEr^ʕ좚c;KȧQZ;r*2M|ז\;z1~aa$c]aʠ)L~C4H~"|W9[MWOW __;JZ|4iq45ӖѬ{~6cjG_ۭrJBٷ0iHb +i7-%^nŘL=iV}=fؐ!RAgw}`wj؎NoEaIӬ-\Dh-;=}GtNKI7 6`x)C= mB|219&$Tbj.B_O aګ b.Ml&J,Ţ4:tgڊ :H<>5= PTY(k> b[4UVr3@-ӎ$;o.ª6½fp#xh\ p_ ++Ry6.zcpR rk*&)1U8Ƙf`h1G~`$:FV#(y`?.PԹaT2D'k\l-#)Nӻ]W=bs!@,c8qAI6FI15^<8\x{`GtEl^"X{7Q&S/wlǧuj9+1-`%ϣ5e=rUlgMD?lU&JmjOMh'O9qzTwsŊ1=o  ~K~^ߛmq 唓򀚭rۘĿ4ĉ!Te1}@UPR +щ_!u@џ%ҫn $1':NlLjf-^TJ[ղΛ${0cA*k*`R_H56jS3ƻ -ӋQ&v}. =нYf[$o垭lF<ڄ-q}U\N.Oz~SjiXlމ /z1c u#GQe֋6̬Y8ke`Ѯ7pgfU.?;FM-dʱw8&+Gڬ`Gx?o]|$~#E\ |ޭ)6 ?fCk7Q9~5~5'yxO@FmƷTZ5JX:]OP?"?/G?&߭L$]8odڕ2ȄD? 2QDL?֭@WAYsA f?Bn/P}1M+ݘ~"&7Q8W.ldY m902,-@ٓde?MjK@M g6J2mLqq+i˪$_.HI34$!^+wJmR\5,@J6P MV9C Ξ'!{fu)bx@]Yݪtn|lS/tmuztyWx7VbMT I&&GMd{| +|~fjj9s%]C B=?xn"v.]>ofNjGN\N[Tʡd4s:əVvώBһlhwm38zta>?.K̬z_)-LUO-W 27pKvvÃW Hxɓj(K9WW<<)7qoh%KED0Ǧ3^Mf?R˨̞C~JHCl'~_){bt&A&j`TtHm6{O·LX6O\&16zyPMu47~n mV_96~m]W:;#']+0 =noJ @8_@j7׌{Z݀nEm^a5UkW=iH q3ޡyN&׉5AMOI4_lpeTF:\ೖ +2֌?F?FMU>):4K2U͜V^&J9*Fe繐Nu$LmٷF}XL%F^kK<:MeE_6uUOag2Z-V"qc{z;&^^}U=Dm?~ iyKC\ɤTvAVIB`YacgbТ2ƞ| ^-xCvRD9XD՛Ġjidz nK7[FSX9\8_/. W%# 2T<ԵKSP&g$Q%{ӋN .75>OXXlv0aD?14=|6;kJj5JP񹲩VkTy.rEG{ σꃭn#--8N.~;MK E@|Iǧ~/^.jw7~J-<7{!5֑m}RGݶ +uACݻg# [}~O3*?sq}`NbHQ(Q9眳OIʮ`իz~eɉ rǍ@.Oʽ;kWkΩ(ؐE! k+&N41 +oI„Q 1Z$\OEl0+c/C3EƼJ3 СfI<#mO%{b7Plq55O!}|-OH"È(çJb EY؅Gk7W%(X50!/CIiGnf K5⴯(7;D5fgRN0<_`|E7fARAep#C*zQyІO#f +'gu(lʂtãҵ:JHd09xIH7~ @w`H%ؔފ8:׷4Z  ̢B7.dVn0(Mfb~vZ@b-g т 3  ȸd`@#2F';NrV8Ԏ~$g*}0hS!VNX\@G4ܢ@ 3PI=S!Vځ]PЁPfT +)/7tPXeUYGyrDM-(돷 ̷voHFbQ-?`DSy2n\=r3()8Ph{r|}+@]Ny@"gCJz6Zp;<ŬyIݧg72ܛ}v(ۺ[MD[ePu/!xxd?보Nɉ=F/䙾+&Ȯ"K9o"y*s9,0iNױIw*ILʉ75H }=`'q/Cu +>T"s{yUګ:8N}N/ +JH>QYdtɅV!\XOvhApF"Tgr[ڭ\4`߸zߘ\G>BYC*q^>.vȎ5Kb? +^ܛ]nu/kVEKͦw:nF@u`Q|@јN͓^nͧ˶dVJe9(̩݌ީa^ +*ShgCġvĉ,Jn+m7:ii2R-ŲͧL1(BxEۗfia@u[-qb=Ф?R6jO7O>+^OqA5v(nЎS`5,FxVR!dYOFA+a01Z#ܴfiq3.N12Eܩ8rƆ#B (]l&x_ς~ ~F`>Ա~ 0j >ճC-]aZ*CJYso+1;\άLLēQl\#Hqw%T-\.pvďpp-5B鄚i +1IcAxbc0Mw惂 uћWUWg0H/ԃ\::v;hAgKͼD?q!Gm=aNj>[Ax ؍nfL8 O7㘖7e]vTv¿Rv:*$LB,RHR/:pm{ +T&BI6ֱ5'@Q9nWY.ja©DF!MkUZ]>҂+V(Q#j\>Άi9NeuH! j0j*@UQw͸)\qɡA^*p(AOY}{e$VJFB7EAELM<\={i.|!XjC Z N*,k&fI ܴOֹpZil$n/ \CpNPY%Ja03|u)E˫KF0nRn؂.r]RmhFPS-دY'Ag hnQtq`"B J!m龱Xv?Ty5 =r"mwۈJܪY۝Нڱwp {ٝR3Sğo$p4Bk C ?L莨@*1 UHXH:pPRPchγ$adb$ +"%(;\LJrg7Eٝm}D;V`;te=T;vkkԃZ +3"kqG d`\G>ܸ@^t`(΍Bi3 +PFiERnڃ)gwl2H增8K>z>0:*+?Q.x8/8K+t=> Qw1!;}FcZS+xh'ȅ:D-*{v NhiH|n;.l ٱM_hw&C#lu J兿K8a5qE+bB;%uEUaݣ)"-H懽N!Xv4X|c)} `51:y+)nSPVeCZXd]Ր + p&1h,WW Iu : MfUӡ p$'1.a`#1yU'rYzD1Q0HUDW\Idb`M C +DxdP# X ThP%_9_%o&|-|6p>yve絾ef:=U:^IL\,d.5lBEQv\*8ŏ,Y92TR:Rs`Rgs 903EF}@ kFD-i"l"*N7U9iW梴e~M06# ](KGe:(>F9g*dڒy y&M΀'Y@ Ák mtCmCOvl2M+~ 1Jd0l{GIFgk S+fpQ@H3%F0h|ԭ<tKNOL g~4I؋q;EWAȏHOG/j W-,m ßY#Ca`@0@ +ea=`J4 t׹>cS0< a} RKoL߹99\ΛiQM̷4 ZM6P, $gq<; e,gT3HM1x{az ؍pj;< >hfaQ?IL,5/Uq9 oFnN Ȼ +} +(s +lY?0K{;ݤvͷJo԰v}1up:Z=##l9S.m[e5Q y^l}UU^;W3Xsd 檕dži7e|L'.N;Vj!=7Chy$}n lY S2c@qe(jȮ/#~k6tYG O(id=]xOn&͝S{TSH*yNB%  r*3Ҫ[TVw^U?*H9Em%λer; +ޗ + +qAA;2Si 2"uE]JDr#FT}Z ܮ\ +됭mc{UG@i}S_)/_)gg-VZQp{K1F7T.j YN֡h|ZXu]r=õ +QY6Vv( Q~t +*6 /tN)آ|uK˺8B v&F_跐`;n,k#+ x*)Itwқ&s=jl#Նh1:Ux!Jr@uw )aVdkwVgf/J!a;.FcL)E# K#n?9'䬳Vb;_t[mٷ(lmF1pu7hK5Qb$*5H :کSV)'`v;)oŨl~: VfPݮĖeRgOb3Ufڍ?k\гuSXbK< 4fWMe7=-o:"OST5aυ~4G񄛺ŝNO>iݙ5?-EPc=Ky 29$U?T\2a=a0ZuRnIy"}6D25;(L, +0Z3.X|RjaRvnkބ%owT^.&dt]HĞDW8lV+XV90Xt,ธ^Ѐ9q]0s;- ۸0["U/hhV(J-%XI13crǨm YHnlȊ2&> fa +mL %4?<P m`a!d نd#VN 081%}lp YH-Cs 2.z +V.vӑfã.]\&bJB. L:|"Ca y P(`0<; Bg˱ .;1Ñ{;=3,2/MeSv1>LD2z/c9&T@󗫅p]h: b"Q`V5֡󭔃sܑD +`r05fkk 8+t!=ѹu^{EVA~a(N "WdVTg rT1.b톲EFJpZ<.t [QDoE[ ڼzqFpJؾ\1 +ϲQZL]ˏT}h+-W5)(sSW9H'F/Q Uc/A*(C:!QJxf|9Z3]'y`@pm [;c67tȎk).~mkd^F@4.dϖwHcL@-P WA)${GB>fftvĄh[RWT@@j9@m I:\HdNt[x;Rȭͺ&DTf=. I|3XsxxʫRk6ub| -^l8$$I&M!@MwXxo_K\e{hxiʕGH%YLWYc2X J0~lKqtKd SF [(HţuR1{y ~D7vD%uD6hcm}$ _ف\@, Twu_Fٺ-˨4hUN'F)[FM *<ƾRR\+ӑ(@^Nw@77@ί-73F&5, ȴudFY|K* X$sإNumfqŵa:JqGNSwN +*^bU'~Y  he*d79ƢJ{P<T}jXe' @Yo*u*8?UC 6ׇB&XWpORoDwlJ'qߍ +MOy$Y5&rm" `Gs`pt3tq?wcF1HSލӢ`ol*a I(CpGPe?2I89<|^2ws;vWhZgr/vnKcGmR|D,wV,ĉ NP::7e祘IL1~fokt΀k 篥Me"bFpZ8~qAz5-~pfޗ13޻H(%7N{H{D?K﯉%~8X{nTJi_UtjrB &)+ ,m 2~mCS9m[$s @I2L[{hZIlt.=lT7J X}} +I]kLT1<ɂSk`6WQXb&=ږʂthMjH`͵L[uYA- #_vlY^^U,>`kx'oxIbD|G2+;)U;U:t9f^\_ts˿8'/ +W4M=zi tÀQX)! *L=5ƅ9+T vr+ؐxt<53BNk ^ vzs)Ez'+(%kYm&8FRfabr}v!7CUp67ڴKcTMRjШZp̚bg~kY6מyw?b/f~[D@Xw,tgF ~:` K«kSA_{{T2>Cvl!˯~OG};  4 +B*Pfa:|yJ}5zu̺{.u jemf iz4y3FDꚂY9׭`zr;*b9y'螪1~y!^dGQC_қs:D%z~N8l5z\30}Y ({+Peb2ooNqy.qQ@QAIHƪ.ʑd/sۊPCQfͼf1Bҿ4F+{_̡_JֶQa&5zt*Yf繙6MuB3TIC.(>m~TkɞtIqQ [_|}J.zw/MS{PW3S,Lz'9yaE0E wNǫ!?y)=uB #>X>t|j΍̊=\ec'Oxǽ="2N=yd!L4ɣRo4&~byAvzi$U;=7Z9L>Yjp5S[/lR/wfyf96gNf{7SxN@^|q)juW K&ݱMOvRDzL@n0*j(/龜8"DZS|?&3̍x,ZyUJhks8OvK/bn/j]V;bs94v'hU(DnA?&t\}A3nDu{/? {ϢG)=b^m9Ji#t?7>V^İLmD/{?-=S7@ƋrNWʮ:]A*)(]t6tz3 ݿ 64ìsT&LhD,L8K %^-#̡u(FܷVQPZ{UT O8=63}>`|Vq?>Up%o=]~DA&Q%\Șn&uvU6/z ÞU_ls3Ňzɪ]a|zq8 <[<"+dx_ezq֛o1o(7 k aK̶ZḿTa_\XCɟނ_9T[Y΀/7bᨭ1T 6?wZɇkSu9ݸ[ +a^VZ1/cSlSE@r#{zjQփނԆ҇|9X1jern/2~Wg#TP0_c0Qs4>;$ukp{#C5DAx R˰}bks%`@!Or5.3o}91|]~D.YnL{.fO23RQ xCbCH ~ >U脆^t )$(~gȎ)g^4{1؇Eՠܛ=" c|<%dc:RǥöPUyk[/<TI)4JH{8g?ǡmƅFvDk6MFq~U(EŴff c);xO]4N:J]j/!o~g>- +N`)ert=2>Wp{4eRěa7{M޿"yG~"8)Pp# +#*AW%dD")0˩gA"z`x֒gyh#~{RD~F$[cY&q܃>G BH"yG?s/5ܞ^ ` H.n/:}\ݫkpH<8KFǝ9zbM+W6xQaL~^ws}7 +e +WCi;CLf>"ԗ=?Ì1T܃EYiȡyLr>~16! Hh[QwIP',z|5;@tTxyJ}H=6L"$^YIbr=ߣ=u )>$E!7zksi[~wG=-2`ANuu_nuFC]k̬s'sFb ˷}&Pr>{ş kI72ưЎ4L~{4?nZ"4JMgtM1P| +a='O !aoN<]ēޙYF8e=x -oz˧=&}!W1?"JY-v_ULE]_)Ws< ^-x?3h+\ʿ*qp T^W]#垘L#˧մN Wʚ|=c}cvH^.({" .[#{ͧ+vsO?\yxj?r9sy)rݽzGܸArƖʮ{h]p%Lb:N;4t7Pz{ؘcGb~4JS5;@֡b;;r0n\aoUy9?8T!I9~ߔȀg"io8aEe9/$;6\C*qwHN*Mxh+eM3s 3g[PN I &p8;=~?'pgePUVBWkgըNzcvё+*@pm P˾I*p^q]sPoƺՔv%uLCGW5HNAYBzPԛC2}*b &O-N jĔ) RdZjyLv:|nӵi<폓zh6zP]##,<]kwJ<\ިz?6۽#YjR5Ǒ~oo6RiN!agFa7?iZ?@Gf/=F٫b@V֫#G9=lFXl}]sFʸY#B,hONASAfrt`m3W aDfLP4fzzLZ´V{4>wZo.Ps4nX~,N/8mIl2LƀO6{6){fuK3j~?aG`kiJ9`:)UQ~gV$.OMi.ؓ |s, C JcW>JrV>6`{QM9\jV%Y#gnXv1dS\s9õ~ .IaM}دUljs@-Ťt 2҅zP=K&2Qޝ̈[?w +fӜ"p tY/bo]S/S,-/n'(OGؔ;"L.cPa(.z`uPEX^Ȑf1AfYvz…x -RAFO2\T"A6 !R:z\`ޓNT9%g@@LƳdڽuY˶VMnoMuoh(ѪM֨DUu?75`Z" 0l r {?A \4Y\m9th77cpu[h0CΣIVv7-#ej׿o6'Nflm>lF؜A(]&2hP_ɮG|PJZ`Y5x5: +~ftnT9t1@6Ѿ-Ԅ@Zg{ZwPyP▽3}7ߺZ=E<Щv0ڱgba˗zN^WJyNH|kZ b tĎlܺ7:6AB1E6S2ZFt".G1:ݕ֢^'FVoO"!N.pU  p8WtА~U;52kCIDm2_k C7#u8x(8 M۫̑JV^=G~nǟoJ䫍 SiBV]˞6pL$؜hMx˟'1 3C]T +̠ v-""W> kw$ƈF e~q\3M`kج'J܄ƍ[;8n ~.;e7Y.G쓤Q [{& xG&n~4n[l݆Gz&w9~?ǺYN[Ѱ^Ws +u<' -6g((te=-8n sz2;|y ZOZLUC6G?"c0Ų:D5dhw}(X0ȞqO*J{6qKNuFoIcDKiK:CyYosn}}nϟL_vz7nu>Џ@G5hGQ%IMDMªÇt} jh +6S3_kF7@~׷mӤ{O]ǃԷN #Y {SqR&i[0i{߰#=Q,s8D||#Ua M ?*^8tWycfj/ UǪVxv?~NS "KDs$xu"Pd +§!2!~3w!:d`P) RT$:"?JCBd\b*ͧpS呭~S/sgsW}4#,&,-*OR)s!no )"J TzC[rРIVB5r|EI}y !16]2/"#/4GX  V)No8E)[l%|PTrL۵$`BSgp*uG |`Qv4yqG|l[LCsRWWI!d[W\POg{v~8=z듂+F4k}Z}&3`\띇=rmc񹸹E9d@kk񠪆Zz ϏWEc۾R NCݤ@OBgTyXmwo"uJt+nuָjvƀ]gֳLKS +55TB +T|ήX[+4HTf +[}I+(ۊ/2,@hȰ\huz8ҰXԂ12JmHÝjZSgx {bW] (k ߥ&p[#UGMVM4=cCQryT{1GH2`؃iJiMl]̃hko]"Um&gnWqtUs:Sn ƭMԟS==32 +'eΦ5Mus\lo.PJ%hJ@D +3Q\n(:C f}]X@208"f;M4:1^;xwnVON +T; LjDb`FGg!4==yMaFf7fQ8  7A NI)8pY;i냡'7܊" +DW@F,WHV¶Z|Z]h\T;4YzӇC55fǍ"!ef24yl%ldO)=Y5X`{Զ#( HV +bi_/荨 cq j;uTmsKmrq0 mCyפX;7 !]!ifKJyT=*f eF] \Zᚱj6H~_"Z-@9r'w='u-f|Vʞu%V>P->7:o_lټ9oimΛhCC.0eB5$zjwJ齝TW;P\V!g*z퀑:I +Pe59 +k5zkP }ٝ!Rlj4 +AN .n fE~ݓW[yqt=?*4BpM3WwrnnowNiym =KwCe傿{LFBon%Ύ?;IOVB7#HRol;ZNc/_s˦wh̪[>W 65X:VRvOGަ{(޲A#w{Mz*cOBdCsuX;oEzAj놙~3܊܃ʮYݴ9;b(EnnCc4[`9u`Rg:\I=HܳѨtݍ(}8tƭ-5G7^t }K^]0:g ֳSADuA +J|.CCbڈ6ERۆ9,EgXގl'%W #HW{2ְߦ?"$@=ZӂV$ m=@ZoP KwN" +F%Bpx_ PDkkkzO!krF?7bc0=?aF.к&ú>Th|]Hx1bXJ÷, jCw& I=dUD\|Hc-pp+=U߭66!~bҝ=4h3aYop}^b 'c| !L s~oXw$6d$\X{RyN|<#!Ҁ C=sw#j/-%>\~hzFETPB [uPsjloܔK?tv 0]# +1HGTq}6h܏{ꤚMpK$&WtNAB [ƀ'n&!_5!#39`r2D|-0Stc0#qZ '` @IqS:蛖!h>٪-*y Ofi4L)c4| K9t|b?zZ2@#u\h ~ᛁ?ٵ8"qFv` $] #].< l-nrն{4a`&i"Hb-(6l=ϱB&#軡>)QWQOUy7bfGN˵I4Jd<{uÉ1<Ĝ>z?j#8EhUdmp<<,w1~K?qsv|w  9}kր HE +(鐦mu_wk֠YIbܧ;R@ +g1*@N9ukEc0Bn$m}Ak{6ȕhնZv.`绱/.pQQS 5gѳ@kӡp\ Z/K)EsM9 څmpc +B?Xʰ\o} }|qQbǃngdDڨp-M$t*tΔ_%m!\+\{^yZU2Iq+ni+僣Xgj5;Gɍ8PS +Wl?ݵ]dsV>̥(\@Q3qrВuAOǰܴxy#G=Ӧ1wgR*fp03E"US2pׯԛF:jp{KT{ͮl9`v =zD{4ބNEr`h~y2ۃn?ac4Jln1C`nfhX '.L+N<R4ngx}c UpB%T.svTN.t:?LE$3O#L=9FG:["eՄʜДf + MQ{,0ܞ}N +@*^xPOMJЃ}L,1f$īخZof${;Wfea`^/,8h'%?z)Uk?Q-C*)akۃf@R-ۋbTcݚ8 +"V\saJ+x=["Tm4x'͵DМR݊4;@9JDҹД*6ayzMDaQOyT3䅨¾)J%k-nQ#vmaX2O!Ul.'A-j3쎈Ru϶bTnV< +!{JSK11kR!FprmU%!ZARI!D kᒈwZOCBTHwr֌"Ls&. +wi)@1Q)jJ:!aC'$D8T"q@:kkJ$o*ˆI\Z&κg)a"? $:8&cK+>JENƩDB(tx+FȖUH$p**ũWjNŨ.==j) RmDjUyT(VagڭH{AזJV1@s +p9@ڙsfiu@OS/KQ85* Q*a\Ðnvʦg$>pz^-O.;rKcW+(.p8T BqWl@52AT6F}5yT4)(,L"j3mZ|ȭ +>ð^hy*Σo-#eM9wEP["noS=a.3>LW$m\ſ=Qt[! u ٤s'm*tNe NDufz$t6x8" +NĿH|kR&@6$mvQM`ZYe͋*7E#-45p6Zſ=XRL3=Du o{ڠ2ebӡLx68IPݣ(@YuңQ5^]ǽ;L:`υTȫM x=`$iL;Pmc}ޛYyx'u2hٶ(A{$gTiƓ \V q Qe v7MҔ0jJ*zqh.ah +h.[fSv>YT{{SC¡jAWU]z0 D"T Q5[hsm B @ѨQMNR ɧYqPy(͡qfTn !n5ǶyZڅvu#Ѽkfi:y}S4 + ׁA ⱞ4.+ &h`IGÕBbÂ{rxԘC4 {6˾04X?W2QQc'@Nr\$xo 5nvziuq"8r.\ք': 3٤=ޣ_&qM{33$r1m 1Ӆrp<'FH@Qxr'`>8Ly"=a!X z@V.٫,iߠ|Ds1ԭfQ ։Y۾p&-<#(ZPQ&pV-RT.kJ%à2Bg*I*ʗڳҰbzoMZ-TGV'z%Q0g1A}(ͷXwK!oӋWV% ]4tŠc҈2ʹ>4ˊLVjjfVh(ai4gs "BL{oيq ػ9vvGZ ?]A:{i "JF!oMeΜU Tɜ̫ *: k;s8*fŐ[xAZ'5!LT^Uh&eUҨ5;")Ftw$j D4z]f:_@( +ĜZHi6jo=-xoVb[1 +L3j@OemXݪ-;XM>IJ e/kv)(m' /8ncN(8v +z1/7Itqd)PȎg/ݐd׍m՚9䀌yJ13'9J;yL3m!2O]TpR$| '6;_ /So"FCSwI踹RI g'2uҖtX36処{ujJ;`! ^Dg3w33aÃ縹q,O:,^=O:o{!O:o|:t. t>\%s\K:t.lr|ҹ|ܰsxԫ%{ ]™{p08f+|y`}W'޲6Zt)Ma%xF rgkcvXcJlq٨%PyopQ5/ʋl$>-ܪ(dƶPv  K'-Z:Sc9a:rim!CXnl;K̪|tv]Vχ +$2B!+bvraˎ=J V9-0/`FAJ`7eʫ6dS2&73ocιḳvy[b-' s֛R0.%AI zBQQ̺cV(zaU|IcÕ]|&R&.Y"yb*h#9P)2|64 \3$JaYb;QY4Ia=I9GO5mMˍdM[VTlu)#GZȭ+I6$ܺdskI t6 +edq;w5'B ~i67aMvhzDu%m,qIy+)uηWL=מ'~XRzrIy4 Sݿ~9sCg-y$`߀tL2G vde>0 #3YQ0Rb\lub ^"i T'PW#G-wQQB﷔3 W. ں!ЅA9atWcUeEt~ȴ;d_Nur +'3yĜ8ܺj biwEdVN*4%JuŨ66~@Ev}ۜJH~aiLU] +|(GDĩU佹,'7SꮳS6U[{ + RTJQpތ2hs>ڦ^QϑR~<-ҾP>gF btijtD(ލʭ.eh=K8gI9WFص\4"/%\ՎGtz=!Ř<w- ɏĜgoӈes[zSU]vdf[( /]A~=/#bS9{@5%GkLWW$ Y ᱾>!ǟ>!W5>H8f=} WI( +\WbPVߜHey(w:k깎>*ZbPVh}Sۛs~ںzXWbPVVʉ}BY}YkbPf}5% %YtbPQo`^4o}L,.ĴĘ> ٙ^d$ZgbPV^CbD:R&>^*kKk{'>Z%q{ɶP/1:47r\ \h3(PxO; +yK'I'r̽O.ubFa|mCpvvIosFxLݜV &^;:!/o|":DhfĨuc +Z0B0d$|)(P ̳odym\޿ ~K'SW:t: +Ҵ!R662o/Gfou+)2-lR~flߜ!v쫇U֢pWl_Pkш.^uճ}A)g-}oԦBvT|wQ~yTK;$R5J2*ÙYtN1ع9rWRTH~&D!Y*b)e QMj4:,]J,GN"3o—dQer PA7#hrX>x"p.kDRL,Ѿ1fkbTBT7P?b2j.ڊoRU\m3b!ܣ=^9G+:%tҐ 3?n)rP!eZ=ғ\-RYR`_Sdy;J3Uu,$>+:;ͽ1+ +U<ߛ*Xu.&p)* +-%:IPvL BeXuℑl[P#2q Y((%)rܔDFr) MIdGdBKIoVHIJ)rYJ.K$GKI"ŷS̶Kekޙ +yzNҗr wBa/+ʴwB4Uh̵_~Yn +gBv;xY\ujIdujҷmG]x(!T.p*NSD/X3#"R&I"\b8ь D@i#RoDVٻ% w"Jgc46ʑG\kĵ Sr8Qh8q/Gۃ؟PǮTdflogiNoh왭Suʵt\p7Z&)QƚWa#wE xzŰ]2O|WuTh ^Qv]pYhq}nn%"Ev|85Pn4I֪Nkzt[!l͓e:<]; P.ڄk1CSLy~QI.P[07;Gz^r/#7:a o}˕!$3*RQ_]A5d +`_oIcFJ.bJt^-Ӑ`:m`o /p6չx@]5Ǫ'i=:W} ~eA֙//ʎ&Ŕ` <-?M4e @v cP3ؤ ANs55}WZ/5wE҄Zx{1eT" > k4jp}0Ά3#<O1 *WcqޏK$ձߦZ϶{lͶP0BBP\NۋszL1Èۋ/^&p)wZY +_85[¼ ^ S-q.M`\j_5 ;IThmHs iߔQaa}QVX*D>yإZӹ5ط4=ro#¨hEl驠} q70T SSWUa>GڞIuFq6Q{YId׉q^e Q WNME >:de}dluֹh5-F0( jOyC~d@xjWK<jYUr Zd'Ef[:u` K  >_&ьj\ \5Lі2@( *KG3f7_Ed)<;INl +rr 6uHR,}F3,| &jDcf_V֓U΂e|g)vݫT2FVH>jb%p ҍmO&d8I#"Oj['c5 ]9&4QɀP]`S`qu%Tdg'5s74}c|a6ń]fՈLaւ@$Lrg@RݠE=h +LS&(= $>J ^t0@UM3e7%Uښ%G$fP=W¥҉@{/#hЯ2PMe\Gp0[hht]`2lNцTݧ2,]>A[ v?CsdBe9'Gs9<s;]i;KVidъ#-a5R 4>bU<҈Z . 1#Mb˘iH!#M:4#hb$D!YFF%[ +Len~:H-:buޕGb @A2I:ML1ؐq +wHeF.Y ZtjjCЯ(:Muau ۃM~;^d!P/^Xzb[o.7w~x81/^ŤvdFI`cҲ=ukȫCj4,ŝoJ6pcDLhW~rmV!瀊 ]nZ=k(?N7Wb PA;=deś" u:V5z :o]Zk\2Ҩ|Jd2Gk1Vj%%*4, Zb{<ҏ};Ɓu)A7@>S F?|cѲ6/ Q&AzDyI]8P `f(Jw'487.9:p.r8GZϕt*C3(g#HMo#E`_\EpK+tHC܀m|ӌ-jp/mbFε4)qP(}O-ke!u0~qBљ3{9Px휉Іd0i=ܤ*REVh"b>ܦSuvh띍՚4 >av:; ;ZV8ϵ A 4CS[ kKO!=0%߶이3@u."B0WG">潷͚NOM9d1 S^Oƫ [7z)y=l0!%Z!;ܼɸ.C,v%0wPDV@I$RmQa3%-#h,2CK-L|کCYш!hHjPx$'W):@nd; ƙ<3= 4 3i]=e3rSm1JRXoUѕq!7'Sфmߌ6{h󶎉,,L2&2b{~7d y>0N?20@R0Cir*Cg)\a "qFn +A_. S/QC65,&\-5 .9(>F惁sƵLP +A24Eȣ$sܠ]ms7n//<-+ʀi3eA$* G;׌@}/eAҐ of9,>[H(ڞhvXex}N}Jrsw>ܢ(O b':VVsm1GIvۢpRdUV>nAlZS3IM+Ԍ~aE.N:6M6J֍gG;o"#ӃWc.;o-O_wKo_~_X( +o ˕_Gcqp䙏?x;oGPg w`c``?O䥯?g~/>Tvv^{GwxA=z``c000 ih{=>qm_'?>ƺh~7GP HKu:B7(zg_'yݦ3~?<:a``<@>/_}_|k~?=OǀW:=?[!?ǺewO=gʗFX``۠c:t000ޓ@{7z󟹂ccM@>ZY wG?;YqW wd>ڷ"<?b2?000^^,(pƚ Gl```~w0WQ `Oȶo~݄G4no|Vp~+?~^w20000V?00 u g0O_F_|ثb,L8LF}q3_or#h ;wK#e@=DO g~/k?BkdX Ožn7^  i``۰E x͗24?0000000=|/~Q{1u_\Zx$Xi_I:?ᗟW|_'~9=Y``[?î100000R`3gO^$zw»!fOo}}xO(~+QşŒ}6ȿ~Du)}_5WXgTOTgX_iy'`\̅y?khsu;`w?S^gkV)UFQjTNHZ5FV[gbUI"# 6I JbeEp~9=כޛ{x>{99;fa)+6U ! `LC@@,mS>'ѣ`ltf8wX`~<8|_"[֭^23zFe,5f@_õۻ0-ɧ}o)h;olDL؛&qs5>9n4 +\K 1 ϋ8t-r  ($#K[hȧm9v)+fB}6{\z\/@mY5˭F>?$"Z@ WD!Cށ-C_F ? + L>1jT%=E,btb2oܘOL/o?6]JB̑잁};lp{v~t%0! `C@@,m([F-߄v3$h@35cÊeU:k_9lՕ|>㖲|! `dC@@,mC@7 2*cqL{ Zz#;yѣptC@@@ @!YoҰ;Un(YoxM֜=+9qUiꗇu|ob{LK?-bg(_&+BӪnTmjpBוҒveӖt;C@@@_ÐWa ?X\;=\`S4 +M +YC2"<S)Q#?{7A{9o/v~{5ɂo'/E,Zmz-agHٳM5cr(Ƭ_S1)aH=s20j5`}Giʙ|LTC@?{ym:~>`  88 a j ./p93͇wQcW#/:|)9~=o?y?x:sS)ؾ;"8 0JLѤO&&*f){lȍ+5{nS>A dIC8mrq`|nYSW-@7\MowVDa_`|B, M@q?~\mml^W.x/6'b jq_ ycx30~CC@@@-Pbt>+ec?&ؓU& `ln&ԅlzQɑvwK+ ]>O;vࠒo:nm{N,QB/ȭwbz!}]>?y?psVgZ#]>d+ؑs@7ѿg3M ЏvAo| ,_п0ZhxX xG_ W2p?д?#va۫ N=xs .RwR? `~wA6'Q@,EܰrD]cy +s^{J{p`w l߸h{JGdŬWc9 j0|D,<\BC YXGghG}@ٞ 畕]:|96oO IAmr{z ;,')_=i]bN(ǟqXٟSQ{Hе-F=as}zz3w>[Ugߦ- Sl?uOF}1nlMӁ#;, ܅\o#?ߧû}! ! `8\vG9vǸy9wٚsf++gg2_{N>)?? UAj|o/^[F!l>BmU#bYHy^g߻v515f޸1p{wQt@#4$ҘJ!-iN! py1'ys:Oqay^ArS_G=P~0Sjrw/+`t#4ru| ! `)`=t|8-?d}! +?r2qHmˢτX3Yv:VJ-}{BPv.]뼕 ! ƿK9B`o {g_ +zcZak쇼ncނ! `u#V.I0Ct_?8G89 {E&6oo)Otˁ|=w}i- և/g_KK?y?/iʙgC@@7 +;n~8Y[ȓ*~qV +ÇW9t+gEdab 8L#;!=\%xFXޠoƂ! `3k,t3~( C@@B?B;/~P>ϼ=;3B |/r[.s~>2a yB _}N ;Jw2 +H< S7_5˭#|G!砛|+ ם$A/neՀ|: /sm@^a[n.GC^{,4߷;!S=~~4C9aU)Cl<ǏߧR X#CJ~^v }79|C^E,C / +W2oܘO1ݕi]? !QCxw }MbW@|̀A%?9DC6.lmxr?}l{(NYʗ-3~ScHYvo^Be{6'Xh1L=M>W؃C@@@pg,6*Pw%V2$ꀾ M-˝Ӷy~C^=ohmw1~Ax 0EЏ>Ti궭}~SG =.?Cȭ9ҙ>bDEL_S&$b@Ap J_yyᠼ"v8..O.kh4p<`-_FX!מ'6΂~n%>C#^xG@֑9g8|"V2C`w"_u +'pb6`` )M6d[]i"m#yўMu#! `dC% {y/ԗtmF;dG;|6i%4jh++q9:ec~xoVmx.-p m(F +"#~1@(Lj}Gi`hr˗]{Ti3~ p@B? 1nlM=M}{A ȴO}A\5˭cAC~q.~-y{j~vثrR^88)9؃@r53]){4thYp ?F|_Ycy|_KL +_Vg|٫k&CCƼŹ;y.Tіu'ߕَh_<v_۴ѣ ]?02ϪºyvEWuiS= ^IrD}l6w}_eMs'jbK|}_I .7+m |M,:\LחkVͅ Ú!'֖h/C↨NU؟ ! `Ȗ޻ MO|ƹ ts;_ŽLKDo>αomv1g9^cUe1ֿ]adl,mBF ʦ?+! `0~/};l0P!'hb(?Њ|YY?πck! B.l szr'9($/!P,FW q@??TiOw{R0!#GJ:aϺ a,C#GAWѣ@K?YC@@@Q#~#F(L8 F=Pr{e5C@@@Q>ѿog} +<?0r:™,&GƉœ'5$4$0"C#Ga}/'~TYRȷ%%A,,jn)ר)լҪ˕_@]Qgm(2 5ک҈ x\ QCƄ+?xum_h#{.'pA+?hпb@Z*^֘ΝL%2DA۟=ʁ ֳ~};tڗˮCC߶}_e0NX-zo! 8z v_;HV_p^ }ѸjpY\~W=%!k¿JFX#KsˡCGi}gَatӆnUxc7\ml^>}"}yƀ74{ڠwl +jp_1m]ҕ_^Ϋί u2Voi:cPQ_ZE}sM=u}mmO~Wvl\`{y+ʷ{J_J߹}%q6P90,Ѹ!zޱgojX6R +bt=evΔ[<1(C_!7Epq}:xk [.)y 4 Zhپ~M#ay>c}˴wCq6Y5ճm- .);xx6ueYֱcR%pFbx(4/6<q ?և  +_Im|^^nx)c⧮?׶8ac{xšlkɮpK4s I.[^ A'V9 ]~m9̆W2ˇ;|"#jwVHH4i0?EZŐ/m\Բ7q<ʋ,ΐO.Qo;[Ru}6Hrori/t"D~OQ"bѿ6$D"wHϥ5 eNW!EIey.c^Qd‰,'.) OJ|DX1zmYzRGd{d;Cq.6-E#ZǤXg3&)ӊ|1a.smNOꥬ#3brMs0?Je*HZLD{UǨ:~V˯oڏǎ>WM7ߓز5F!o9;>ߏt8Q+<]=6vVϷ]Ӵ̏ڤ:TmYG7xxlUk98%gc5$FN,>1100'Db O?EY>kaR?C]4uFh WYx' ] :#娋7 csN9fѷc5&Bv|sջOQx=Vhgߡkxwqx4 WG#uAVDc"Pѯ)H.8&ρ^ͻDs,3 +~.Mg#sNU it])847ܪg=m:(bcˆ:8bCCN ƀ#D`6}7ݵN3}ܶh/{rI괾ugyyR˺ZM>uh|D${RsM9 R&"M}:~ )"K䃭Cio"Ŷ%U[LK>I=izs,Cސ๟dȦr1mH֤g/HYБ3}5}t+if;I&u^r08*tN&d+u>+Hl;R8,zAUG;䮘/r,K#3ivE ȕL8rm &K[*؜:&Na~.+ע6~6\Oz"=]LytV.eyskΏOlO9?>?.ّ᫶`=zd l;29jߊ^DC)6?Ҽ޴zbcc#tLc.XlfJ;g#OmuUOMfwۡbΕ(~xn֑MGӵ }9Gw9O{ss +tM],V$(y|z ?p~ >=A~GNNd&?vuH`j/9 xO².fǽ7XDagj76%TYD´˩ȗ*1GjOHRμ28!'^hrY=J3g?'.4$e/~z__N|6)w~2'23}V22:|8^ϔ[3#.G>zc*K?7 W4JJԤ1>>MRJuM>gCU}DJ1cr+.G?[ >G@#ٰJ~dQdjT@>fr + ?*Ό`:x}o7MOn]X?NsLNj{\0% +-O9]Qq)w1ϐ>zS / ?q`0{˅.ڌkD}eK3.fWGA({6^=hqH/#L8:3vgQZGZ@3ЦiI6Lڤ'9 yRnqGxG)|=A~l9xw`.9(4zs =T=$z0D +O[A=9=Lh+>^<m:u`նK)y[ؾJK:z4_?d&fhH]crA&C;džsn.Q;^ b2qȐw& Z'|$ ?sm[|b$%֧9FSGbRC;=cC#V3IcN=&{}ۧt44wGl 'm"Eh:td$2dd;S=!CM$RR\Ҙ*cr~g{)]z)퓈t LlJ*P{Qp<q:CM9H@@Rl>-);xX3Rz9H^=g:\O)'u<3)i5YGLޡuer~ B:0us:K)fdD\G9O76):ylj^ [W5(C)ە˳،˛ /4T=#iyoP-i-%[X?ICTꘃ+Ϗ(y~NM8ƺ9Gsezcn\/z:չn8 M>\f:|:6G+th! k;8:=='l#Oy?G?{쯵nwJ 7u|= +d||cp- 0Yq%[г'ixy)>clnY'ҽs޷:=5/Dz~G/+{xD3.l;!)r*)z꣬8];RHWs3ڃo,Zf0ҺOb**y!^lK>:_.^X.,-;oWidZe뤙5Sf6pIqkG#}2ezޏ-jtI"$iVX_Y>tIqk2 gqnb~K?^=m$=9#o͎:O1%{r՞̛&3ONc(d~ @lo{2dU}EB*y8ʋ|/{:3ilW\~co\5^@d1ymil( r;w~)q!]-֙:'ŽkؿOqs0?0,ccc]=f zb0^9/k񈃱K:.^gmkգ$oD12,ցDr myHt܈m#GLZ3n!#4ec}n]R:%m%ޖd2woWϙmI?k#q; Уg69Գ;{\ӃqЂH6"RzDٟ|<{a矧܏:}OI ;.681)z*Lnzz/]4XukDL E,k2rFC>LZ#Ktb Z.w)]o5yQGŭR5wN]Bk᲌n-ɳe g:< +[vIll7#{k#om *큵<@Jb4vp1~.Um y6wI=<Ċ\eOСvEMl‚B~q;a +jvzLo\![1e@sO.!%8c c(9lk|s=cbGɘO\fe2wd#e_^eZ&q}R֡i74}j"#Ξe{v輽N%&g|Ex@`0Pyv4gAKfSG^ HM/W>7rC`+gG5u>^(ϖ8ZK >Xs\ge"C,'8 dV%?vٍlǏdk~rwQN)7bljrfOoXmG>Rl7pͱk~@v6}&cօ\gz&^IU< t~`잘qif=6:JvOuEj[ooİV/Q3m63,{z~ qXm\ROٚ_=RXro@O|mo2/:YC?F{'{ ؚHZm"Mt$O񹇎a'LBa'uBJf|7q3yF~B$pZDZ58{]H/Hp֜f+djZh@fmjSsMK8Ѕn)Yz}ighuO9M <%unKӇZu~9# c߱~y bvHnX?N3RNt\Ӯc)O}B;`+}by,ao[78؞{W ĆLp;*3ɑz!p\4i:~M@(>߰22ك?%[~"?|)~ûmScI}Bφ,ミ.8 >1ql|s*۫z1ͻ phօr;P_B6l!3YbIh[䶆P}~fw]Sp]YspWЏMI-˶'WssIS*c`&9{XL $#n@ Âzg%+&?d fvЍtl pm 9dF?&7}9s wѴ2'oe>?o 8H2iљ1;q'm[~HuB]QO}q?phZΤB.,fk7^n5ܸb ]c߂ד)戺mơ9Zq8CmFvF*uKş͏8y_>B_>l>g}K}\,}%x1w?q;E9]ܛ$mn{5ˤ-W1McҬЗ:dѤ |{Ҟ6RCT]yrw\  Py.쵇\τZxfTsi|/Rr;sR曀=(8vBy{%w+RĹ2'槩1ƲC'DF&1 ru]uN;ɯm~xRlNYri.O ?mE Lƴ}xV߳Jё`.n*e^O1aD7|3q侮p#R,} $fo ;  *߀< }`J9;)i5Vܡqyww{ Rsm94|;*_]G@:WٺeGsIM?<'>2,@g{lPM;zkǼѢyU2KE:7?:!vCɋn] q@Ko0gzZ1PBTastw mB3yڏړo qtVU.syY*CWJ9NQ~%pA/1yl#R&g!Vn=X~/_>1-XbypU>v{7]9~pmHQeǞ:@=K+DkNd8^hƙ٭qI(pv˳xfRj~v|o'C΃w|E>ؑԀ#znsbjCH7kH2:7buh2M3>!iI8ayn ya~He~=3#usұNjEyѶԼ59)sz1:a;6 +cs6W^hO*ؐgSn4g߸BlK/X=FNq9͌݉>@_蛋y$iosP{:R;{VdNk;:>Y./du{k:8<]鲆3{<'8ܐkX7ݎۃpRB p]#,'gˆd%g~v/ak1[ׂD[W(\=؍r|3˽+(+t_)^!=E$6?_-ւro$E&1T>< D\'-b 7G'x K {[hm`['p]aDɓ']a {ohnf83A:,jmu4}~ƶqB:_m-Y8g"-˵}#Z vl *j0?B,?!*(m戮#D=Ž Pߖ *|}J+=jώMJpa+, |q+x\dz~ O8C<)3R"}dhQTK3|T:5a79ZԾd GW~fL kHkJ$5L|K|&n*?25ϣ-uQ-r[皒SJF6[RŕXy>t|LE^PCAMf&NwY6sx͓PRpUR` "Eݮg"u<uyie}<[n$IC)<.19҈^w[Yz$C_W9TN'[m,[~︥n-}&w[<n|5˧nDBY<񶃃}vjEuS?vJ%)\'VWC7w)?Vw$y/,!y/V/ k[ZhBu:^8u:TF&)nlꧬ)֡*C$޶8ѧxϳ0m̶VޓxW;߆ћ +endstream endobj 32 0 obj <>stream +%AI12_CompressedDataxkɕ%͐\h #3SzA%ih *q" ,4_\3]Hmm]"#^zoϞ|u98Y/}v嫯~7rX?s]Oxw_~W߾~\o{>G<}\F.o{ wByy 6gv4eҳ5FWo^dJO3aR%fLqDe|fo{իwwo_ck}ճz,ڽ'{d+P/߾b-fzoo_~c\# ~1f;_,RĄ O`ag?/9L7$?pq^~C#.M޾|/wٯ^}/|ُ +wAϞ}7o~C#C? O_ts==͵v:\t7Ӎ7߄xn|susyz;ݚ[{ ~omM_x*/C +9*5FL?h>c9*5V6>MS2&| +)rU:kmzl.6§Ou +׌+JӀr,.kz:ssz|{xu + 4&Ɉp>Y8v+qoo )7i1]xw~'>>m$3GfӔypDk7rƛ`@%&l̕9Xsu7OhJcgAUcAlÂ9X lRK?^^qsuޕOB<` FBBvWrI!?żAF.\^ W++5jNn)dž^I^d] -n p +ܘL8&]1l>ۓ- &G +5cn=볧{]LBr bmoO\y>p}ĻoR{52//>PYI7g ܌)RmV{wc0`&wH1%sϣd9E}7w_h}el |+rr ~ d+W“N"}u$|5˕r|\xM"o|{Zx `z?\^I(Wʅ'4`5j` #M )nԮ W@n.?A]c2\2B$1bIg ıd-D5 +;CGtik!'.kn7@ @sB`1 +3PHR.X üS!ڥ0!lwqC[mw)m|vb8B1NJr,;z!p|euv\N]vp?xg0Y.}j6 +ipq. +s>. +3]/>?ʟGĝ_ض^+!<]u\uZ.³x͋?'yUONt1c5#W3}q7q^i^І7Tb+?'+g}|^^b$b(-W$Frxz%r]WCM5A@7(fU\u]13tj\ ̈́5c1dFL<3[.%:Yqq\[g7VuPĜl,#,R'$_H֪>?/o \' 1Ӂ: 8D1s4Y2M{3VVXM|nդ霷㜯x:ͧrmnndk=O'Ê+Ӯ]u6]q58<> \աW\zWűIE!+>'N(`=ouu?TE"`UqxqqFq &q fq asq.Ƹp^RZe{f5MA{cbWNS7E ]QxqS{,jvcZb`+ +n88`.zU +_I]yxͣ Olz}=w=tL\7W#@>Sb27=@U "{f/??jֶЎ{B2=Bzo igs\ϧ'ѭUzIGۈcǝy|87r6/9ɗ9yx_G{RPO9rrɅ'>Yb?$|$5:}kN{@gLef..~(Y:Z8ҍp%^O7MŸpAM§*_ŕDyCi{UC=GRPS% ʼnmu'=z=a6ӝz4;yW:w+|_Tc*bB_##l=ԣ;%k p?G-0&YCK:Wꃕ8V#}n[iy^4o <1&N BpߒN$2 ,@X<4 ,$E*DŽr"ċoE.{z@XP¤fv'衧f23[Ap+^' 3u+ƽIJ-J܅ϖ%y)A8-%<\\p^r]mz%sh̍pĝ_xbaWbѮOO\۠¡ G£r))6|VDND$R BCE_`MG|TkcU?V>8x+v lm[ HZg~w`V ;Uw- ~OS兦F]Xcr*>0EyZ8?۵e9O׫ $3nmS׹%e`ZM O|c.ݹʦ0ffVYsYg൫ -!\]\i%?fR]BDre,԰sŦ,9ƺsU<5?fY>jȾDi(^/V !/ch{m7t$$r|X:3k\a5r%21?ԅ!Ai m?jogss:z$G5[`<6Y4Yٍ*v׽VҚpzz:%q|-$%i'W^ipY]?W봿(ޟ L t?Hid۽kgZ\fk[*.Iq䵤ό^2zQjWgk)fؗ(($4'+Û[b&7BZsÒh7I-%jIulɎ-qMxG+!5qM}ܗ](".mk)g (I"iUbh 8F~\Sw#$K.?Ny 1q5}0̠PRibikT+s=R*R4{\0eW fۿuPos0 6Vӆ&f3"i@{ӑpT-`g8ڄxk<ښ i\VC6LְT A5kd,Sᬘd%,')T\S4@ƛZ8ҜǺP 5Z.NhvA!Jԃ'>.kcDm}]s{l;WaP./l-e\rwM1IFj%N(I[7g g`8EVxչ\\xW_Ce5EV'h]ԊGuGy3|z6Z9]`7RAb35I\"05v.DX nvM2BJlo[>,,Nkk +^^y"uo03 j搩 ha;RDuY ?JD,tuč3wշTU IӫlTf3wĨ]'r 1O=s\xߟU?1ӔOH8!e01]I\vBe vILK +fIYY*ʉ+{̙=?zR~>XK2ZÔ_)W +шZ; 8=-m`R-s$bfjԒi^=shD.$y4_1j5/B61Ic} U1jM6R@$9ݸ*U_-<KSѧ,KOYD?,GE)Sѧ,OYD?" +񘼉s}#N`vqlvrs溽R6NfQֈz2 \-]O;&1',seq64ˁ߲-EVf͠I-UXBe + [(,sey1mecېA@XDc_!b>j`*iK )KIVў~. 4 ihaHBA؟ghjGC[&p$hGE |2Oܛ12h͚lAFǨR(Ӭq-Mv4(BýP06jP;xq7<k"_7}?ye>2iF~N= +>iش8M=h{XpIZn݃pi[ؚ$|37uy /fySFhu3Z$){AQOVu}z'~zĝ e66]FgrVWX>w> 6^<]b?|M0dnsaق3[xf ̋ \5 5oӶ?}} ϖ m +g/<1vߗ\Q'x}_Z߄,x}ڇ矧WG1_ZXl?oxۃ;lx[φ+^??zw/߼~oaӛ7?ۯ^{ {7/_=݋w?>3?%e-Rت=Y&gc>8&ut!yC!-86*Jy qq#; {p]ʃɟDqi8 :W;3g n;2ۃ1Y vf(2}Ɠ %ȸ`žV <`w1|w?u3osTqn<Ęr c&3O]dc;sL w٣ſfy@tL(ЩMG`(11a]p?REy)c1N1cd׍pTK Pq,c"SGuI-aRbRTX o.:3TV1܁yDD1+/Sb"غz/GFH< Hi"f ["ܤ yl@Յj}+'P"$& gV{y4Fb, s=)' +1uй + ϣc )F7$YIX&PN׃a2=C0:0 sLd0N`V3NI3h.bY)Bљ N'8pLqtDAf L8M)Qc 88qP:i0Ma#y>- g30)TL لSY KMg1[t>`Tx$5UoA gp&hRq8/iq*戏˫lq  +: |Z? cN!8m 0v; +癛}{~!.0Z7,;)hД$N-s$SPǚc1@,^ +gyowŢA `y&e '湊*قT{̞_O rr 8qa-M3Pɔ7PpθĶz:袚 NnUx<6J芢ʓB'A@P 'bU#G?2Ik(kbC4FF4 kH׌{b A/;L?$ vNw?4bO@ 2~hvL-L7b|u3 |6ד @\c7?~p/޼*~+?D_=޾߼/?yMl 3 w _G9GBmiG?>l5}MPn`) O)%X"P.8l2I.ph WDs1"xWV~?l݁LTBg~|=qPX G?yu^!,x- R2pՠWW% p}s|N^A@@4DX$+؁:@-|{Kt9QxV"aɂ阭`g(gb ȵ*3`GGCY u v G_O^]otX40X< }^?S#"wd& e+h *{,v_AaæD 3T\H/$iJ:H 2im8`>"nB6eaFq? ǁ D(=rFK[4p8g3dsjTfTQ)77r8~=q!xv L:024Й'˂beͻ4d0S@OPbxM <,-jNEEq\|)R6t8/%[[Tg# T|AŇ[|s]<$ѯxLADLpY=;E$ ([Z+<9T/CnQATe +Ӷte}#3ϵőG( I?pSx7HƐw"M ` v]vaLzʠ0"]>SEP1q*x1*~I $8Cc/P9wmDd`,` + ϟȎ @t6U8PBւ5Fğbkig}݌Ι*:tR6x!홙56D- ZADm0/N(ȍOT%!~j2wulwR߶Z r2@ +(Ҋtd` +et oveˠ*"fWF;D ]iȴ!yt^> ,z%=:L8mM۹3<1?.'==3 &Y!A+3 >xȼ+ #L S +{%sj + .D!{{& +xÑưƃ!{Иv4pvn -}9| + ħae ɛDk[AHAXxVm[$ͧ1S G̊fyT b Rh0QQ;0V&CC`h`4y&'0T=M SK*jPa/(V 'QxpEHJ+Jq((@$ʸS7NW l1#Ǝ +x11 o[" 㳴Ӥ@݃ =,R}CfAH¹%M k2G'!8+fBa<4G}fU vv\ PI(jv@!LU /l úo@y6@!:C$_fQSڃĚ^Hc $Ɖ^уPѥ0Z`\VMa'dzE}a' R q `ĪQDbN"2varج膍p'MGL,JFDa{́6p%ƥz3cK=H`z]j= ų h9O3ϺBwx\ '֯0o^|xq8E_~b0Ęv(aF(/ %ÿ$A0RaϜ$mj=f8,J-Vz~x;LfC"7Y{PU^kQwhZNz#mtF[0vhu&}:*QR>Fp8-Ƞ9 ]`u3CZF,Tڤ¹G1u-(7ɐ=7\ c +ed"$XXgD23'SF+N +ɬx`g*)L/$fL!8;&Yʔ.Lt:E,|S$](Qxg čYzͭYb7!E8b\/,Ð8*'$ⱷ2Jsi+)b!S>3b!Js2ݛz yb.-;y +fe|Aw&5Q1ҥm2P73>H4:OcʺaXoi%+ -1 kC$F! Zӻ| NƴLEp %vbP#y{v) 14#B?Pu1[K]/\zGB=6&Bg-lwc k`&WK T3;Ȍ&FFs(n7zt Jh]?}pJ667Qol.yM%Ef{wx/`X FoA0Z[ꛬ$[il +4C`Ryb}G=^A= 6$YBO,2?72N-ٮwCo7^"2xLU#O]=H-qA9XS2`Xvď i ,ҏV:37-m4(.a/\Ts;ۂuYc ǐ $M.S|C4Z+3tFHK<je_@-4]&;<KCulJ`ʌ Ɉ_cdkLǹ7"ukd6Uc^5c nA18Uc% >%øWMPn M,D,Y#Y*(صCh8X'~Q9G}(fji9vݡ$0FG31%O[:<:?qeMdPl,Ȋ{,O=r$ò-ԐTG7d13KZ+D}.=P%L(8 +j3,R'B&cȪ, bVS`IWqTJ.%G@,H^*sE*֊˳LR(-Hܨ5p')t:Ǡ<3F (w{lFuDhBG2Ҋ=v4e}0rL2t漊{?$hO[~.u AN]I1 +*3%`)^}vg)>K3' tB2vnP` +ǣ&OBXk%ޮGN(I)kX;PY0iI4~c! IXbz HVd0S j1=CPa,- X9I ågIl4Va +?&$ɴi59W T 4%t ,LT\,X!*" +ҿnP`)`Df<,d^7uE`0R\4-'fn赡1>h/=O $z3 hCHjUT°=31 U~ŭH~3PiHOx@}J[N*"^Pqd,f@>3ЍER1\,{jsvx2%ceńMQ=u2k >MI3Џ}d}z 5̬zgU|ieap&䜍.Nd+xOB}pN6Ye;IQpj jSK`<.x5jy]>\Tz'QLp N'U-ѧtX8gc5ҕ !޾JeH7ہ:9S-2(p#.]-V#m9틄[G!bKV_$`^]ʦT +R9ƃX*|Uyލ.nYDsX-܆㢒..EYdP,3xpKR5 8_[[# 3#!ÄC L- f,|3gעn"UT:b % LWZ"^G2nA;A/ rƾgjF#E-M $w G ec+kAh\<1>0+v$ iAlHh/}K3e"nĄVgaB,Mr]dʘKsXJ1EDB} ^I`i`5]d8c%K&A %&;Y3h)d^F/-@  Eؾhkj/Rsa;Akbx a +ɻ قH7 ¼22CA* Uk7 F7bl07 l+C'ıQ7ajt6 r<B%ɜw@K }=n8I1 +CEkDt#ֵ͒2L&ʀ@xPgzm<4_@amK# @ TW "ML3]<&:*ݜq0A=τ 28߲pAiǂ&}&%])ч-Zrkww"7x,*N469;d(j",Nd*evIJ{߉pfhՐu39a悑>b7xNLCAb9='%UT;ωaTt + +87g<'"¤d{:ωfMfz|o ʢ <'%K*XsB61 qsT{sBi){d='Lao'sɅK&?Uq$ +96pxfG:{ ܊Jr*eyLN{KS9nޒxL.8 ;i#I9AWܴ27[R" Q$`}iOxV5ػ3-C) fe֮ШYOJY 4)$9 yEc`iV=A,DE]VJv* մkXTD*+80")hv8K~JoM} +V7f ۊmXj"RXռ$yxH +4ON+KU\6`hz)xA86an~Xՙ)âd8x[* +u*r^0O$n +9\%u)oT\ l(3UmU)QFUl/4Y ڗ{([i٪DkTrH2bV֥)31յ-^,<9 XR|[@bvM>"K˜] u/veE)e:kжyl笕"1p]1̼3.9syZ]a+.]I+LUaf[WaňY0׉R])CŶFԩ?y3Ԝ$m0-HݨZVhJP X٥xg*j:QYTvMxO!&RiUi +3LdrL*{,](D%JKpWC'7!1e1ٷd +b'ՀzE,k@>1ڕ蕶l^K4 +i-8fA̭uDY +x+=tk#.GcăoCm2!uJ]֖G"uĂ$>,z/t4lakfd(⎚=]X[sH nf)!C:+9^] ȑʖθ8z%˦P$hFę7w=x`X&1hݘ[PXCEYȁ FuE'3Kܕ.-R +Wضv|b?4v`0mg/'umJT +6]SmvbUd/;|:S KAMN.HdyA~Tdf:SE=kc5'0B)Inm؏—Q*oz$?~!Z' +XfFS/h+հha@jI Oc='>kPޖJDk2s>MJ弰rܝg`rpw*ΐvEQ-&;Ɛ]Fzh vMw$``&R9-̍EoOnrfhq82_s^~D +uJ-/.(}۸pdA&֒Tv'6e,jN(~ka~}ub.圖7Anfd,J@dd+n;mɰ"@?WTd )IL1Á"<y_] D WhCQuc&׎5\\atӪ}] 3qXՒlV\lM<ѵ;hn]񖰞3~h%rzHgTYW'gWՃyB3]iUVP7NеoKѢqD#iwѥ&LwfDƹNKj2·զUxEGőfb}K)2MɤF* 1a*jA~lp01ȷhY*zڽ =S޴`kW_xNg4(9K'^Rk׮AalhZ{FvbO{{X6Ft!֙6k>.b_wvver}^A2 9_0a"&E^)p RUƄnRa i/ aqs  <30"Zf[hز%frN-E{Aa.]ErD(δ[kmWU6ae]eÖ*u}t#}emSf)euJPTuTbWŵA:S;UܫOUqYWY ~Y#gi9k˵fUTsܓYUc݁r3G*W%PhEkRkRذ`}aaMJ^:Zo8*?HCUT@e\RURaVsVҙ + +̩: + *09xPCUm"ۺj 7\B}X}ð𬾭`TUVi4*J#Rx.JV lXikpSTNwQd+qwBUUN<JtQuY验)K]UuQUj8S5 t#f[P!b_*Tqf/a]^R˙vʌ+*@{ KW *#,Q2`K+,f7* y} 8&l}Sm}q'eTʹ5aPqeU= #6зsViיM6Y\=GP9#Nue;υqؔۤߢw׌OQGSpM0ޭN;þzX r*<7-j*[t([! Y݉ r/V@BQY| u]A%囯تwPxN4"Ɲ8T@q'7qpr +"p:@krFcUVR[ӾA^Cm&ևQH1B1OmE7i]:tUA}\)[,}H6^!ig }H6p"G :5OفTО%X J*V[l_:Fqh;S1 NG6THf3 NeRc@>T4U쩲ա)UE6l UZYx\eVʞBhJ* +Unߛ[2ԫ(뱡_Ei+G} :d|裠Zj5s~7^l,{b-3َ*pXqTJK%n0Eaccui`D/2L_8>DiZ:w5Su1 u:Ί)l^uVli]lcezg^aK C7w:d\Etv.iigj=5ȸv%dS& ^pwn +̀dzf@r׵nXOF~)O#0`@ѦՃ3ұJW@rSUkGзS-kֵopv_YtjoqۮÅbqk?/]i 2^5з,(;h,zD)KtUc8/mbWfl\V2HPjsc&A iLjk¾.rw=_i:/ΦvVP}OvօBQD[صgeWz6X>&VS賷i1~-Y:ikeA3J1,SW0R5ޞbԠ- 4ÙbT 5Kͦ/O^T\@g]PJZ*FHIE__*Qe(*\TBϵsURj!WotPt~6Yhg:&pDA)4O1o&6lp2H}*(݊u MM Ո~‚ 13C;[x 5CΥ d:&˂Wf\, s=-xe`&9 5MT5dž:Wj+@9( 8T_cR[󇁋 +{ƃ3Ou $)OVvf%vwZ?_WWVK5jzW>=1  Q=dPupPDz b1mbQf-\T\ZM2ez_X%W(٤V 1(iHMI\EMb\I|uZjP'NMw|tQ,,-1VEa{[eI)n`H=b"\li":*C]R!\=q#+yPO܈ftjmtb-5AV;Nj6#T`jnGbHū/MmF + r[<:m{Wޥ$|)P㉸*Y ;֍]WG6G[{l:"P;YV4ݹIt!Ap\ZQ/zė{W]d첞°~^ZξލaQj]qkM}}lMWBۚ>ݚ ˇӟV=ԟTjQUq4.W1HE"ӎiMlENXvhn<ZH3JUh 1hϔ3#jN =Xي(>M 030tzo~s\Yϑ?*R& n: >lj'g܂5LJpl5RAkkfgZm$"lA+i)W31&kN.D6c乴|-xS"-ֲnxؼP<611\C\ S +Fdz`+c1g$V#7tS2$7V '5s(2*Ʋ3Vɀe\ ƒ8$8ːp#5lmq!|lir +Yi/SX4`k!c: +YSJR_/4J8xw?0G@6ڭxˢU 2jXHZqe8Av=BIhC$l['ͤ%n6Lb|1в߁ 3,^QGI[F,\P@eT2~J0ZXJ~"ۙdTK3,sƱY(C1a:C(hzgPft={n18X,BD!Dİ F?|L.5Y#q3>;GhTLqlCRFr㿎hKǂ/\F,/¸kb#llpC\~aI a(+G9Kʝ Ehlz$7H #B$Y˚?vrpB aPx3Bm +4TWi7ki Fbm[;K~V`^#=5$j3fj+ ۖas,a/بbu2İh8Dz^$9\顗jLEykhfU-J?Ӣ+HcG + VaI`#&SH@ȸ4Ve{MEfpm0Pnck2<+$Vg +;L9'g!oڜZvlͥw̉b<0+2M(AɍwɮB;k2у& uG,7L>ׅ>+^Lxٖ}$R*ѻ2[e]N3H`6C;˫fw2 ض)%Opl<Ŏ-ŊD=m\dmɿ#{yrls[-,*l x2ĄeB-4%b"l"R#2qC蒋_ pR4 DD>YI]C^`-*8e4f@-WM֡4g6@$9C'/J#A),b,M$oeq53aO^܄9&nw a/0L%2*RN)2%V(:EftDvOX)K9;;ҷÞ_a,ԧ^a6JGM@7&ji?a]d"aifzS0 *w#oә!o ypȸ\c +@QWƧ=NDA1X9Pa;‚/223i^(f*AzGLk eʑ[̾R.# @ K2)SNh^L3-x 'bdr(mz9XBfbв:g*e~  f,$Iq=v,}E.zA12kl.4"Ք;4:):{AV^N,4 T.Ŵ٢kcddXo5Q JicYuP_,]*H:KLK)cRwb0Z]Beީ̛#'-\ 5L Y bQh8HcE+& +0@;UO SEĭ*c+z&^!,D)NMADbljŶ"CKWݔht@?h? 50OH'6nIQ?Պp6adLd_z 9Lfq7s`lYŠ-ocg Tbq,\N RJ a6Ī?FE8 ȗ4 ؑ@d!2HaqD)`i`ƵTJfeI$' +m,l e!3˄50#y*SJ|y"z09l -zVy~4/ƫ2OE&-[-/<Pbs$fۤ!DVZ4ùN S0dzڣQ`dJt_,8]N&={|#1d {01q16#]/00?# N%3zFEXȇSY-S +Ņyb&Yb&3l.լy)々zv 40xzlS2-謤g7EEg]^ +̋"-3*qhxrN.z[ttevf F(%M2W0X%= dxn<;F1T$l1$w16؉<r=DU/FkqK@H2 +X s +顲 nf[$;٢XG$NmJ( Ny5 !ګ:}K4XI;ƳhgעJ>>W5/2W1Yk|M7کAU2"yyE\rH:eLÐ0tc}GcHA\ΰ>[SE*,aR',l3CEf( b_D ;e2{&rW"pMz㒇MxJam4J噰Ce0$8J;sg{A*xU`W?% +)xG8 @{2m̽Y,c]bevĆL6NiZ!DC.WĠݲ7mJRHkywep__L,Ye;L rM,lP<߅D{o}`\@$]C(+B@8v;gWsl~QK# 3m8hESYXO3Asu噵AQ[DK">wC~ #)4\>!,lK)ryVr +n.? :% `BȕscQ6GfY75]aMKУ}j>Hl 7*_ɥry~1M_- ΋2z$\}^(#@nI 9E\a@V-H 7M&.&6u[?ށ٣4VMmD̵k2z/cnxrL]ؒc ze#H W3*`lQY bzӒ1ZĊr3L%yo*[DTƉ2ԕUQ(J* glU<ЪRe }Q5 +|ވh-}•ĭ]e)KkF~{6v5kPo_Ǝ8Q |2Q?:+sơ)DYoKfK:9+L65K-RG%eAkHjV޽D }#A^B0 C&kmeE\~4wl)Rp3rfH~ 䢕D??@br+;*Sk4WzbW=#O=Kcj@G X^b]nKlLcA*Jg9KM?J,=蒲R1=rcԯCBg=6NDX7n=>'~$';zl=p~AҶ$+!+鏾I'0S˞ L)GXu=-K~WcJÕ@7oYhDt, lxR;fh ~:Cfuf4Ń:FoO Vql?1ja@,zh! %) 7XP0UGM+xWz*fkXDj &gZ&l&f\_޶מ{`}i빻E8'İg  EAeg֮sO>I8(v~Z YdŔD8J`|vПFǢM)Y#A޺vz +pšnVJTeS6"%,*Fb?I;.T 9zy%|1lx]旪 u󔦁Ufoqs>,tUfhu^sdڬk Ms|D X KJÜL|^&YA /x(6{(7PI iLUh;^gizwrNH"CO'969.n^HJD5 Ge||a3;M.=,wvya2}rru[4phPqy;ݲ!eVlп 5oW ;ZTuFO +YvٝOnʎ90[wi~ aw! P9TGkd|h(|K"2ڽ@tdUR'%'9h&44&"%. )MY<VJwͺ^n̡Oks$K0B,As'DC9vJ&PVDp +HG{gvv-buw97vG|hyMvv);sſ۱~a +pY +M@ۆX6ɾ)m³[(;,\n̗B|`_5K*c1! +P^B^ hNG:P00_:~Xf>ҞM&JN ]9F  +6рE&fIbc<]l[@=ja6t`yay[@*ˇKO'X HѫǁQ]5 sԶ#K(:88qs`bWzfz- *FWB -l3?ZmIĴlcU|%g1!Pco;"wloM Cvf҂8仒,]t&n+UHv6صIhՄ*ġG/Α$Nd/Ӕe4~ꉊegU=S(B⚀k(~ ~`KFI~'$d0HAn㽞Џđ4t]O_DOr-nk783lwN&8!z}eH&NroLV՛wS&6>%'x>^/lfAVکnl^bc~=Ʀ3KV^4yΒC?i;7mOƨ6=>^5NmwF 읽'Fd,–ʨ~y47Fؽs!P'ĝ :( +vhՀmLeE.ӪpoP=Vq3Zf?lh>^Be{$lV^ɣհ}]{u{7ՂҦ=cV;h w&Mm?Þ.vkE)oÊY>M\"6x11d {rf&}^;J/YA=T~)욏 \̘@4 II6{Su }ٽy)SWJDvZA+;Pیo\%p1#bRFPh\FɫJeN%zj_ESIt)ێ<_uI59/+*OeLހ,Е5\j{'lw꯷ͮfuPpd,1}4,4zoY=ANj#ܧm7}ȱEbIMd;RTu$'UgQZ~$.恫V!ءyTW!F*9-4M>1 KT6e[3.~ۊ̚@9nFPxYwe9o܏V^t)3fܶtamV 4BbS / uPBWh}’S`Tsc%Hڏ Pl7#YvL|^}|7 7* 2_ʶ@踖~,l$&5WZNc=t9o,팔B5JGx4(U]Butn6ɌCPE}|.j;|qO@.*~R]aKDt؇ms/E E m'r#M]$!O!ݰ <^Xj.MWDb%()ȃDH-grV%J!KQ[?9(jhJ>3ضX}-?b%頡f 2 t!vAцPldHP@թR-Ş+AR[ ER- +jCf~__ (g;Y"9h%XVHQ'd(;#fe~-xN9j%_>\_>-$(`˾pq&C|8惛OsR|ي}FEv#/|lބr-*'xep-&ӣy/-0}Е2 qG@j1\.= +lYk{xꑨ6x=d|ߞ_+W&µGҪ-`}3g_۫Rk"/9߻Lw)7Z.kϥ4R.kܷ<=RPp. }Qm5]\/dM[׾ ӵ/Bo]g#>+9(>{3a%' 6"|1Q_)j߀]Mn6끟;n.fwXR󉒟 eD3~ tMrp !Kop,wrTE4ۗiJ$?HKn8x:9.ޭ7ɔYd?n-|^ &_ vރWJ$|ū P DB;ZD7rR©mP"'N}7CU2(/zx֚wS{EFy~x~Cb&AqÍ|a롭 y'=:쁄ʞ|maF{^3vS[Xv3bwχUoe&*zܐ>/=SV쪘A}G^~E>v@iƻ?w[w#F{lQn{,05AwO-W`güh{0C% թxq!/{D֛]Yi,w Nt@\fV̄SV]7CPfͪgF9 [Yk)yIzx!aYa&EtHH%]\H^Hݮt2g.K.=KaLz[ԚK;\: MsvןZ:"\LfUz6bSY&cR}R͊)zƍ9[BN|̙CS6։ T浦3#Qmc~Rt?"S,p|xG¦#B=`s簝:AWbzmo{ܒ8In@ެ}lkg@{PӺ$~,rL립I?7@{}) I"CNlc`:k㫳_D?5pNII-D#a[dԐNa8c2# A`n?v'r|lx91}c;/'ɻ`3<Mu˰pm(:i +YSă=kd5ՉrB,mV{OZ +Htns/lfOvDDjq ͼr 5Չ`>mJ5ےW'WU)UrN6A`/bo(-ՕHumJyƱQ^'kT! :#U}9a<o˦e>B!)){q#:bU3䴫TJ H2~5].y>MJ楜UY Ome(m?й.PQBS,t@S*Yf\31)X?Y>u;wOp$p`\Kg}~&t൏I8HĨ +aL^ڿAHT/% +qD: HP2]y+$dE&K"m0I !oV(r,m}a=:9t+өZNG|QFX/#Ӥ䎪[ =)kzZ)5ElRں`\Z,M$GEAdMC<Ç67~u!ڰ =8=Dx2K=Zv6( h8;8uv"#"q%o>,%dvF4E6mCu @7G(bHA١zw-ςdC/ܻGltB^L]qڥJbӶJ@3Z$,EBFY30hnQMe%F2 6@AņfeV76vweDq8j+߲ s߮M.U`ğa`Q/b8&3̔cS򗄋Vr<9+1S6/R}z#NF|MFf7 qm #XIGEc~l+ 23"̈4+OWC?wO92 Jr)][B7c);9P (—J h[-ﻺʦ+LeܵjmI̧1YFP=n~ 1=F7#o= 2,Z +Mdq}{<|CKjLKV|5z |3̒PZPB[Y_ +K1ܬ0jV D"E9^(pVI1@)*곬 -v-ڤ2⍔ٕn2ң̬;#Hay}qgwݷ)[BADA(T | #'ef%15!NC_7 Eu_Er*Ղ(.Ă(\`jQBm|a* @q,aa\bz^#n~L]E:@i%^NMsDн7Ɖ| 3@WNl)v*G:%lJ'kq\QFfaV .- D3E k<\D}첉VBf/~fI${쫄Nx&g3"`qp[)3~M ԞaSoD؆j?G괛 MeP7pgh܆va= +Q%?wx6xHXw`r;gWZ|Έ; ;dJ{E ,@ JV2. FH!lIO/+<g>MeۮG&J.b곟:;Ēe] TɠpH*}pTiv{1ō +57)Eg[YOosll욫 O[t[/Ą_ mT?& 5n9$&E#YrnIPvii|@\c;K9ŽB&Kh)2(n9e0[v3#^$1J6]`jLn c}}%/28L]WP~FJKc}L|%G}[)fl,0p!2`FA 8 }#g-wu#]p{yaO5wߨbپ) j+$M1I:C5Vp]iچOԝ nU<65F"F֌y&x9i3R"`FP}fz<;OV%>h42[mg{ΨȞ ~p𰥏,RV ;}:MFYMq۲b mO6ӻW!:;n&98,h0߉bRhPhZJ`CP`̩elقT+lZ=p5Jo^o-yT/EΖ&qt]t,P7N-ey9#Dcc,^;?ơ.kIsS=xzP7AmA9c[Ҧ+.|r}Wzs}3-W]]bm/٬89אZ܍w{@VaBcI! Mº(P|$ĩ a݈lXBJp 7Ո1a1%9Ǣ蠼p,B؂""3zo awC4!Խ&VЋk'~L(prD]mlThHE_B_dfB:D=Ly)yt ++JOBˏ)/~SѢ4Bn04& hPHl718S +!ϵH64WUV5ޠ&|M|wÑy7.1K!gʫuk$׸$sek#}?2L&?U>k}8z1짻g=$dUR[̲s /-7fxAL=ekDMHC2|0(aUKU(_qےfi*݂5w.Šϩwv45Aؾz > kMOU!-IÀ`^ zᇾˮ}ʜ>%8lh^VkuujZ!i)RPaɼ5L[(ZrutSq,I%i$7~*iҝiQyפx!Pmg|>5@ݯ_p)5oPTY˜+5?9ZZJ>c7IΡ F;nsxk|K|m5W2GvĊ uD\!}6k%bg +&ܦpB+AHn3nP-[)f+4cܧbWǚw!k.O5u\chc.6!uBJ54ҖIU!?__]>%f:d0wK*I2I5u G̗U6t_LX5 >3UD61NnH4Jwnn7̈́wlk~J;qڗ}/aJ^0;W{,[o~LЛ+#[vϣA#CeO)g& ȌIi9ǷÇ +ݬ@Eq%C&<8(M_,;saV&#٤|cFDk4㠶O^=QHv0R2 6AIKWzlҵ̶LxJ.L]0XOUZ[rsga;POo@hM!l":X0&Og>^(Rь(P X,MۼeOinG +Ӝ6p޺X*.!]95vV / {X;2x:'{EhMa$l{Xlm!?kJJ[De'W7+Kݬo5evJn3[W[hrp~Or2|+e 9݋3gtD|~8()"Ӷ|p=AWZqo׷ml3&$#V7NS,^|p'HQ"M,<~ EC7sDoFEm][x5$ӧK6.߹Rmc&ȌũR$7s/ʡ Zל{~vXfyH,3#,ҽ~WU9\"]yuq7Cٵ^aW).EHIUs?K0>evG9w* 3}WvΌ(O`\ z/1Urgn%O`Ҝ2ow{9&.A+t7 {Ȅ?B!sNʞ5V[4.M'ӈ>;R {@HD $+ Ct +h  '/7S@!O'XDq=38@s=h`93s \?p%D])J%2\?p{.@DOBEӂz,ԙhl6' )-UrMPY[܅kŋ*SLYP'tTV]!U;ݨFr7[(NEs76Ot-j>Wv8(LT?2*Nx!kKIUGּ'N*)Y + kRT-@7j.Qބ +8_@Ib[U^{Q>>\Ng!Ͷ/VI.Aޮ6? 8YQ фpޭ]]] v1hTxjTs85*` F0TF0jSKr5HeA` \4AFFF[[E*W[E*WK*oma\ma\maԯQrQrûƍ5J YַgEBM"Z,f᝹;xsw2`Q!%u2_X~$c\B1loT,'4,=ڰ)LQs^͍P[-74ͣ"}ZT͈xնS=7'N4ܸuQ$H o$ܨQ3JMuᎳ7 6Ear︑pkHj%lmF -BP IĽ8djרV'Z:ēM`. +wRnԽ {rangڥ;/;NUs۾8%q/?6jl_i=_$g 3f^9@O{cwҮ"}wjnrO/%\ñMuqZұ Nv(vHEaav@PlqLTvFr + 7U_ojjqlpu2u^Pow&HQcp޿@(a&]XEa(hf31a2ic0PCCFndAP^iPA +a@Vk#L`XzȤY w lO+7c.ǸdJ/@{rd7r +v4To_|",3`ة|K",:6ռ2-y兾fP7G'& +zs(p9Lms J(գLg[_+U}lx}չIz-.C u4Vt"۟ii sPIMMk![i2ǩ9{=N51>2M,_X}Xjk766uYoF9vnafF ,ɣf&( T`UV_ߞWnm|$rhΗM\T('+E/w4rsr{{SObg&"xhxPMtbżkKYlR+ wCR@%{}=- +$|v돾΀SOڂ_~? fӿ,.yE4!;dJeaPi[zĠПϨ̈́R>RW'Km +pL =E (wάk;]GklN !i̟.ptq^ZZS1Y)xrTQQ.fggLb?k@h' WmMU"LtCy~v 3PʓK>2|/|B@qX2$JwpћWU#K9;lm+dn#&J(rNz)uFS.j8G5"el`&Cf!wrwsmo(ԴNd>@Ѯ`6 鱵JѮ| wR I& 4o,ה4ӺJM,"? vKgh8`g%E#0fqӭ1ޕf\CW'w?"3&@RA2]]#Ie)GnGY.%+[FKur5dJ˔ N|u.` H7BJt6 Mn!l T f8> U0gwfZ^ٴ4l|y[5'i!5/`6JE2L4Q6,$d[,-#CQ %8E? -ray gCXme)a12Xki`vpZ=Lv{w׊/Lѩ;KN1z7|t"( $,ަ^J7s5iu ꢻ9\.|dw֣I-dP'QK'To"!@( +iw:R-vc0(bfcNx?M +[ÉܜFgBd o L/JaPR3&Թ0t,"yn(b]*ZYLw:sjz-mfBpuAl1/Ylϰl'hߝrH9gn]OF *T/$UDy(ju]SͼT0&G0°zp\\)&p4,_~q Zpױ~t + 44 sݵ-z6ن.@s7ւ.A囩oBE0~ 4)u Etwk5ǭ,؜'M/^~o>[v6Jè}_ݺ-"ُ0P↼vzƦZrDrl qMVlG4=\N1-fژזּ:27^{dw=&B.~oFAGCt^7#fI ͻn|KsEj7`TV.4:6"iqhd۽*eYz +3`K:Dw mh/\U⺢B~6lZ +Sqpa!`~30r}zPÑiAw3o y B }èB#hJa7pآJbK9l7'c +Ds[Tu2&Jgl1&{BSlIۮWI2.lY[&sS%_cI@e-;̶4nH(>y‘c2uϐ 8]kk|*5ߖyTܮtENCf ƭX,5ҤP}&S$RMhmxtH]&kX{_H54y?ٴMvf7qS+yc~+4_>6{*|7&M#Z(o'D}& ѵ3)}rI<:T7P ^ns$05Fm;4GU +􅻖#,Gb-mՂ(||5˛fC Qa ߛFNu:$p,ވsYCϸ$Ч{nU(:>X&Oԭ{P=x.-}jgQ}.$W@\wf-s}a]QBn=g+v<&&(Η,hB2N6Rln8 Սsp +|}.Vd^rQ&ޜМ^ͤ޻#A^bG"?j i[܉&sA|Z`p>.>vʞv Bd9N&#"{qڛvXy4G?ANww;bBK6NV߹6JE(: `8:7]N$JRpoT]_n)ZzQO_SBy7'Zz/T4_~7U-1M<7 AÌw9[BDW15Ex[.y3N KxL + "D5p@㍫Mvv f.u[I0$4Зf*#p6θUJc-g-cgX 04kP`."]sy|ܧL@H[L2HxJؚx~̍xy ?A:A>(39~)! ۙRk?PЇ_88Qh@9LA|f2ߥd[ +sz'Rz]6&ob3Uv gX̌OVi>6hU>5뵢A?1 K>Wř +O]+&Og{♈wWC"a}7  ȅKA]7yGXr( [C`L[}h\N0Ҟ綻lkew5lVqkjķT6R`vi f3AJ?T)T% !@szٮjR'Z Zfˆ +Yt/ ]X˔/&ZtI__WZ+w-(XKa: km;n>K~[KVa e=8"LĊsfB$m +{6G07roOI0rD MA'/ x*LQVlҽ0N 8Up3EnEu'Z KvzvX\. ++DEбBB219GKm(hߊ1 +ql 4+hEp_Vm(maƄ + I_"{c&`C&gLg&zM(vIM}*+C*lśZĭ=$v~M }1C2y˵6ٹ6#ibX >)B>qA !!ZIΔuhEOtV(0!jsD3@h/!vP-IK"6>Jvdj5kEC2\3}RJ8wś"t3f~DYT1b-V7Aޤu[s~U5Bh{ jv#xkrUH[+lDz<{|2^Q숅?U\]/);^k_?=Zi-; _Koa0C:֊w\PkC=ɍ$[&,cKQm03!Mh WҬL`ް5-; ?; W4ةA?.}GJ6r6wm5"3Q@6` C_%nֺ_G̙>x8Q|yǜgcWxϔIĚ EͦwG(߷b.כC?u3~FSRt8gy, rEk|R#6p6n^4HCKqʡs=x_akn@3a(p )u; a@1z=qڮ_ʹ|]矫kC.{ws3˗YwGf" 9>Ⱦݍ8~{+U'rR,* 3Pir )'QMX4y~$(7wgD@htVh%b$LSBP vt.]Գ(23\jBqTR{4A:wȢiik +I"`RR~YހPDs$ĝW: <'|j`}s,P@Gආ%eiVV(R98$遦B "}i@dudmarwL ;a)C)r̳vMT̙A}0Oϩ= b4|Cy⍐8H74QwZYW oR.&(l žZÕ,rQuR %Fx1`H7RI[{yN]pӹ?Z}H8f\Z,$n}T@-r}){j=‚Sa3lt"}F!"VI4IG|$dl}3r&R;R)nw60WDL ڜ~5MRIaFTGHb]<%tx +3{d C%,j5U\i*m#zXOG\>s/[uD[k1ܖI7d ϲ==]Ta'Zp/aK\ Nj,U "j2# +DX!xpmV{ʽn'x'r]&63 򉿧|"y*U{Jh>S0c?U +q5\WHér<ͽw0=P uR䦋OiU;Tȸ!:+G-ө"z]MAl m㰋]J'+0JlwP$3C'XQl,{zkI!#Vx]  ][< T$$Fؐ 1liGRڻgi p;B1mht:_N^l9k&|@qpJ)"盶gbg|6] 笙0]m2]I{9%֕E0qfo9.Ġٽ]^5p.fi S1RQud_CBE;!w׋^\66u#K_7AXHȖNU=#Tk[\SaCOr8Ni/8iJvWem.v@9\Ighm443¾||Z8 MTDe_8l62⨶COǗE1 =!u#B6 /*ٌ?˙?&OC+mIb r'\{jEͫC-oi"wM'\hfBC If4Ʉ80&[ ],Kȵ9s؜{cJmt/Q3r{8>_~t2y: G15m][GnU" NۧUE{lPy1Qfӎv)jED}ՒPƚAJjYkMD&8.\e N،ʁEa jSBU8~6}Uy#?P1O[RQ,+`.3 %;箿!x^[;͏rзJE;y:8Hvm[:e[F:pf[|vJ-Lv\IжS29lݩ4AaVOUk?5϶Sj{9LVoF{g>8d iq MZmG|m Ujd(3]wvZ417 +[+FܦU]v BDZIϞgE{Yl) ,"y||e.g5qR{t 7p! jbwJCsSyb V)T^kYIPSb;{,%TcۚNp%6* Kb.2bF|a,wNh P՟_pf~@HCmᕡ]=wxԫUoaK)8'Xn&DǖrMٓa + +o2Ws9y۠lY},K^|*ѫ|RD/zA{WRcD/~sexP}[?6R7FʜVPG29}cn| +X\RdM ꣿ6t]ϑI- +>5,ʞ`րv@ w nNhiɏO +:2F`h=dֲC6]UvS, K E N=$TpA`*t(g}^Z<; +Aiu]wN :&4JkDj[)9fx>=.2x\_݉lD˳h[sͦΌ)^*j_OۛQ2Ȉ+LW[\ߎhށD֭Gm`J?K^*MX<W%/OpWp| dPB"W"3LpdjwteeQSq0v!{2sQE"1ǣcA[2hW"13î(@55tK]x_^ |I_>U!Y-₴eJktF:Tr.>)IuĨymYMo-ܪ"8'Шo5Sx ͘FA$FFHF$B`@o rr Kh2L F#౰fQ0(i#6]iĀfdOʬM~ +9aXh2lRPtǍp%9̽T=ԋ6;5wf!=A +ZrC SEKVF G떻g T|8{EP&f l]\r(V0q3v}h4ZmOUϚ! ꖮH;FIju MGkc7_{s9Ao ~I G#,"d*G|-[*3bH脦8$L ]$ +EPNy@ޗjޔ|`?ALyOkvw,yۣ +65| 6Upz9@^QX3'~eĂ0`jli6i2z|AeMxK3ۄ׋\6HZ@3Noۂ5;iC \7 +51Cfj Y-pNMN:۹Ʌ`wC'~xY>.)7؛mu`ޟv8\ Vw?(xi '= 9O|VZxAlD`:s&*'cyGOM2~ 's.I1PUvohLֿxFWY" &މ5uuCqh ՋiSC c찫zqwFd[o9h7%bfP|8^^l4@x(ΟyT[rtvdG[MNݩ<:mj/MźFiFЋhI xtZvRjASLЋ.`^O|[tD{T&Q{#IS.5fn?OZO)]otCA1 |Ds/DIs{nٯ?Sm , +xh+8司&6c?t= +"/CHẃ4E>"sAHuC6q:s>ޯE,xQ[inenm{D`ǃS04ERU(|HN'$`9b`/-)F֤lX}TCƴ0'Y`0.q"uCy(+rEm6A0샓;Ea Q| +lC5totp4)7`䠹ՌrBѝP׮aȾ|I(~[c4D^QɁJO1Tvd3tޕ=]~\p`ЮwtezޒvsցR 5u)luJDi]:-m,Tmt~vd_zIofuz dp!#.;-GĀ`n7A?.<<]G3}0)Ay m<37[4+ȅerV@1>|Qzjۈ v+'lI˪Imgm'-PUJϸmAοlDlf?DJ`뗓IJ6Atuo +A: nˎy:p9*Bj䨶$9D<*9"kH9 y]l" &is rUjv^P,.G s>'CiVH?v?A߱d>zj"HˢJϼ GѶ'L_o~J-S-vزC0I)1˸#b@Ng_rH1QU'÷/TMь\%sQBҮ ,cr0sfLnpR EI3 Dq;hXTg[T^l;BKr-)q@I~UB)&13C*14M sLJe P)#*%%pIa㽉uw\e [7{9{ʐĚDll~n]u2B3C1ٌUE’;oM;|/bp ػlJmuU)Y@vdĔ;7]TͪMM& ط +w a>6_R[։OIH;ep~A t4:JXy {{~Q؞NEw{H#z#`~3] # 9t@#yy0)QE\Ko.H +,^gȜH4K1?+͉$7UrMQ "h?}afXatN~n.tWL+Uτg:UMe ʨ$;D%՛>,>Gs:Bnwt~$C;3FوNqnԔ Oa0 Unj,İoRZSГC\܋rJVE{Sr۶mB< NƧڥ̭vyoVsLpm7#>JnZl9Dk)V[n*J#jFWY/ԍOUWEk<{ b5r0p]ؙ1vwmfLK&ODT5-T=24!50y=;7*uP5؏Qbg˒a`Tbhs%"Ufso$W]LoJ*N>(JЁ3∩ S^&bB5 [F! +:\3Kfx3P-״@?Ֆ&FfbmRne7),әvIJ d +~s [,;jX %K^6v+_)'4)K˯@< Z@DYnzfp0C2Bu.ߴ 8~[$))c"Ok@C X΢1sȫ00#zI.PTM 0I9\mSarGXDAGxΔV^̘jf@IThHnm]*)i"}(Fƈ +2pc`Qv?AC7DcO`iPӻJg0L! " `8UFNt\deyT# D%֢Uj>Ig,.T%ER1vZJM97cgm*VyǴp\{*ݤKmɭ'|[|*p)q)D?Ȩq&C^()j[( }j>ly%iIUұ䯜 )ݚ Kq_M_942})Z>i$h0u ⏾dLs?~/~G?ɟ~?~WSq~Y +卶%; rw'jOP}fk[= ̆ -*I$ͪQ<6>a'Y$A# SDJz4i&R.ݪ^%¾.1Fo|\}NpGiB#^*MuCc!IU cYÉ! Aϯa*Ndxۜ9y6xSihSǙ&8j9T\Z!#~D$^јhz6Hjj zPVM^{Rt + l!kF+ξ >4XgH]nbwQw2DtO9mݩ +6I;)JgsdpwɹA`ALARƖ(/gޒ XMvUu\\P1\۾2-ʏ0xWjE'z0j&Z I*Sr$ó4/82C4Ct `҉&v*M,RW%_8Evɫ$٢*\IԆ{}&/ϐk_+gu@9Ht@ &;"3&Y%)HQXBHpMDMO{ "g+[j"17A9^Lxʍ#XIRz/eEp#[B%h/ +Ti7]ؘQF> nb e>`~fRmEE<)M/+:[ å4x>u9[ |5&paQJC=8[o deWm9Q6&u(s$Зc0 +LňۋnnC5E +mr;0w/,H +bҵK*h%d%6?FNg*%tq߁B%eaԣV!VV?} nDuPqAے_~.`O#2a\9l?Y±u% P|Dӱ`;tsBdO:ּ/ҡ4~"],QX^ +ADU7a 23 2v;4u{'QCF|NhC-aDp+?=9DDulD?G="ŭI]I^\2䐣ZRT@e6?lDq1E`Xt[\|P5IVD nbօC; +Qޢ!4f9`_u9u2ZwYljK|Te(4x'x_H1y?/C:uyTNAKxR%CX 0} A^Dα FNL0 Qc& & IDÄgBu}e^ݯԅuwH\fuFq d\(}6SR6'(`ŷT<\EhuSCvpm(c~=(*&ˋ?^@Q)Oс\bEκ7ɰ W*f^Nr)e 2~D@)1%A~錫W(ddƽT.-k]0j8G̀pZe iJS5 7L-myT \rΏ4,R׼!zENB?۵#XIb0ɳg+V ͂e/G]rDV?扔QC-^"#q-Ulˁ5J/{b[!J٬H+q>0{,2gQ"i.)]0Dk'b,ٜF]TeM5EOf5ane(CTPJa >r'G69[K^D@u\ra̞}Qaf@|FeLg]# VG<"tůmZ-F[lCe8!GE8r璌vݳJl@3 :=S6&iPHPﱶN ()5QUL&dcIGz 0%#ORp',pD|@50%ZR kݽ,:cK/;!i2hÀJ78[?xD(Iq~K eNGn);!PKP7i!O +b!/4)t^:ݚe1C4D2SaA6?4lQpǎ/4SUDQb7ͅbJ.py88Tƕw%ֺPҚz$A* Eސ`ADIe9/ta?jbk&Q4 +wש}Kb=c規,@*iyx GYVa5%. ýI$5:P{iV5zBf`{T)?}BBg] z.d$O.Q̺ =Pp@;, Z%zDLqXpKgGg,eAE(K_.!!lG!+V +^$Ll NQKa_s}Ho[ _%Ww|@ +cTrw~M5pV7,:@/afCwBa+fPVձG^t{'J% ɻ;(c)\{W⾞#ŧ#EfyB{XiFRE%^ꛏz93 +QEp݊vDAVjFDkUߺU/?TZa7z9ڟ7 dr)9ŧc_2 @VE,{/(Nu̪9ztq}m9xyVJtew"i}vѴ(=Jkd{4>ʀ\|coTz6N%R~"q:_(8AD) םWEr+Bovgr8DʣV^Gwm<ь0n/?pec{Dt"jw o^aWկxy +Bg4c( _L†zhp%JG 7i0~ [j\r.52ڗ(y+ &H + |;.$ĦccOC1z}A_F: +#:qZ~4eD ^…P!WP%sq00:KJo"#qc^N#Tk3"4ӫOA~¹lxh5-yLɎߨ.i瘌S8ܣ.Aj_-HA4O^hDM) \>>OQ?{ke3< 7ܘ ~)/=s4<`Dׇ=Vz 'K Ԙ!VʹxNv¬蜤7UѶwKhKZXyRҗ}\'-^ADV+6Pڡ:<'QI>?ɔ=\<^<'o3+}ŷ + EЙ E0̄`K*Vtabt~N&Oo"rsifPM% 30VMjJэ*x ˝xGFn㦕ɕυ*UZ57} +HSa%q#0x3o'b +Wd 1_`1k N)m\Atv֙$k,P~d>=ʣJ$[#HwÆz& +ܨjoAڋ876sTR8b +'-1QV>YcCк9A'Zfl`AkʘeѰ)y +endstream endobj 33 0 obj <>stream +xGѲd~ף5\uʼ~ F"*u-,{NQ5h=[<+u={hO/G(-4yȢ?:u]ڕua)pِF>+L> }>Wwzӳl,}EҮ)趦wkg5Jگt-:kAvzq% Eճfv |s=?tWϞi6 }!ߨ}<5:|xGNy)/Y8`x? 7Bo٠7rtK\ݬ)7&R*QsáMIyG}?Mj2޼'܎g>!Ů~Oy-K7F$׶2.u{R-sR* +zҭi^T܍a_ )j$O]n.ps,>gn>W 2QГj&qjA^M#[lҭK'dko!D8v94t~i2$ZˤV]KVvV:YS3߹ _֙jzRO˚R֡-kiM/Xϻ-MMUH=%MnQ>s3j+ r^M;ɉWt}Lǖ}>G~|l{oܵdiA2k+dde^jkRjPOi/u^dJ Ǖ+Kʽ#WTByյlYVjd1Ofa)A_J[C_{dٺԲ^}i3>ĵ 8<~_N~@ք Ou^c_JiKg/ٛ׬ 1vࠒ\gP(_>ob;}}6@#w1k|aF_§On{[soM d:79a+tԡw?|,DaJ\H?^dVH])"JFF,HⅮx9HBJP١\1R_im}U#sOO4!+ ʴkkʜU:SҖ8qk2\["E?THu1 +]sGw좷?&6lwnjnĀ`Cf?oq܎S@Bdg{nZoP=^uH@̇n\HjP>_2AR3u_ϞJ&ūXI R7s?p^ɣw-s +)R3,c+vɑ49Y#hTt?gʼTeG[:JqgrgXҧϵ#xWȺw +XO s1]IǓ#)yt R,P~m6[]Ӣ]߷ =?>$퇨Ƥ,p{2'22vwc]]6۹Ǝp)rbj.v'&GMU*I/I*ܠ/DIRBn_;}w@n#:kM[='(+KP)fjsNK/?:Ɯ6ϝiDJzL0񜺝b5 +<8nuo?VqQGE,xٟ?[i+AoA^K~>8՟V.|M}x|yb deoapąy_p?`{- ̮볱[ٸF=<\IɍA[/FOLY 9vz+MjS: +q2'eC`{o3OTH-+qpci&"GE.mV(ۃSW527?V=܈<{yVĞUcRpLYٚfebv$bO-?5[aQ(kWUp KGg-tryxm4+ ҿxղ.\'4N[@omj=HPj.N0'Gƹ?ֿ\,'WBTmӎ|xnJNI/1j?D,8/أ cU,ˬߒ_1[c3cv$I;M=Z1U8}sGs(k1GFo 'Cnc܃Ѷkx^r Ƽ}-:Y{d[ݻRmYJI Ql䧤&ei=˞F\:!ʑgu*5%/?)O׭X2 :Lc?}mx':2v H6;c|²ǣu ZWj$;Ggy/m2_v~9lxgΏ=[?g F%:?J^#׉lYr y;6m``; ̾/t͑}?[φ.wwsX b23f䥙]>MfzĒ+aVF7TteHE:[}!]M:/by Ǚ]_W=lܴgz"c92vSCJ> u(!U?$/>(I^V{>,^z}m[#VdӼ9=j蒱eƂҼ";H}J_?n^+8UY7Ȳ 9g*˗6i +=Okpq& w qs?x ?t9 8ރMɟX_ҴA'"Un@^=H c'qG^ >5Yw͒/) =TFJ: +_b\gwOrKɌQ#yi B֣ +ط$|qB*$ ji18ssVkǩ[.)6?&RMO쿰x]5խuzup#,^AWq%SR9&?gk~WU?>}F\hFX{)nWcacB )4.;1_Dmȇw=ќтqU}d{Qׁ.Cg1_0KlrM|?3!ZipTgOG5H+!y=l ;`56sfOA5i6U &Mʞ&t#nBjRNQAk _릜86?- +GQ~tvj|Y E{D6]FeonWv/^)JDHɢ$eƏyr k}.PUD%oD̓>=!7ܰ(77PWnCPAL#?GMKI\lZ>շm䙍k푭O~O1VϏƤY>Ƅ{U[H,C )Aʺ#{ox|Eٌ`vգ=dޚryz>ϰ?_00%Ilu`mhayDJz[aDY1]b|*#ɚ nEz}}1[%eP 앮C0GSRr{OsZ.w25e3k 5bVES&5\>6J}{ @j +W.2 ,xᷥӦ6o ٸjp9J6L >y-&q]_y$.:1y5 P`~~h?p~ p{t[Linui5|'RQ>)){7[S~:xk}3s"_S-v%jwbGDrE:_H)F^C#r);JP>!)f(R4:mgumކ9u0l7fj~el ZsG,BƆN`ble~$N.ϮChʡzT_z`.8pFIiؑdSLSV{m8siYn/`m3tѬ;@voGyn >K8قTۄ k@J6&etzvὯ^e:Hd1x9?:-WE}\TD8s$L*K~A*G'X3hGф|LLTgXO?nU=Rkudq1G9+Z,OG+^XF]s0?,3|P܂Cj̏dz^Infny_aEy4vzA5#ȯNAe>{6f,TM>cVo$+xnmܷwad(ZBӇ>JqbkvA\Sf4!GU,VO:G_lmDd#rfաJ"ږZF"?tG +ĻgyL ^>Z^C%gϏd[I_hr!ϏSRV"^c(x{*=ꂽH2N]F!Cg~d뉱< elB)\-3S,-I{~gغ_iN;Lܥk'=Tgsa'yEGLzNsumh3'w\t}'`~'QLRf5F ym{p.Cl}I7S[q7y!n]ʜ:ߦ,cݛn3AI>__F'tGC +U6\>QOAno +TB c}ChAa߃=go{6^wy*nCZRI˷;U]L{u~KߐuakEUw0}G^N}l^XzT%83{E$bIrqĄdϽ]}{'osʦ~@'#t;2>vпR٬7Nbc:?>ۿLe2'TQb_KR~oKuO+R>7 +22 }tVgi5#lJnMʇҼv$|I><>)ލ7Tta>~z'g:/MwQAt["*K5+VĂ, `(;6쨱! "s1o|_c#!OEE1ZB֘ZrVlK6փpL$= c*v +φjPm֖A,Bi"FK6*B+tzc_z?5xuP.=%ډ ^cgۻ^%uGx-+-|.JrjgP}4l'/04SkMfur>ͻw,[ㄾ+hd:R[Esы)иQ>}zA %o]z[?(n!%ii,&c^w3GO$7]rM"`51Hk>U7]@օ(HWbzpǏo>zӕ>a0` CᏳ Yu%lX7>jg+B} Wdםf=La A(L/~qg(㏚;=h:t)`QKyO<wWtiWEy5oh>Aꗁ(QG5N"!,ԓhKm\?hQl\fJFy0]Rv!mAJ/A^1GJ Ħ$ѱ'O1^~ ȿ5";(/½bzUve ·gYg4cPŽyFIx3 +oj`d![;䇜{јf܋@ghL'ZC+=4d:nF2@" o.))ᏕyW ,hۉ$߁=_ @{uNb~iO^N6`2SYا(lԡ4`;TUZyN~,EAjFƋA`-.ܿc4N!GEɊjba+W{`0~%+ȼN~x  Pɽ}WKytMߚ872ȘǒrG_7K +7Q`\p, sz=u]j,?3??: zmhRYç '풕ܺk@u|d `W_MQG(GR@i +\>}h{ԛB?;36}*5[r>I"1ACuRJRNnhW8W؃ |xo~/Vglb"7('g9b-j*x0ZrZ85}cՓ `Y^;͗}vPNu֚?8ZP6@ŷ'P? E͠.3+3"Mcn6YY`g "~A>%zqmơ;XcĉZ `OPAR4VԄelOO&xW=@}mTcnۅc'X! +L\*dG},Ķh=:υƎD= ӌa9 ^!sx{]8RY2z!_KDT=?v8ߔH-y,#Oo|ޅc}}-$80-ަUdekh<;͖gWWRg^{ou]se9(s/?=O +Oרn})$z@U(;sc1|O?|ߑЎH1(OQ)0:I +~Ys]߻m 7FqO6TjRã*\ayaO\J8erSo箠quCtɿ;bl<>JjP 5#zm{'պ'Z`i#h +"&Tm@{@EcM=B,I_ō4 1WS0#@G_Vw +B% +AxWJu% ?z=bA=h%AݍL搼W q<>uHgIY>h0&4~†CzQZ|Psnw^ +>e^l ԡ-*# Wh#!q91F<؃rUM;Kb1ۋЗC|q_<zbMU9|sN0ֻRsjp]{(ʃ!0VKVo-:BygXO62KIV{{@]+#a ۗt*y9o.}PAֺ;4Sߖɬ %lRE]Ob|gR^ї2VGh+'b~$'!Fς$K:%{Do="aFf2}q mꕿ7eJsYN>4R=ΐfHڇ6b8G;Ag?}VM|x(=ߤ48L[b~k~K?Gwm9cۆmGst#x텍'?_pWb^ "Ӿ96iI گ;_bY1SR c9e:[毭_䯶RVq4gC@O}/ HR1APWeϚաLlh1CϿ9Nc.oي:mC7A_ @9_^my~(~5%&>Ui-ɴKwlb԰q겻goq5'wW%ԔG> t߬+]:p]A pKZuxtD'jpC]QB*Z6jPޢ!TIH9O %i6jaںXbw8hO?w$wgӌmA,̇7ĬJQ7h1Atod( RN]8\vmu0Nh}0{Bo=8hbb>l_}m>0/{KE6L,uGTr 1:6#}^u{`-yF8`;B.#6߀!$/ ߸\G0E=FG(j1sԼǰ2l_frx ><̔21{T<v=] Gv=p eUk$5GaNQLjQ 5"{gٍtՄ0e>Q 7w}Z@*TfPD3ԇXȻЯ.?Iv=;=yAdn lc~Od:7Wvz/{|6z|FGXq/1 ?yAy >>H.Z2Š㽊޻rK xL/IB2_F/X ɤ}x6~)2߬RI b{:LDTfg3pzlp=p|Ӹc:"=:bk j +s'җ>9qqoJxx~<#ʄWd)gf~+; ~u+z';eݏrиi -oXA,;챯OoJ:(KТe~y-x/[筫;Wd+L+k>ݫL#Ox~r+ǖ94zL>ra|BLl4&J ^22/J9ސy w-ot`]tvS}sX|=z]p*{~bdؔ=wq>~z?->Bx~$S<W~_X78 +p;o1E]`Ņ_"gFV/WTe⼬pշJ - .|Yf^e'ĩ; WH3m@i^4cK~q''3ħr (KǀR&;{7 +gM@ikWS:Mr ngy>%yBLAƊkOr4j;io[+_]J/ Ҷ_ K|@%dAʋ4 yީX1lWb=Jjcu6,w$Ϡ( D1~-3OPIKN0u`+NA;tQQHS-d:nCayh}|_c0$~_Q2Z0&=^L9PBأnd{E~#2w9AM%T<1M&b0 vcrZkO u'ں)iXSøw\ on#tYO !Zn: {<ŪA+lp8q񄇂`A(Wv^R{LJ=&+C6D~1Gq:)9{LZf`5;7*v̤qB/Vfz :yrrYIc/q+CHMg'PŹΪtɇ7 Ԟg8;3Hxےu=2xOAaw|q&߯l $X&cJ:UTRf72e74z#mmC#\t+բAU$Ϲ[_}2#`fڲ92^}d9]g=I^Y}ŝ܈LE_#|AĉqGCoILe>H9vy#sb +ԃӻҷww8[t$[,b/a^Ŧ|/{cgm폠@˦[Of9\;ٱi*Tlƀ:T&U"; umnx9f& +GKX(?\<|ӺHzO&]?P!uwY+n3 *:VQ`Pj jSNٟOk\ҮhmPAMX,Ku:IȿzFZJE]m} ZimA%TH-3BhQxYP +uR3~jhk4s&c<`=Q/HYօWȋqx9zC?ڶhI޷B +W}aƎCYe|fΐOWk½eFݤ(?v0{oyؼûd63 `lQ5?T-cXJ{{_I''dX +%فKc=<_:u<}0JP'xӥ"y0W}r}1o~I#LI'+M?>jDQֻLPXO$)z"TQHSUrFR^[o@,~Zrtx,k.`<\T̨2*tT ^1l& +FR0f.Jrmt1I]Ct#ܒ *dLMACSlGONOBܣǵ)X.\KY3*4mɿEqH-IgX.1,JW ioPRJy׶.j]wB^9̂f Aś  .n)7u#ǀ` 84mGN'pP |>n^?c\V +c[Ъ2F@7g"--C=5'<עJ)#Ϻ9.h tޥlzmʄ_H +jc& tHо5%\bZc`i{d6X S@ +(=zI[]gV ! (k@P7EuvA14dikhT&&}˗VqShO~@px=M'w`N'_'bq]ۏ~}cE@? +e }{v%}`Y>xI+a`}_[,rD? OI˴uczڐ (A*Kh{~&:[-s`xh'Iߛ?n c7΀I!Oǔa+0Է 8FȒLM9?9-4u牒=<. o~IoB7_ ~Tx">ܟ' 93? )z:WLe d'E0ʑ`o|+ /fm{˻Z @/O}ץeK| f~0d#v}|^O|r{IO\q1;,UńugOe߷>d יkq$w.X. ,pAݏvt0IM̃ w0_#D{l=[pױ1innO-uՄ3ץ;q`R2x{S3k FMb酴kW/E\>xYN/4)Pc:lqZ_`98vVMho9go7 vzT颓 CFgĥW^I=`3i .Сvހnu}&]t~Gn<f!S4k+V/.s +ԯ`0SzЍކ%Ոɷ>q,p1L:mz֭߀u;,72jGq@:>R5zG6#<wR'^t6!X$vb͵ _l;4agY#@A}{}-Wf@E|L~p^';]Z^84`J쨥T^Ju`Ԍՙ"!ՓvHquG?z?oO'#a_ 2$w@y_3 D>o92CV;I>꿍?p_u #^E gA;Fϣq? 5~XDQL^eCGT~Z^-UP6zщG}%W{q$\iw/a]~/ +{DWx ܢ;EʥW§Q|]tZ(I<{I 9]tթ_! JƬqʪ?r^kw)S:t9cՁ0ݏ ,Ұ)lO 6mzts(lYc\%]`MАJHaйhpdq҂ +>LQ 擱jیظP:fT4i5>.0o^@y @qTbsNш'3p hUQtS.ůP..9 >eK{ʖ3 y;xZ[ =wޅ{zV09צ%Y4 l3^hUG=3$BAɛ4?HhuxS?,^1l£zcy_cvhl4 G޹l= +QA)wϬV(+'jȎ u'߅  h34$W>O +=2/0}pT} +!4D JJD|Ѿ|P({x y4,bbD&CNCN]ɷi 3Pdjj^h!IXLDͧ9?Dcc>u8K/)+ϑ'Eݭ>9=uhW`B ԇ?P/1k.%)_]kyڶge c\(}0ջb;ol|ŗ[ߎu`0Hx kYQl.1JeڍJCY}J8 +OM x +FB9fk; "mU|x F9BE&PѥspG\c-ɚ[0G[١Qk#e!'<}"VPӼ хvHۛ>/:Y4/qAvsA6O5XLchF#kjd9} . ^4"Z7ʖO5_FM`hI\B=sp/1nBAY_Hy@~ry: st%whVM5T/tTPn/baEH>)`GΧxsverzkrvEKv~\Oufnsۑ~1:zJKJv@'oIf-9Bcw5 yp|xr!$G/ 1(cr4«埫Kz{;mMwW`@N唭7\QX. +_RIqo p 52={Xnlv + U +!u+ :2T{/LBJt{d~(a⿎3,ݠc h;hǒa+}%?CCxonEg~7.>PX+gڭIxmSrؓ{ΕZC^(9羥T6v ie5EPj< 2Y*hO& F̳d+yEL `K¥Me#Y أ=؝qe>Mzh)`Ѻ|Bӕ+1/l}4u Kr:,-n*hPxNa?fdlACEuG] 2 [o7~7+Osڝk^L%K  uY)&>9l~ U֕*㪬!Nh_6ÆBi;>Wqy޶!W~d9ZtN'BݯS6%~@hq\J9 +.}gzD&k ;`fx?'Ǐ}}|;mT L5?Gr$^ 5 -K0i}}sڊ Ǯ/xjԕ]^SZ恫|=‹vB~VO*r.h_Mʲ'I{>eZv-Ph䃽j Zk+x&DBOІ.YWzw]BnzѠr^17Wm,6z퇷6HEQG +ɧhqGi2TA#5] 8A%o0lAݵ)eKYyݴ$,=em]<4#EyPP ̩`>HG\)i7?åWzgR΋ +hzhKs};(q +a? }mp GY^hnSK! \a*(WyBih8Mf .u~vuc'_0Q mGhBULsWPZ̆0b2w=X>\8h{` +[  +Xf!({򓮍TQ +L!~F0WS#q(_I"MDQv3D'^ٰ`^}}`3{׻"?.40~c[-KIG}ҿmcPbۅZ>yR':_$_7m?i+q.3gm/J_H3Z]ȺmMMӡ 0'4k@Thl'[u3Du0Vm;9L|9E_1b&Pݺ<*W?^ZB0@r˳3*sl4.*d?-%#F2,y 8ЫRT*"{|a@ p$h[I:~9Z3}bA?PoO{GΏ}t2yǍyӨ>,P1$c8vT' +y "{ɏ`ŃV'FbC8i ,}dt=?WWk|A%$(Oz +Ӹ[n-~5UjHҷbvQC \nAA7:^m@ԍ1+/FTK"w|_O _44lB%dV ΃*MujEAWw.i0fֿM`H?g e+,zJ_T_X/[u҂md&wJʸU7 H=}HU TS$??榬O{pa::n8`c5(n[I49~"XHCdjjs$8 +jh-/;Z \o%z7Oo#3.qel:ߞ'd"P!U;UT{+`~ᶜv;uԂ7?;hu/ip8U;SqtPW=o+͞s̡O^^"tI/?kzh] +7̗ +*381|8Xp"4Hd^]ґ`q@uԒazҟjU˴kTM (b~>GOe3wJlG-!+6 KYY,Fk;2광Ja*w㐱w&=ĥ=jS.[֣_thJ}ojWH(6!IX=̄Uv6M?4f +*,F,e}e#ʛ sqhϪl]ci <2!BaKcqڻ{밸ngQ~ hRCZof%Eׁa4E mhju̹񇡛Bw|k%xS>b;ߥ*ȓx8Gvm;|ѯKA(O3C>hfiͅ_A<{FBo\1uM؏qc/fx#ö!m?F#&@ϱw$(P|P~y ,3`¯io`u^0u0#@ (SRPq=7;ah޶H?nijӊ~+¬VYl-7^Q=qط{GJ \]hݰ +rl/گ*kRqmhM +[?ah'8)s<0o'pt\8bf9o'o +Oq~P2yVa0 +QUo.<`$c\v +c-Ej$iïb2Ĵdmzٶ}I{6΋«fH +^9ԌpP,n,4HL&4T Յ`۲m&NxnnN|Za1C'6|b"]dŮGtqIz햚#DĚ}ˎ|-|}r4DJcAQgDJD:1q j(oC%.!~~)ьUO2)ر! O_:BVC5ըi@ vMг rvZrwD[͆]ؾu-(?@EvxM=ZΑL{rVG<ˀӶrK$.q /|cu0PR2ۍ 'Es~C4r=QHh}xe%XM$=kvq%Eӡ~xԤ L6%fq6^VHg)7NA3m@eX _ƐhfǭNI8"f^ns eFgͳ=|Y> +<_OW'9F5 G/RsfmT`h&6@X7|]6>Gw"!0ֻRֽ=Av 8n:n+cmmSr qx^PhjoO+;8w -X f8;_IP (kWNPsokln&X߂8Cƒ֣E} +*QcwPik 𲦏zNI7 S? q\:j8W!@K3>%Lá8Ps QuUe U6@MO2k)L4/uF}#B;~$̰2uz-ݳ@y&*w.`w3Uᮈ)0~2CeFz` bAƯ}<t xaz MDž40;?F_uu೔鉊Qo=g}/0Aѯ=C5e1T^t-ߝ/ZM ZSo2q +wzr*gA3lo{v> X`ɋ17*>{KY#A}Bl%(]ݠhWw dp%WEˍ+līm󖛡Kx!ї#m"ZX?D(r] ?8riPnDMޢ9,/-痼K].P"[΃&;Hn7P.םHzad2 */!u#>cWOX1bP儯_6b4xaExTK,F03e#ϰ +cxcPVO/:?-|TNP57rpD`@g۸><׃{)c&& P<` fBl;Os}dhFnېFg }f7r!m'^9 |yY)F0y'ňuQǾnu& i} +(P{G^3#ͷ|0&:(j#`+1^oʲ}_nqS(WWPSj<@E#uV13T&SA*Xn+2- :T\Meh &3$@1ơ1Ϸ=y)牶. Lᓊ +kV"7e(:>N»KcD[6NbjRv$_ +MyF(,2jx\)z׳sWs15wmNn 4Z01~eRz*4#M,CT!0 ȋbnZ甁v#('߬LEfh{2]-TAzvHu2Nukn@ +٪5&4J큶}wlt5_Pޣlq6w͛}3}޾yq Z=N9k$oCtWhpu#k$iSaA5Ab9VمQP_( ˜~6[ (jq7<⡋o ^^GPtW((p 5q(h OoJxG 'XqA v3;[%oF@I#Zػ}ý = !d8ZZnv8qmQ(*ΊZGUܻ!<7qVso6;i m}6y|zzxt{,{؎ۧe>ԣ5O mSˮ<Yx SȬ݇dczQ^TM6&CwrwdבmXsk}D)<(N?q?P9.z1ǩ6}c +ώcyGd"!8W2yj+&&qO2x̆vf{}Tc?mSz~uxo 2w +Wc+1E.}>|h+ǞW>xX WY"DJ܄)jפ iGM5"C>ɏmw%!?oo{u!:ةADs>KRȦ>QiM|U+`/r˛&JXoH<od=t?IJ!5wZ욛Xߙ9?_{!AVJ-̩uP,@|`sy^tOqQy]3Ԟ U;$z=1<)kk3ܺS2n^lFTфmWǞW*־zf +DMͨ7ޫSJ~xv/]JZg4vߚUjj^Z&Mzyv?jY[-n>v$Ҋ9u̾P?[tPLpG.]d-,h A]R3 *oU=ş}R b@;Z͂mgiuRb +c0/i~uQXsvTJbZ 1cӟ'xw{^0N}<):=1a| @{}LgS eTW{}w'PxD7X_\(=S/ŗ 0Վ{% 'D{2 6UMN1dJuUœkG2pK"^ȰFprK 2gv=X6ͼfo{pD ]ALp>wPIWO2l8ںŽ={n> .6l>=@m@C o@g ?>Adq$Y~$! qGƊޭZB3 ?=}ed?nD9? MKtcʟLd߿0,exGT9+/85+ά? a%KKM0х7RRKS^MX_^ەlKbu#+ qdISe٦99|OBbxNV|V3S$kZ*)"P=w%8'M)EAƛ{Ё#c0R˛]~6!4F_2o[ĉΒ\5pz#yyxW3"Ϯt;E\*l'LWI.Q9d}hC6])E^m#o\vIbʷ5핓rvL(jQU]#׫78!NB.iocߔ!_Cs~oӓ8j߿uoǣcz3]%QSo[ I.#ķW}@dؑn[i'oHfzqMQc16ښsl% d|BX#@/eKK=pgj>X!:wt:F=f3<%u%rj$Ym" =974 jbLyvjuF6&u£T-t/1h_lYF6Gu<׫te3e7fnms +g?h2q꙽b4[{ugHѴS'vĖL;Mu=K3ALSMfw5,ݰdMKלAK֞ۗ| ao5[~6?W(SJ"ԁqڼt͹٫5U;c#m*PI}R\bn\^x%01~MLOuݘ#9SL[Kt`ڤ41bZx\n7[+_A|/_'T/.{fϝ.Oz,`_TQHyV-C>{9<'YoSPF4qMayHmi&I ^_N),n@Sfjgeŀhh}To<΂~ׯ_M:_ZXg㸁lz 1!,?} |Y1~˸ķ9A@C|8}>9öū qOmϒMhk2yܨy` Z^JxsB^NԡK`~)Lwl18D20 +]}ǠXB(W}bۋ*/syX=[6{2EYÀ~ߏ!8t5g˽P932MA)9k1YE.sjS/8rT>@m\iuϺ?iq9nGy֗M(>,oCI7yf8'ͨh`rgWt,h@ $NS.}l(9?m:ߛzc4-by6G>d߹f!?ńO(RjM\w2jMܒ96EF_FtP_vd -8EGrƲ6TVgYr +Q;{*7CvV WMHTT=.Ô=|>b,^cs[|ai`S*=ID/g>Z'UelC>U:?w}e}@z]D(S)o_ۖ>ǏJ5R-Nd݊gGAJ:R̢D^{<+'k%ӊKj *ܩ[vk>o_~9y33%máᶕ/3?7g5/isƾ8p2/\)?ǹ5_?g=}FT=??[m|nNܘ$;:2gkNЏ |7`"SL}U6uD+kwu!\u,8;I9w?2 QW`FٜF%&j5+ FM7/&Bg;(+8QVo{zw㕪"|b])Bxe^[4eYfdٓoEl83V9Zf}y z ~u<_F{`\;J>[{1~ ןY¸bW⮊A"_.Gy7xY{ +) +[*BBV8C}Ia}TV-`8lBlDJ^%+<$_fw`4鏗m Sz6#賀q(8Uv'm~&TnhH%i/{GR}H5Hi :V1[74=V>U~1) _1μC40]^gBC ~F%4p<89ì[+.mo_|Ң"5+OWo7\q%|PݐG7 yR?1U8kKЦ GajuІU[.bC7NPt 8ܖm]" +ǖSq§|)3 qd=i{Kh.=7U1o_olR NnCA٫c;Vৣ?~`ؼt6uBZ`иN]Iz+XY&/ +> qDz95-1;SP+|rg>)C䀹Q907Ck[Eÿ9O M:~v53pCqCޟ)~]Gc|y=>KTzߕNd;:_Du#ɸn=ïǫpgFMqy[ȂX' +jU_ x{;ʝ5'-ndULIMi_<6̶TgQv]05߶6g8g^5mo}pϐCke/]85t AQnaբ2<͍u넗'A*{ڃVOٵbWdx;D# &q!R:Nwo_pǘxknΟܕBM[P$#wNT'{ڜGn1q  a ~|V{6)ʏߎVd\9ҁpĭbHœgI܂d9k/w1-#U_I՗" +:3t54 Sv$O󉔧pi\k_sg;$,i"g7F˛ORZT_Y =]7 oX XaXh{F3['=n95g^D}# +L:m'=q7ޅ{htNj#mZQܹ%Z$'xW5;Ռw|lfpV9):K:`\ vKzڷ ǕQoX7g^vy=ÆUW>(14KFq9O{GNdc˅xzq434?CEq8?ygAGDžctvh ?rU'N +;pR@[} +??Vܘ9|wu?+KE$.'K:&wI?j.#0}x=Q9N6ȵb#qGC>?-1qVz~BFNLC]Z[|.L؅0.!NjkصՎA +<1)&ޞ%+c?u1秵SwE<o`/|ZB^J~QF{cΦDu|95?Ds}*kxv?3E?e`~GEp+I#=՗%.Q[p +.haK x.-k&"ζ=q 徦#g*11޲6գܐܥwj 'Ǐ!P ycN69˽ONX)$[piuc.0X+*7B#C|-87bKn61%Qp"PP.X{k]3ՒKDIQn!Q-*4d01וڶWo/g`SGLj(HMT\+n>^>!0Ia8X-J1שߦɊnCTۄ]Q-2>}uC%g|{gdCF 5Q-Ve}~FS[ zdqqvo]Vwem~2M? sl[jx@I<ͫ: p<-9]W~~\[EUr?>*9.}e׮|xLg;? ֫mCoQgBiG ojZC[K8Y3½k|$dAg4$wn%<G(U'Y XmFBbQ_A׷3<463B\9'lv>y'o?ۃٳwW` 7#~M+[ qI)ȥqoK*pAL/q 50̶a gt6*0F3>tH&7tu|˄JV].{yAg +Vg;y,m 6X! KOG#rq沓K6,913V<ڕ2+i 3 5^5I,/5%;UeF!cڅ|H+H̟혠%<dz";o)-#kK~N)r50Q| +__]1Z{-_S}m;V:5||N gsD5`>.qMaک%_I,sޞn/-;cNN 5I>Uͱ_ +_N92+z}eTZdr,$qJF< x7t?~uh%C;L}l=GvL='ыcx,vW<OO[G% WWx~>uzj_PK?`kQW\<-#LUo%͚s?]`ܐo)x_N4#lrSt|u"ă" ^>ڃ=Z'}`E|kraOu5/\-d>J<c ~è:ЬY;oړy.qoɠ=~c߬>D +:HZ I}CzG19=/ē?/*{{13!4պoA<F's#"Q]*oEՌOOÿ?Pj^'Q@e 1>U |woڡ.u q hI\kL^ȰaHJ+NծvMi٘kiOX'M AWLKt(8\%*uA|84])j}}1)lHy'eݜ?5{/&ujY~C80ϲ6`}F6vy[W=+*a _Iճ @?x?|R^*f:PgSwW<ӷ?r?-E]G?43 !`ˊu9

    Q)(!S>!?R?]:)d +cCJL{7F{<k`J)?opw| ڥq=}Z{Ede*2IFir(8GOZUClr֬-;#E㝿H͚w߫sFu9?džEC7S|`Ͽoysos|_!&#JTx} 8̮)=qT甘jHǷԦywF8\qԗۿh?*>wȱn]Ԓ*5d!dX{E\@C-'su@N.MjZ2GUDߙ.]N٫_/6ܲ^_̤~+8*y`2=㒐]C(7;b^I>HVQ'ԗ;܇lg3YWP+\A,}Y鏒D1S_uR5(+822bW4F +;kJJ=NJO}'Q,m'XXЮbNB T[CUYntU,|Sşm+;\}/χ3ZC$ >Q|u"s5,{M:S#=F5vyŐ볖Jn5}\Lx'a.+gV5'UD14?.GO9 @{#~ .+k"F ;#͟zѽF ~x_s񾴜 {ݶg̱x}Olqx^gPlNQdϸxyM9 yCvkZȱ3~C13񱞧аs} }-pK-h 8! ԝv,^ynnshxOOqa͈ dsz;|":~D'T.DGmb=s\1 *t{'iKd)#9AsH`l߰ŕo~~S ^"ɳދ"yօJZ%jhm9CR5ԓ֑|6Ro]({70 .:4j8":52^y7//:Sqꔍ͉kbis36im26b<ؖLڒos&gZQ+_!Wi < m;YndDWqTW7؁ B$G(KTrbpC hP莰[m$xGݶQt,YI9mt/|>q5 "#g؞;sEi4RMQLFVŲ6 Qi'Al)jA%-U]wd F ! +Ԏюr)bpgi(MA\Z!Zqk$0=҂ci#Zs7!*/EQԈ};ޟID1Egh8qMƉÉ[G2ނđL!LKޣ"v{G\2RGe=a1gShis_invF4%#rVzy)JELRz:?|v1=zh7gFlTzs'{Cs`%K7Ոqy{2on$xXs-~6!oN/ DWZϦ?5CFZ5HLϫZ5oE^FF(&x $ ͽ_lp-kӬk[E2 1n2}$fDv3s`OѠ廽%quw mJJ9^]3*3{.=R 9n>W3y!:N˯p'Cw^PZeI񽂽c}u04?n-Cٽ/ =//995c:ɿqg"4zy +yp$m41w~'ޚⓐ?3$9:{G2.$>f3jA$Bm?'#^y@t+MVaֱw1 ʇ!rLyãwy?nfv5sVU|Gltd}:4Tۃ7= qO?yXtEm'hLWo}*IٴN>"m鮩51%Xc1- Fܧ.dP1V4߰Zo1a['`aTsvϽ6۷ΉQa<:QO"1QDi䱈ov<O QRhy~NAIHKsM&-&lhZk:$N'py:*Ki>go9ݗ>gߣX2Yz5CbTsik뽻w&zÿlE``<kJ GX|^9 Bڪ733I}NokKs^C9EgVDc3!v?NcH1C C6_S~*'Wϝ\SeZg!_A#鷻xƋ7l>Q1W ,pw0,֤º׆tb6jpA5JV4E\Ŝeb~Zݪc]ʹb3[KkH`fZNo 0PksW=K\V}7gmt<9e?q4~,lcܱ>Oׅl/.ɧB\vcSCBeQS0tk7Yw c܅Ibpϩ?6Q˷db˞Kqu.ڰi,Y'"GNI_Fzy@Ly-El)(4ͻUoT/oސ«>ʿ+]bU>c͒~ @'6KӨ1Z̠UB,D>i1^KHO=cpbLerxsq 歹wil b߲!l +@M # +ӣ~0 X9 ɢYUtF`>V=ޅ!O%r@=ڣ5EMK9P&o VGJ@NYWy~]l A{B g|ߺg]PAZ_P?7 uQ_->4-lkZ;iCUڦry>7O`Q3#;tV|'hԷ13jBPüT[Jnoc0q5êTq)qyθs? <Գ@1Uy?m#\[=<y.8?>,o{]r]MUL?';Md$[iD& "P5_f5Ci[LpHz'aYA{'*aBm%&5 N4`7rگ>%|kɸp#Ad %ΏuFU/?R=5M#RG &GS{a~">J7B+,%܃J>MBmy%A?w&qVLӳ͈ oaL"Nʶg9]S,F<ɠ#,:i%yA?[N̺TqaLeDl;ǖU} 227>dw$)&=U 1}5rg$ϓ+!|G\EyUyv; { {.ݠ6+k;D7uh&`S[ Ohza۔c(H_{`z9Fqg4<_XWY!:ű/Z.?oTG-Du}7. ~qN7㿍Q~?o N횝!1ܼQsEȯ9װrĊ׶1j/0p&ohkz|C0-js<S l @U=~ʴթ!|GžM0h7WmL<^l GmBѪx>AܷlӓgU۩6ܛkgCXX >Y~@'X[fC/{9:(j}kҰ}7-{w1p?-7zݟ֮)ںq/A/CqkGL.ȷi8k>#WnكI˝ok2dB eef1>3YK?Q+U5w/߉?Gcl,P³~\dNj#j]uj#%02(DOZߑEQ#4+RY^dYtIW枑N&L[xQ̓ zjѝn +tA/;mkC\2}l[_N lߕsmI_u!2_)•^nP=#?Ujw˧E]i6+ϭ=M})k|i c +w܊#W>Ǯb#ڣsAwOtOlb]z U=[Cx$?J;VC{ +[Gi{k~ +F:E +W JQ 4k=b}|y}ɾZ~7I퓲M:NNȥy6B,Zs*K}qq%_+LuCJ@*y@6ӧqW7g Ğ^ @^oy pO}W穈Q72$D|퇢&TA)_յ/#~ R˿pO7R<<yջGͨm٨_PuM6hF¹ƛjoǽqaܦQ Z|52S?|T%@I5=8'-ick.2O78we ~πbϵ@ymS@8;/[Gfo'T >=ko|LTlT|zn[Piѵ/M<VpگCp''x?|7?PbKCg#[;#À 2I죨E{ȼȒi5>&3fIdXt+->BR9L0i߭wO]-I5yڒn/(u 8)=!ɭ +Q4^ 25*&9rͯܗ/cϨ}w8>v0s1WDI F6e򮧭[od@=۪efn]{3jiL\`ܙ`:!ED^=<ɉ :8a] + ac:KG{kdz}~LF9cRi\q}S\,g*|kG|G玭V^(21BAbL.E}c["_3FjB;ʿgzy֗jX=bO }iyثi!pG?^7?gb >SnXo-r+HqOSuLS ˝nN: M?'m sohA^BgB{3}+jKT_*N5K%uOeDV5.!;ȢU +/$lu[xأq]Ȱઝ +Hdz#Ɩx 0$ AKGjx*ٴߏ4]2Etk?#wTXNWwp4qR2I>jBNCh֟DSRjD1><SrT>uthͭ8d?HRx_ї[ W8R?>>qq>6FԞHCk_0^ > ׎|m5g\Q[_+}'-o ¿ۮyOfen+k9tpjU>/_Ž4U#; `N5ّiFbߣsϤ>3~I+mZ sL&r6~uFϕETQbqyȠE+1l1>~R2Ľ aU?&yC?L|+b4RRWqCU]]+FT6N1]Ň1TwאOjYkc}?ߕJ8#{[2LU4ZwE8e +(qӠn*01|P[1x-UHTs߷?+k=kȳe\tsFZPYjl_Fؒy|eGnS$jI\WAvrr$c5ۆJmV>qZX)#V:aUly= +.6q5 IrW-e[c5GvXM 5Y[TSK;T(jޡƳv-Xl캿 8{??G}D<>FPU]Ew/3ϔowT2h@SCOY3'+. JR]"YyKm 6a0wnxO7Bse6-LۆckW:Gu2MA=1A$N?E 2+J#.;O4{.Fz:AϤܚd:%GX"-{ewq@5L# +QɽC*t2lPq>o +uOvY!Eyܡ&rr"k ."/I2j`,-pje»j7ɵls.ں=m`]5嬵&lq IE3s\ǒ6ϐX9lKu` 7]m:[GfM>;P6%hC)wF[=jnڹP%c1 +*NVeb_nEưۚWi AmWp5Fg}t UG yJ@cT ha\5kp7jF<nn<9[ -?UHkG +Sd9MrhOGQ‚8-Op|o ϋ}:ܞT8o[ +j&"Wr_nD`wƙ463Z!D+JBTT٘+! go}7Qclq}_Zt=O FZ8!y;z=\|<YP3Yi /@eTl`HeMNgTk`}j:Bzh#Vw}\rťڀ]߅;z:V'!9nk=Xi#=K$9kooL&{)Ub#֎PHvթ7ybN~b}Km;k.*8 0Uw"@Z`L\]ܴp#%ΓMs4ڵG:.T?o[`K%Պ|bƹWzÜ39~s2t5ݓ̹gun9o\q|ޖ?ׯ^vAxnB\>Qh*05RSbihP5la+41E+}}L @:]h )t<;=!|ZxKp|ަ߲*W|~`vWK;wZ^®QAUgOgalAxf6|%/ u7K mZAZ>wދUv+!T c7:!ke9Z>: 'Gs!T7n!hģ#>^oy|~$?4E,@oOnR2Gbӹ7[mGuqqN C_@oD_/ +;:B!膈%9ŨZ<7:E sF)z8Py;˃b |X< Qc&'5E:RPcK| % +vgVؿ±3p,XFM:$]AewAZV{bd`:U١C#㣌1a/f<;]p)ŹRJ"$'P>2-VbaKq=|+M^XDoU{dGRR<1*rBfd4V`S TDJ>k|3u| ZF?ko&|r1RQ>9AXŦb'ae}=g:q2$#(9 \XQ$l[~LDyEqu}|Ŋ齉7I4DD5&05XAwlK  XR{3;"*}||fvwfvg O>'6 +)2!*62D;=gG,Rq-=*5sd}vOrկt=7B v4Qd{j4oYqT_Vꗉ"ds/. > ?mhNڨ&w#S1/9ޤy2k7m`|޴Tǻu/?'"?L-IXo 9HD6jv9znYs\{> !.?$-fy35)&kmtW"Bd}b}W`/Tx?hq3<_A՘$]a]'İ1>#^PEB\Gldj҆ bA=}\`f}|pɸ6ES]ۄܦhcdV582ia"Z[ +fcl Wn,qK*pp +D*ѝKt%gK\uFQi!.!H#T642Q/=mwV%ӿvzaufy7fOYr [QOƅY<*.)`S0Y \?"C`a) {`v7oJ / @ ag;su?E6-sKsK4t9{3$Nc̹wET溾,OFȉ[<-z2"0D)ߏ-czͩ c8ψ +ttnW|S#^e֡yE { /!}z}y&w ~WXKa4.级j k9[R Zÿ̊9³:ͱ薶@[Mez¼ۇf1E9{A֟Z-TROִ2?|f\lbXɓN^m/GG-})q>Bmyj&0Sj3HKߝY_ Y !pH%_ +[^s?Y˳>XJNyA?TܦHtܔ]7#e<%V:6cޖt[R\ǹ=ch^^tl3|W*ʚ ҤZ|< yչ^d )fk +*{PupM䃏&u?2Lj +>56A͆K{@m9Ă /*ھ޲aeJZqBH:w0^aeC,9=I{vt>"eIf\O3\!Ƚ\/ky1zN7GzcN_bmHw{/$-O}u/߉tl!E7R뾈?> RSU+h聈Kxy^T:6WE,ZqSsu!c~;C Ggǎ3,9ym!~_AuPGڧꞵR^KݏT_8ض#dp1Hb?rz~6ٶob7iBp?,Đ#M4C΁75g|3qWo}Vs!wsbUDXY܇T\EGGh+mN[+߬M*^:o6t3}kV9M +&Vבʞ벏a:M)*Tm +?¶[8ga7vaECK&t?8,?ݞӻkqOZ/t9W!tG |?_W<^XۀJr~SeRϘTzTl΅іjS1& "-*f@d:%/wb[S"#?>k$׆xFdDp*-֔HrROKRyXRQ'+XR;{>b?B +ΡTi5:\w,68 }1ZxI|-K<[r)ԁJZGv<ݚtZs']Mʶ4*:iq1G6N%]{QgQ>BeW8MօrE:> #Y'*n Y-Zex)v.^㺿S'qW?gۓT;J5{1MEGBl8G_.O4e}Tڰt{= e?3iI"o6v^ͰrJčT!abnE@=o.pnf"{Zr#.$G̈xp3BCd7#0]ÛWr _P_^iA=#eQʏyRylу ;bU#* +6uQ+qAQej,Ci~lr.7!ll,xiM5`/)7OzJ=ojLzD ^ZT)S#k'W+z[Ā 3 _0y`?w?1B{G]u8 ! {KDڛ{8qdZgɜl5)*s}tL;\`pvqXx2טQsz%љ YKm0=[ +9o8Ŝ65kt%#xhA+j005<ޏ"6C# +Rmv6֯?oS=v55+q͠{7C, Kwm:w\.=؟;;sѹ Ͼ5(Vfm;d qw.>5}sn:O]3džcOj ^F4sԈͭ5 6tu . ܳQ~<^)&yb-5oVFa_kO&FFsy*sbCTQΰq@H>#٪d-!B2*^2+@2鰿.ۯA`5XUjek=!5H؋uItO_?sC;j[1y{([׉DfipX rq #?ZqJyss})$!/ +ͽ "Izq=ع\ג=au" +&d]"?J`d\/ԖWV޴n֋t kUoB8e\?rV6_<W.!']MA|y7zgrCܹI9j{?*ܪݖ*?WGsBz3bS.(mv1T`ShI)_[w,Z^9dϿʘᄜW^HdU)أ1c#ht}.j#[SiG7qh,GQI!1'[GK]vg +#KZMLm +xIE=Bqfn5hSQcz<>-?MkԵ"|*XG>)z!LS*3П><>!=e@,}3Xu/Oz%}s +˹G}TPi +{Kec'o}E>63(/#h g_eCZ}Tm#29WlQ ]s kpj߼SxҦ$jb؇\n_DHtwlʅ'~,TERy +/{6ý"tsvu9y71%b89 +..mijs\M:Y!zE?s)ޮ3{8OU877|AIXχ(܇TH?Y^Βb2Bɔ'tƏ<{R/,>O<t2A^9 p'#̛xl1qdD5{ȅ@Zr]Œqޤ#0R[x?~(6o]w).~ji16TSR +.ת +8v44/#wDE)^$2?]5V7IEsk2<:5 =Φ  ZU㾷|k;C=UJTKȂ˿[!@SOm y$mc1Q5?mc{Q\R ɺK|J ЋuqXw$vsN.)zYl|̚fK^5Nͧfq[()bl!1cGQPᖷȱ.6>:)R,Q?=cAS!?ߠIӀfib\4¦; =eݿm"x_`]T?&څdl넼E3}稌9aw? _*rl~l!<51O|cW~=LnRw +>eiO?c9Ĵk8dLLhM2 >"$mPk4/uC*dڷ*0ԥW4߾R޽I}N1kF<\n}qċG:g{8FJ&w&Ky4G߭&Ƿ'[dܻ6:@Nn$By@ Jڨ7ދ1F i}ІDc;k'{?ҋF teǚ#Q]dyGJ>{|Ϳh_ec[3D%wl?>~ >AW6Xt>Ư.9Fz|÷a}uwAjq-1Ɓ+^#[fG`cb1VB?yoc3f;渰}Ř|<eh_VJ6xzsFc^-p "y:.`Q<Èے.*cF#%3,J~~O$æPQ@ἊDK9l^KsXQP6X46ec_JcNwfkց椝ڔMHL[;M:3ZqHuoںwX8iGe#*+ >aB9?cGovbٹʰǻ*zqlOBqȳ$w/,ɀqۖgk6_ l4$zX1nTc\)tJtA;wBva/P1Q>b'OzWIr_5 +!FZ5+|O O6 ӣ2:E?xsR8H[玱.9C%̍~kyVm_{YòǃŽ7qy`{bMMR \ChdHU¹f 1Sg$uhNZ"TҶVՄ 69=q6GԷwm#U$jҎD^.|MgmkmlǚVhĪNdz. ]3g} ".%}'^'7쫊?hIM?a|UK⥓Ow.oĒ1>ϟw.<3~11Y +c!Wye mSpS<A縄N0`yJȨqO=C/g >8~3~;w̏Z! WB~{_4[sOk +#6???jx*yYd \C~[^;\-P!ݐGqc6$N lȥK,)o/\  C=G$cX+96?z֣I|q{/9ӔtؒNG2DvܩVfFFдnmں Gb~X +9dM#8"òE=i^?"w#Ue95|xe<q8š5Wtg-ǿS#ԑhGvM$ǧAFTwԭpKE)RQ}}'w5R5X2edb@E /:'%zx"%jk XqğAy)SǟK[I'-ʡKDPY"N9kmUHWkBvW05/fߧdsRaNPݤ+8gxh?S_FߟL_ϾRlNX(ia̝b4jF:BiT*4aĘcp="ìԶNXpb9LN;s_Ie8"}9i-W= E<`jn1w"Cinw?]c?svb>jߤK~U} +|#AcGݘc??;$qgt.\?uLp8.~_T-/c=1_gl.渆L5p OmNېNj+&l,>F>+w*j6?ڞ7dUl'ONq&]:KE2ѵd3?}_}tk ][4.6g5? -"Ů{mӫ,orSf7e8ߵcˊ4,ly%xu@&߅}V? {7rfDg"k1S>ǃ8/=՘/wSdOCU;RLAMXkdb yTO.&T *'oWB].Jwgp {.GS#^G,<@kksP* +:z^Xhy}yyՏLzY`(24(6W4mF>ִ x{ںW}o$wc:*~6fB{=@*;*t ~U2to)0y~ǝ> +?DɷGgyħT(?ڱwq-؊}AV>~ =5L<~NHp^*R=ez޷g'HN$ݳܓփBy4w *8,|1{/xPsH?cGƧMt/tzԐWR ϝD8b rvX34$Ҹ x4mf.$ +5#Q YUNZ챁"&aHԼ#v1#ܮ7tͯѬӑϑ{)S]}48trN7~[xK( L._ n0"I*tGk7gמ8in''Nt?9j`.+;h;uMZoE Ob?y?)Q9]t{G:N.>ϩHرOY}3V:NSMd| +qtr+A#Iw3}yq5UB#CXqCg}L5Q- Υ 俊xБnGjW6aw?``Jiڥ98q= %QcsC;/msțqُj#R`ς]wrkkԵɷ&kӄu132#{N"lG(_v\<62_HA~TpY0 +S/+y)E+U]Yՙ'3>F K=_ sq3};z Wm<͊oimqkqK;`:iSnՆ/y*D\HzrX';;)N(2F%dюk7:?]1ȫ+fF$:\w51UWu-\tux7C;P1n{*Gd[ucAOVvbUuѯߥ7еhCtq>ttNWofv }7 o].5#^ ;6ۓ.F}20H) x| '¶b|dIlšGS-]'h#o4ko/ğW$c yӺUpQy9ksawS7\DMZ:~jk9Ԇtcq\myDۂ&sfd(2.'y()8K2ݢOւ+z@{>O7&m0E3 7CYgd1珖LCbZ8>D}`v|9?"3<Κ5$k;RC3E n}[/<2%H qGKeH;' !^;2݁j^q)k>:0fƘ8_*w?w-kRA_umzIeّ}D6؋(3ƂDL@FTf/=Z+cŶ}~wI3Kqb* +F,w +m{Iwx)N ŵwzSa@KH 0نN]d};elG2'D*׸1FezmٿyI{"vDIWzȎ،w`HnTOK28֜tV|t׷}ѲXtgU/XCNI9.'l?y־oݎe;(fv<]"͖jJ:̝nlp93oYu*5E-wY# 5o2c3g}N:amH4 92&'Q$2f o?ji&**>=E|З_ez%l$е+3< ˏgCמ0/toNgDu!X%tu;$2o |cgp+0iiI_nr`Y߂OO ȟnp4lkьvSgϥ]8_ܫ!+Bd۽|rٯE%aTuЃ%SOhlObIޝ +Þ#}Iuz0Q.yHJEݏ)|;~'=w?ކb!%Sڿp͐uV]zdp܍Lv;RT9W]'ls|=+Ï@GGSFS1O7Y3xnvj%LPQT0=ezG=F_r՟^b-˦LЙFDR"QO|A {8˵$MtHGR=Ƌz H%f3TDwɪC`,{ dP{G^wH,jS.YאR+}|m<2+:F2 c$l{JjYؖE_H>RNۈӫam¤?[GzŶ1L{bupSX<~n p/~m5<O=iZPI|3&gp]$ }xr: Sˋ?eݣI Dtōk2v°~/ڻVa GOgի +g  ߠMr4P~x.-oН-U8&B5rgG {?ض&v7'}cf+;|.Vp5Ԓ91 |LPu؇[jQa}L3>Y{d`ZΰɘAg=#lÏ-+oUfk06#L ηcR!kv͌l_~r߼SD-kpM.^]}?4bCOHr)b8N!׺KƬ7m?=?ގGX2Ʊ'?Lu08t|asOZމzZ ?W#8BN5Ѣ!_ulM4*TJ(8Ny1jdӥ|ܴY0A3n/1 +Qjdx ֙`lL -c-2٧TҐ?4m %ݹ8郫/rzKT-B2v^@RGT Ṕ^aH^tʉ[߽?2qŕV~C.&|+zbfJPĒẍcɐ .Ǚks:9ks5ZzmH (_K!ĠL^S5gJF\cͅaY ?:(:EgseIZc>t|]>rٖ$%,xE}2v +endstream endobj 34 0 obj <>stream +XHI͋Ǜ2U鞵&uIa7Î=cE:?=?tKr֌ Grlŝ9:8}n|K/m1~(e퟿6TҠ&ծU:Z0HHob,yŖٖyxM=k:@lLDFhNIMUBݾQ_p2_) Д! .L`no<;2JiĨQCVr"H[1=}c\}Ya|)D@CbknLO^g!o]5*GTK0\g_Oł0:<'#+{߅[ĥ&hbquDܼ >qyuo%z<v$#ad`0QDKg#W!1Xe$_c%댹+P6,kQe  mS{>.|azZx`8>SwgMI +i8ncWcإ!Wo}VGe2b$5L5/Cqg)u + +,:`Ʉ[HtW:af?|i'#;-($??2jW0ci~gJ_81^^UO u7Q{&?H>Vnj)\g;m u#O" LG**. +cs;YΜ锭I4 oC*nȖs`L+|Pv~ ;*Ɲ|k_ɷn%27+dgUppEd\ N~(emLY݁wp:W tkH@9xe7P6?t3cz3@GCNqQLr j *dst_.\^|Ɔl̽i/p ]ǚVez-Z?86n " T`u_zc˜XK5J +8%X<ԟ9Tہ9a˸͵xչ}|jTn=ANWueg[Qm%^kΘ*N&Zr}ͿGۃL&;s )kr5ܰlOgc} ) qP +N݊QQT,4bގ1imSI:]CqQ?ܙu(YO;A!yYQ%Y~g'ED!@/tӻ:+v[~k*,|̌r:=cOEbHܬ̈ 8ٍ/GcP@0 ! `DM 3=em|=_9_,kcm1Wha`1KСb:?ZTۼՇ;L5#pe|U,Be\yb!D3&5 :ugt+UvgP۽vsќw9SGJk7Bx51^9?kṁ˹)8vHh([+:Xc;?Ύy3c;' +psv<޼ۗc#&2mF?cSa^s\ y>,]9P6g5!g[CvYΞxe_NGh(by8n@uZ4?6Xfb.8at$ƶU~B䑜9/!|IvB%8ko.牺VĄ?=OkE"$v%šJ4&lDEo=aaOfN>+k_:ܥ{>oߗv76s#Wx/y_#N;9CEȌ+ov!9^NY}{'oygg'ݜ;R>/>˘̀c1_[uݚ ؔs{Namωjdz^&N#群q`"Wϙ3ؽv^Gyѷ1}P;D^y".AQdz߸;!`0qʆ66L/~O_kC.ۖmé~(w=x^(KwEkQҵ~< + /CrO"+g= ٰ w0+T\( e( g(' 2N ,6!Vfpz?|SRWI ­=74oibee 8}=|gl?|Sͭ+$p9 0}v=v7) ; Vwә1vbMU2vO&&ޞgM9U\ ι؃AzERYEp +"opJR+ugW KIEZZ|JYuq5mbbRvvOqd1.@{u68LaQ?ކ?4)*z?~> l<XQڅǪ8p]Xс`%cbwmʧ;nMk\kn99rĚnY֜?p A,U՗ɛĂ4f^g[6}qba9H>9ƒ=wOulwh/p̲M.`fW.ZR&]-w*CZE]38EH>-QDg-KPb\p"Q|$WYk~'&YfIy {7H1]"]I 1)%|QIS|rj(m QEkt~R-_mIabfÓlނ:S-p-Ier u׺$+wx`{AU]_K]y??ߴ.zw<8{].ٖ;78GXυS07?&ZA%ѲA~W'Ẩ#lwtcx$ՍjGg ?08G[<9ӑ˫,'` TO\yS=nlLt^6Fх5My)Bo)1fAi;#uJ=}My1UriY+CHxؤeן+}ʈ?Fґcn\snョ?fTmݨ!ӣs祮N>2j&MQ,%6^F 뒸UpQL}4'T)޷0*bg\?1xd:A`.هqYKoy޵6XPD?z}U'$S#~rl\oOJc 0sU4SHbjlמZ5뉵Aled.l_=>A\hOjEft,m["}^|Om;xx}fϺ7mH=$Y?Ξ!؄u+&8G{GEO;.Xϒ(3l"MAԼuA`3F\ذ)3umQs\kA.B^A8t?a2ւ'جY|5{HƷyout/G)q QJ"Nw\W{o ^إWhL45"XP.XWP1vawϝDDSew؝9s*e}r=#ذ8́IAraU9};>E~/-OScnVHuy[?>lP۷Q1aL+Ym16}'3G1Arь7C&s#if3GU(C'K֟0Ip\y.ͱ8n:, 1'80v߹v--о?0*|5EG(9#yePX9]HxN+çku]8]iQzU~eN dc& Z-H,L0.[>nepqsjԴQ2-#`mCċK1v7ՀO\x{o*/}3 )k {yJ}Kڿomw=GV^3am^;V?W*rdXgv ;H;s10&!4wJ3Q;tT<˶D buӄ\=ߗ2Hq,apAͣ(^%{WV:A>GO|w^i>Ig7]~︆<̕Eƫ `41M +?|ci;=CȚ~a!p'X{zpXxq6ժiDw6VE8xh(^x_q&Mn\IUߣ{>3A+~UU02/2[ea{s[I$Xd1 +ŷc}3NCIS9Bysة71f9[1;[mǥ9[ X aGs?>_/dYufφ[)>7>bm(SxIk nG3 +w65t*Z+h6)\Akz6cVqcjgݰk踲{A.8pݣR_v^$Y kޜAwO_Eݠ%fb10'׼,2i0AÄSlo1q3-jM[`?pBx}Q?Ũ5MUXi~ *E5eahRZ̍0#x?~wR%H>$6+^g&Wγ\`^c𵈪9/W3ψE%,Et]}B<x imȹfyU = ֽ^<놎JЀ ⨝w{xV@R.)4dї=7n{;iŰ}OףOقDgK.\6<'Wyţ*넗. .?@,+<+_y9SF@W=`rA$! + A$=W!Қ۳<%UNxc̵ T3揎E |m =ñ" [څM؏!=Kz?Pc?Soxo^i(^(8kԗ &EK7c)[W0.Au0m5:3.K/-%F27TE-+?A<-Ym,VQ޿.~:h֘\~G\8&{nYr;kΚ7͡JPZM?>l0+Ӈہ\n +0vA1c=@6 bZPAA|Sh3@+;RNeG۷+@0K=^e) $p qz>?OZLx_W9_bƟ-g?4[4׿xqWQNwpU1d)icŏ[5'?зBxn9xxs͋% bׇe5YUW<8y= +z.g!E] XW?K/;C-/YM>lcR;2x))68 fr.r .~ULvy"Rד']'`~~k.j|t\ T렯JֿM%;;[ Uufjw*s f_"OWP<ҭyY@ƾfڻMWAcm2\z1{+ܿApY:W#XWڒ|D511AJ>hn֪hGf߹둔~HἊ<0a? (e&?䎖7EaPqu@5|P!pF3u#\r'מ04[Rze\㶟>t2H'%ԠE떻O^ORUqsu˝Fs8qƂLBcKxJ>ux{+hAGyp"kiqk?^7OCٷ 1ʭEXvν,2;3%`g,MR]]Աk}߲ +A,/h2*C0xGw-qtʜ, ˭k>3 _۾"8LJ):#f c fAּ &|`\:F&Z>5b{{ .'` Խ"|}Β=6OP#xS3oԅNg @ {N+>fUǣ WFB2ׅ ߈pQcjs@Q7иG|ѻ0u3.A|I::4՟chjpG-YhV]?9h?jiѯu1䮩 ٗN$v2c&m`CFnK<{J#ߺ~WIMoU5F\<eU(ƥK[wr,|X&;.>m{s0+Tz=tyD)wûv~wW6qX]П\@vx%^WO:#dsX]A8Ȓ.~6x+,hnZFx}ܜAr(W1pCs =rŠpSkFg?Lr-+]A^XLNknߦ~=Za+GBfδ/ -c(Ŕ㏑<2((\U.A[=揰Eo~'jӓk\9{' W LP@|x;}q6>P=i-yW|}\,Ns޶H\K[?Ɛ0)LqphIA4Pwys{Wܓ2Hy.VA80pJV·^ ƿH!1qwG+usC_; +9^e[{hBMO5q9 `Fc04u+0aӋXG ~hC޻%XK2?;z8ׯ6^_o ӟxxE Bw?;գ򥣠^g8WEgUS`}}%?}"t)9Z;?zteSºx5y4RJk#ϧ9A.ֳM )ؗc{~2Fy.0J]12 lm2aMz '=ރ{WyW9`ŭ<$\SD|3s4p5"ks5@ +*AƥU鸽gcw1Fc&aui]YSzM8} k.Zo9Euw jY2~JPS.qs>zGs?>l_`=W @9O_x{O@8_;DԖ{(lU11`k'[_]?rZZo\ԩhJq^ùP#]+S~pܿ8Q*9.#>n`E|M`v=y{_ҿ`1q Gk^/+6ࢥGNIut܎e{ _x=…1-ţuHj<$2 22M5y.g3z(_ת*+.m~U2PK橏kxt<\?+C@@GU{h`/%.v ˮ{u-^4ud~{:E ZxIss`?,n㏮'/zكj-k|^*½߳PM>?Y1 oi wK|v폻c!uzR8zO^?s{D2)d5k kq]ɪJIsuE蚥x%atsȪٖA%˥>b+?z3h9&uEnT7(CFk=zD[dfj9{.)LLŗΌ~zc{&R1˳S.fL{}w!r /1qLӫJ6W\vuFd9k#||?\J]J̹gw7hbOd{j}(p1 :}5I<ɝ1"b!u=:Ȱxhb\,F[كƝZ]% փp1+Q¾6 IK쁺oj?aޢSsuT[EGM짓VK^Db?5Yn,h wp)E[.ν~a}WHF-8H50c0lk<-G`c`Ց]j$܃]Y\ +9iZШ^ow>|]Fӿ10$n͠?)g+|8PB#ڙ]~tcm7A -Ac~4&9ϚA6ldՂrVw/QbactFbYj|P?kJ62gt@! +gb ~>yDNh\TlCci jXkz?Iڊ:o]a;DZ~^ҽZm/&e@ +~fZbii1^Hu .yM__1&~=#\yRACO ~81Ŝ[2W,k˧!Fsy6v1{v^32 <M@ƭ[ԉ5~\hY22[$ƺ>k\wK _=V))?t: (g^n[X9*0+Dp:4vG@"ܣݘ[vs~惙3.d?^5LPnyt zbO;l1WN;~v +'#{|yQ {] r}Xtmj)oVxlvV%#A *)=87AvP*oޙܛyiOS{Rs } @Շ'B2q\\2audԹ@Nyl\xt SaO ba=ϔ7. ך6lV%Qø$ -}mr ++[eËc0/3Z8īt^t1sboJJ~LtuSc|r'-1Mumw>>'7\@F4@-q{[G#~uI|_TAWlɄSW.|k9:DnmWm>؍_{AzJ53qEoiV8@>dCƈW'|7] fv԰3c,Э zYwbOֱOh= {Bcӗ5O2y39d`ѻ=q +玟R#v6 ~~ y4`L_wmwڭ~e/̿JPU0&X <Ҟ.[_5^m 0X7`h4!yOcU'`kWYZ7q9E|P׻iڋO s`S/8sk\jMb SEvTWN|izz/6vx񪯽c_'0JOyG<m~ U!g͚m/yYVQh#>_[4H;j'y[ { v`%CP9F1ݽ5̭\CןbK}LQa&y a/zO2?PK\n6+uS'qʕn  ?l^|?ǘ9ivvu_Vz[a_ca躙ZbISa ew{L;1o CgHD|/4tP[+8W +C8Cs*@[MYWeNg{p/9s;3b)+n9!7Ŋ-dA+Tpq `tǛZ֔ټKMHs޴vq b"[6/̑,6)`&ܑ6!X=J`nn_+@]mE(\i;렗*ˢս%оwPiB}zz|[T~ 4]3G& "3#z]#blQƷk _{? \9dPʱ]R'\I A}Ahsi #7~ڳgW*lIѳcg&S?Z?|a~OOxSqMOf ]/0l`AtX@ +6߇AxfkfF:sVH|}m[$\dܢ?^|Pj4:-ԹŞi=$K9렊f'^ڭP\&(=Ps%B_K1o2"vkl36o}$|yϰԷUz c2jik$ޠo/R;[e9=dhEQMmqs>jN VƧY/j h l :,>c6nuQM%΃֔ґ| +#METg7xdsw 0kC؃0~$́4C79 ks)j& *Dz?]nݏwJLղ„@ǯA_8wOOO` gu!mHΥ=*p-5v#rEGҷ6?2+!޵?;W>Y;>FZWrc-!>Ս 2~r+J<+oU}뙞FfA]cdls?CZ_??}ol_?ڸmKu&ۏvjdȵ~m+Г\,˩AE RNc8].hosq1&:EV9.E">}iu2oDf3& Xޝx<7vUK/=ʭqVIJŇ r5p5NW-h9 SS n3"mٻjd10f*g4A@}axrwvOV -2 |G9-蟿zuFG1?:w˜4d e2trE],&ګsyda9 +}'ė;hŸ'@VH?0ԙ"C8Iڧ='(p^fTTU/J+9_M8)f+$0tZĴIaob=נ4=S8>'^pqQhqyHq)ga%qA RfC&ao_'y9ŠZpۇo0+SVtYU9\8ۄ-\PjhDƉ_8 dN"cRP H se(=[7kVs=yĴ'WN 7le'#:ϗ[?5yW1ô$L8 v0lTd>AC{5v%:*ԗ0zcuϒlˀ ?44jkܤ: (WU<5f=ӓŻ(3~~S1ߺ2Yw$L6/ o N0r9w$4a }/&n^5E sT02@1^;h'ZW jbjr]e=ᎵĦ; V>j4xeYZFWK74̀q'$cؼO yv|r0|[̫%=txR3z#~suf­5E fG΄ ЀXM|$կ52чÞ# +O>%z$ 0ТuPqjA ߙGryn ޭ:t?`"Hc=Zm "e.oA^׃>\,iBM1Q`:Xljx)l_7)6Xˈ'%|S8D+dd.͋39VqzW\`UAMnb0#Q)Q{Cc$83dGPs7~P|;.^t̵KA=5KضDvmܡw#8z#mA&S_׆+R'i cҼQdFYU59Г75ڼsE!M>Ύ]w&GOOZ+8[oi&-1dg4 sh^ͧz`[G"4tV$6N* 3]Sf,$N4Goh(Iu*a9Wk[ZJ!k]ӋHkS15௡q_P(N;}K^$sA6(ӇS*^j\ƃ__psOtS|ö_Ku ?M<} ޗ~.pjEZo-A%K͝>ߺO̒Y?ִS/k Suj@À7&TP#+s ?(Tks[jX:]4ƻ4Ũ:@}7n=NTG!P/Yi+,w=CUrN&\1g*p5Fl">|;q̴)s K*0 ُx~:d&f_ YCp+xUy@=j?^y:48Ck]W +fOu ?" xda`i΀cK&OjU1ǟ+Ӌç nkw<c48|j7>^F5@%Dc +r;*C~$q<4<*1gN}]_n>Fm[LW{.5O+lۯ74n%#P&3|?Yu> ZN헱EV}a.cc;yi޲Fr>1s]GZʟKZg8@=b_72%o"dsp +k9B}<&ة՚xpc9*1s.'XHex_Q~Fh4jksoG(&B8̽,]Ѵ#O_z<#\_p |e }7vZ>OTP !墨Hz?`nO#p7ӍF5i;-m22&"w`\ʌ-Կiyn<ZZ?p rS Fx=t!F_Z<~,rYh T !rbBd'cw+{o2gw`T7;Ňu)8y;h_K0׌K[ +lDrN+%nj@pe`}ꡗt|TMSkL;hsIE4 +CT΅,@}5 ,%((ZlDN7QUX]du[o_> ֎׵#}@0ܝ;<2myﻙ{ ?vㇽl$rCİ3K?3yGS']uKca%S͸ +m~^Tu-tXooًapncF;Gnyć!g_vFngRЁf n_~3Y)c[z$yՆZUۆUx :zFOd0$jY7g#}OWxnYG#Cnm6Xsn@kM["?1CK+ݠ-L7s"6dv㦠>|Kݼ|#. ]$r]*œӍsy]ڼXo0Wmrl:_}Ș<@٣qA鴧1|Nر7 G–G> M[f8(e .ނ yY߮8Gw9xl4YJ5G*dPKo:{b%JGz1 f{Tռ$}?_9KL{xrWetOu<-G@>]_;񏆮= O8I'Q-۳ƦU32n]FC1O=ZM6x=֡S-h_0ߣ?Z?mGs@;a ʇ @ s.c#CsJƛ˸I6Y~zTѝ#NqjtzD;y.4\UnjuDdܗM2,V7T#;~~t]yJCvq,g_4ߎ7C6`_%1KӶ;/ j<7,JYT9fPh߃~-.yOs#3l +?5ue+n[ / +|˯GE]i fݴƠ.t~ +}g+M'L1s8b]Cޗ0Jw=6k% + lޘe:jyV<> /ִ\YWYx}4ܛjr߄S{.yޗ=A {9Cas8#'l^,\g2$y_tʵŚ]k[H<끢_B|G%?]a <l mOr@QqgEix43Gai 7vXH{VQ|&v% +{ { w5 ϱzY8njaW kO#{g"~?2Ps;Fn9aoҾ5l,50/yϧ +QA_h]/)~8W&{psû~_vO dW\$e5g6_uqkLђV6^ ~7->v%>{ﵗ"Ds̩6P0EwrA>p#:_\u7,2n\w/c8أhO8[5%q2auzi!M5-G&sM Ϳ\,)#T%YUvZc$ITp]# oߏܞ ہIsP;e,Mb x 7M@T ~ -A`t>֟\˾v3\QC4Hs6 {? +;]uV8 -tIw6{/NKak Uϰfa2S##1!oF]X~u{:I"@]4ԊB_ - +Ա=v)΁FM~9~?~ղPgRIʻ6TWC;pF9_]K`u,[2`t \q?8!)K*B +|YYAʊ?{)u l@̲=54tޙL0J+ L_7_^,|-.b|f rmWBgI@yHPR&7 +^W +Yt2 ~4 +aׇ;k(`|`NLww`?o  ,^@TV[lh=7ݼ>}zU$k ܗ }~4㶓+ YiKdqϒ ]zg(cxGk>A:+#2~%d!+\qE1Y5ި?GM@9+Ȏ؞7:ϡY< kkƛA|CC]pZcF#A!e4Haϯ$\h$5| +=%q)0ƫ3*eR=G7!Uo;Z5v~y?y9?9C/[s{7Y TG %sԥ)lZ SpjJgc/f4xǴ{Oi号G+| +÷h78wysisć!p:7XrtE_dNxYd_w +[&PiqnZ}rd%}z#Ms9siȡfbc4iӭq~g0 rz&$>K!yUr(2O~>:MUFf-VY dg<8 ~_r,W{ 8n҆[G<a0jۥT,s?}CDfw} }ujljiW˄?G +X|0㦌7̉];Gy-`A/^*Jw}˯֎JW0VA c 2#@cRt):D Gi=@qR(tsw`|M@?Mgʨ[ Z*-tAC |Ndo9AK>w!H_ ]ڔB9fN'w^x92Ƈ0{~h.Jas6Q!º=CNۨC ctram6ށR\ˮ3HkB zܒ}hO8c0?" l9v#lMlGfڎG|т~Dž=IBK[V >9B}`\Wj\cUFV:闐[̣ܢnr,X=\=7+8'{,|?s[\= #sx %zP jtU~?Gp%^{ʇeW/Iq>#)6˽/{#)_;* 2$24$/[@;>9^xy#x*x;%,cupmQ{cn5GTItbWJ[2 +g %fu}X(u~4\GW9g;vY+2o$5Mx?w6&oD cC|9/R%3,k_S02.,=;Ƞ +h0nW .jW]`!@J#c]0r??2-G@A(C{7?{|;uh?`X7!Ի70#@ޘ< + a_h7;s1!o+\ xPGe_, CaͿB^;;x(^D5@C;Т83{'W;rF TsJ=Vu=y'o_`u~4؀C kZ4}"oQ.[5Qq 5w`nշ@)ay[=r癃zTlՓv6Z>nkP$eJn(bo4Ig8k)/[="A"-wzoZL<艱Uo6X2ӻBNvc/?,x牣F#I3d-ՌX [EgrUϲTOx}#Wb?c:8t(h9*Ucan wp)>zvQ?#/;ٽ=؞."ugMONO;V?FEy0q,ewapi;0g{dl' +ƛ9j-p^xխã#{R:,id^EAqƁ,}0 +D.@+lWAO'`VE +|BLַv21>O}ywPE݈6?`k+ 3co^5r1v4$[%պ&~w" aՌU9X[RfopeI%?$Ů\PKWYYQiu'YD0sjczwh۪KA0Ș  +-_?䛇Y}|i{a "A +-K`[̃$CQG(|h3P}=v\f0φQ?r<~n )68;&juZ^+4u0E8 +d\PΊ_[nM#@50FmuY%6T=42j:ƮJCm@֏Ƥtpˁ;hL#`9x?)Uo\]Ix]十ouR D t x& ^"&}\,v:NuQk ǩb(K>Xی?",w\hhC:V L$Thawq "ZhDS/Z أ5mtg?$ȧ2PN_lFr0=kޭ򎅠/8GΟ ])Q^X߇s<}dtӇ7VG+| +hwםeьQоsA?x?HJv{XqI̩ӷ><{;5zx{pAPaupqyD<+/~QhK({ c]ξǟG;EgwG< ̛UA-jb*S5J^"Bn\ܬTO}.搇b Q0wՏB?BqXa-%ۆCm%SS(ث#lD@ 77#e`M#c-}ಉ\bxN"ϯ8]]|rOE2C\%Y6޳c[6so`Ven ̮΅7\o{vYO;0稿cg`fGEZG:k~DE}+װ_hѷVP(_:*2r`d3ϷF|W`AfYLo#n5~d v;oyP[u/.jAҷ?2PQwxPOj5>sy^/`̀eh/A{ @&d9գtynl|!h]) /ֈ}=:zZ̯J#1.t*5U!r/g^k ^~4~Sc.v;*n@<w !w$8iKix)5@ŋkKrzoϙpR>>{̜=rF^&&wb8F; g{ Gf#M!Coxt}T 2>HSQA.FMǘX^\:t*8yO/(B>0b2AHL u<.{ȱzzH*b>tPkڈ(+jc[j PټOz^$sUFwu֦m2daR{h{MeoCfK叿y_Ĵ0$Iߺ]ڑ9$l2tk?Z !qjWAaY!t?Sv-55<|0vZLٲrяc˗>{{~MiV6Ka#kZ2I`CAַz1>k?UKd6!а܇Q彝]21֓jq>UetqIsc"U&[K$vTt3IdŇ bdQĻdS% kD?`HK^z@O&㵗f(GMhn&A?c[6>4}s*vVJϪ$'5JkUE@Kvdtˇf$ٿy#?p N7o7^zj5czB2q7`y/IHў؝2wc,A:VKn5D~X)l(M+EW2ik!vX[!\}dnj󒬍DP:)<)ij#\HҜ0 2'I;@ Tְ2ۧ~ԓ%-zLcf=/8u PIB!xKj.Դ(߽%UhzǷ}yы 윺P%ng/Hgg +n"ܗDґ]3nͳOQAK$)<;SDd^瞳yQc2 AwwnS ol1"hb jBpp*n 1 >C;!cՁ/_=0Amzf-E+"5-]|Ob,X '<+n/"_o/ow9˒Z# ߯0٤f_X51}}Kj|]W۾7":B݇?2)l \JH@A^ρJxc5rl皻on' ٻ͇ٷǑgܘ͍V?|uA??k#})B 7Ѷsך; ^N[M\[f+jA׽~+w*q!SnTv+jt6-vۏ0& @B!Ajǚ ]-%-7,8CW>_: ~Z!Yy]w+a?Zg97Hg m+ G/|~f&V{;ZuǑeoغ1-?'*,7#;g+%ZGǗwoC.8ڠ` +7Bw>s&Ns LɼޢN>=ax*;5L1phƿ+ B-}> 5D,gxB-r.1{K|\?Ɩd>1ېQIo/5ɧ9UNׇD6iN 9c+?e62![{+2ߪoLs(+a>0ך-7ȱ!h?&{Dʦ}tK_5JG]/>s_+|EXp2&6>=j䞋)%C?W]~_w`nX{!(.F=|?zBO꬟~bq=+KOɳ?!=CV"mcq/};c8Lx@dv҉+3-}|ʻ H%l³G,T.tϞ׸tГWdSa?ioa:k?o[q}琺x |SsogL9ybk?>/d~?6Q ~AoG=}xb*~o +ߥq<5vcfT!K#Xy/Oo=2Ca8WT|&O?Bhݣs_vN- D_Ą|Xiyq]Qļ&r+}-K=dqtjvg[13Fܟ[]'(;r_[Ǘy^>>?TY#T>r9 +=LAM}0h''WږT_!#M:20a%#ULWi[7=PCUP싳Gnɐۿ9^Z +x6ވ:_P`pbLΚ'ofm̧l} ڶQ "O ~3T/7KM`zЂ ȕs >޻s)6_o0'ٴFTRxgdkup|cO촌msģNn)icaE:]0h»#6ksE0>)1gKUXC+> {!p\\<*9 3hցVnLL߱>:CT{뚇cCkr ƚ9ɍ4$;Q+;dIWܨ{-il$hh:xCi?h)؟6J]&Y>BѯDK9h|H0v}?DUR ࿅Hgʼn0rq'ru a/ͷ:8ǧH/_n/Z">_/Z4It_טr+#\Gt`nY=5R +0Ѹ$C ˟E?9{mYx1xS&3pꦌ>/T#J*,~xt?p-I9+hoԆWwK.X#$GbsF޽/ۓ9\1BadA Q;0~Cӂˍ(w\=} c;Q"642 9R6.^6h Ѳ- 1)JXLQvt*zj_:oDmsө<^*B:zdrji*Igc ?o˨A15BqojOpq`<$.8HGu!{w8g3\HŽFSwF!v&B\{nxE[Mzg֠NjKDg?t푹O/e#3^L*fN'j%YQ1Lߑ\^ȼWPZdZȈ"k؃SH{6zԼ;UH vP}؞4J= + +x( +Rfyf?PA=ScϞo$LRĄdD)-S9xgqﶔGS;Ռίcxu"ᑤy<0??G |qo#w7?arA-?(\Q/eͩOpc|$Xzlɹ.ҁ桲u|\r_]k)ɪJ>擙$Z015:[Vql]n8;+IƋTH5!طa]zǰpxX6qQ6{gyiSXm??뼑E(fBgM]*3A#|[D=>`Ʒl'r{F/KΕ#5,prM}ZcΧ,[tE_WtM|{d^^bFzyPI׍#O_8c k>`l^sP{pEWKshun_>'jP\DwrCֆΐҍ:J;&!J#RL{3.P673ۄ̨˪tg4YglYK"TcO6Tc杩ĬayҖaۺ8ԩB7ŽDa\ousE՗W¡`3_A FH7B#6֋\gQNjB.W="Uy {R u,7nqJ [Z!:d\9ekY}`/]&T nSkT 3bcߒo? otGI8tllV#%p\^qmptRY |hؾ}}wcmg#6/tzFyt'516`=;_mЧj +ԡV\74kup.1Zf6xD P|G<QKW]43A|?bOrHFim D4AgMlmۑ q`L#GC M:to>~v!d[M U x!PJېo/g!B UC.畕-vM @W ۈ]GfDΖOxGY#_cLz>ES )%:zQ ,mkWɂ~վkMM-󐜌qlR8Ev c + }vzZyUJZThZ,7"Zla< Ͻy_!W/<<=s'"vlx΍Q[њ6ĵ 9oˇ^蚵@萜*U;-ޟvu7wm{W,_wrz dk\H >y?^ yܪKtO#r #DkvX\1:(72>l{8#}\Zo/ UzGr0]W|W}<%\ m_Ç]z|^T`yN:`Ž񇙳gUl-gVW`fo=5iuYF. Po+hN:#@zʧ[OZ}osgm#d3b \Y#CMIǟˆHlet:9iIf~ޚ[wy>+لkN,0wN,y[Dwzʍ-پpE][&N'>_Ѳ/o-7wš:~0|0y/_Uޞ#ɿh/M:^}ׁec_1}~NP*vnr32T3AoS>;4+fZ?=р*F< Q%l +ғ :F=pߎ/7c}ڵ36NmŲuR,3aCn>:WL7~t5릭Oub; Mi}3 sA ql̏߿ϿǽoQo\RÿQ%2O%Ey} U g"S{ +ōtsI@vȩƆ2.N?<ըe1cL)\ _doR?K‰$`Y|5ƫsxY73ކٺS*ZpA+Z\C\S~je&EXw2H'O󥥱6XSDeW n<:80VR],13SE?,|&\k8/as6&r~Rt'jv BIn<5kak /,:XL `+Y[) k1F ϣKY`Q낚=2N>syH;hf?gfбnHl;HGxS$K}}l&KObG⼃{`}ݗJz .΁ò}*ndE~1x6/t L)#HUL 9mௗ9Ѿbmlяf,&wr NkrTa9QϘe;XYF~l5xaİNĨvĸ.I2]@'<~dl{^*?d>j_u3oҡ]U dqh|4Y ?tΙ_ӫN4&DktD,>}ֵ{^y/~e_ +A[[OM޽97=#/dl'b;oMʔ=mG:d_˹I>qŤ L/kć騖:Œ=ӭeԤzh:h<шɈ}4>y1;28 -%d|DJMٵ`2絡MHVǑ3rˤ C.&#2ht1TiPۧ5zUlf1 }Y=`lA$yĽ2kvº/1&|$݁QYUdžݯ޲'8~A={[/\.=X[E 88m> hDz`س1 U w!Z +FT=l_0nx/>vc ;o7mϕCynNg^/2?2Dr$"To:U$񣲣uWLx1 u\,ng,9a0Ԧ2N' {l_G'~z9Y$ۓu;ZTdZӅRf}|:3u +CYF] +MO^yڑg#ئoQ]c{G'R}T2c:椇yQMԹHαY'^!!9I)t/G9{5ҜQл_`Kں-}w4zX>whaCIh?رjERB`j1F8\q=MFN闯`~FN'7mW?fV`ˆ68]nUwlˍfyb]W;,uW{9r+1R2zL՞{썑TKۯʋ/z22UPS]%AތMl)Jt]{2F8T`"u0|:2kcsV/B3Au*?" 'gLgD#VRkveҟD˦Y!vCV R2jn`9#F5>\kvDܹ16sbg:euMPE 9sqUٳ{πw[41d:U{;|w0z"+[\>`, ?2%-=c;6y1{ۆѹ;5x?pO%uΎ|Ýw&gu Gjz}]C-UxKчKhFہ={T_Gͤt#j9.jU8S"YhZbY:1DRJ*BN>A1>"~Aw<`gd>OIc0ط6I&JaC0lv'tf?iPLvQ݃椽Ǎ.NnUOV ƦI|9F_~]?Rs_x!f%@UENQ!Z>?)WiE yo=`I=k4LGHX*7B}S% D_X T)xc[;2{ 4 +hA:ȱAIQ3 wV:x^h}Y&jpaƸפ㣁9kxGKdC!ܶLRv-u/C;qZ4 z95zCr͏Pw).rWP{(4f(N-VEҼ*iy +:4J`G!>)אYP`) A->gJtO>wmH9x/#o_!{=<;wju?>3o_jgGv]lDAԼ`q(ح78&ڑ|vUT߿mã˧fׯ,]Q۽q٭DMU;oCKxRu s#IA9Jmv](Wy9Dn7ny%ixaklMzk.kY}l:仆=cY1. {~өsA]U2` \GG͉e(Z>e߸~߆Mp4._tO49u'? +Yc{ttwH-X b)im_m٬DC=nMnkPyO[ }W7o6o3~]>kZÆ6^dΗ8fTu PX]?dXy`^JTEnlħ_Mm|j1ih0gLOj$fC?oL}Yÿgq01u~%Vu|K@* jΉÎ1ёJ":qDD02ol%OØ /\ӻuyG/@"o;yA5T5nt CE]F@*nbm`5_dJoskf/F'"T9mcOɏwlmϖmGE6o QYE&Ym':)f9n'IJZSe멱m2g&&sNDrmKDη:~Q]c%wAQc˦GͰ)9+lb:?OO5M޼c6c]ac Tl=ZЪU:pLdu +S= } +~V-uJT" X{Y`ĕ?ЎooL T^ +Q7k. l/X]!1/Pu(ow M-o,OY 1F$lP䶢h%]A6Υ5*_T:ue%Lv;VS5])ϟ8۫ ?{lȜoyҜtN:Sd +hjfv)$E[.SOE-Ȩ ~ C󀕏I3n{RBj?*&tC;4 -gw^agzԆ@zxtubqh%{V¡>/;t̔vtir[녎1:qy=I%?``߮V$iJ*J%  =p'Hg7{ꊊz̍~bEaL® ?)i~x$ c~=[ ,I3vqim ;)yZ>vI2Mbò="[I/S  q#`?`wJG7#;#=`ďcG9*#G\cȹt7wukNT[_﩮Ƞn1$ѷؘ ~* c5TfX)|VGȷ[_3>wtp"6ժc$o$Jͻ/Փ*ydҡ> +F0LwaOs8}bT(^ⶈΣ@欆WdG"yN֍O_bZ3 "4oU4 rKqBFyW zn6Md\($Z=el:H8Pgn~DC)_%uI?WmLn54{jٗ7&`Ϙe B+˷*'9/pK @\/\J@CD̖B=:옙?S#;46c+Y + ԑtTE?Ml(gjژ$>[7SϵtEr~3BWܯTG䏽9a?q +}>?kTin1s]J?,`0*Ev ^Z?taS-bLŽArsbDl[تZNͯ69&sʜWATcڑVs9¾BEayOsУP҃%^(:&TV+[䁾I68DvMe7qaݱϘ +^=rze ҽd?3Es^_9|y+v]$l*ȍTz`x州Uj{T_~>c>Cwj}-{ss׷LMcsL8rv!5G2G\ڇQ坭fܮ˓SۡM;a ύ}} D >g&&UL??vY zK~gueْm8 z9|W=>T6,91k7 +׺D#t"J_Z)—~"B?T=fuG~g&}!ܤVM?^-}pI{]?ֱn<Ҏ_;[6j:;^mxT#IcIXTRKoo+ӓ鋘ɨoW`#@E^|(,kK~H 7Y笶J|&QjřÔRaBzQu3AZ˰s;MP-5V[:{bt&CD)'ӍcLk`(ɖHl,͚z)7܄PtӮuD>o/`<]2oNۿ}tOp< +ٝd*(agY)+&4u.Y;uAܯ({CJ|KBqC>hQ*> l+O Wo%QERւq}=,+J 8jY$H˿m%w9 9az? ` R/Gq~Q4u/Rx/ѳ}Y$ptQ}$KH&& e2/M ڐ>$ӭK&yEvt$s޵Ura! WDf}11#Xxj %[VUIYCXR;ѿV#>)U\-v̍n}coR35HWҾмD~YIW0>P{ +}̱bxPF^[X\jLw;TB^EN-s~s#wG[4yr}|9kزܧ!$ y%b0[)ӚsݡTj 3Nr ."D;QklUYnO9i3FE?. A_gfeOݲt4!}1!DPx|l\8響&Oαܾ7< ~[`,ԄϿi06)g|Ncl5+0nh7cex]+N^[;z5&'yZ_Bu G]1'!9EBeH(Yg ]e&}`r |D?&\gn6 Zry`k1&}dri4 +q?dk?s^sM\!WN _Y?H^<س_Ը imZ}ƘX6c"#݁T c y>^G"{` +:Qƛ-<[+WVQkH%,v1B~qj̕aC'䒮Vll1' g0 +~pOu^"׋xdw&l׽PO[EߖÔ `/Zs:8*"緸,?嶐ɮqm{N vmn.NgPM,:]}4r wkV,ޏ;Jzm뭒Z-'係^=hDqmy]ҦUjhgrow{=,A"8W<R ǤJJugqvÛ_ Kf{u-b_OL86TjA`"D,op#M'VqKd;Ĺ6?"gWP|su>A Ըǯ,8 6xG +NO_%4k-lonq{Ǝb$1w"c5|HSVWڏniѴȏ$$YEd֔*/g̱&ۘ,I;oavC۹ nO|?:q YyR݋H3WHi7>E| Wy8 ˖Ɠ/6r??4o]d,b_?z>?zO3x eU< K@_ +tެ:z:}5kྉ7Oh+)W=nkӫ~~l\ҊCWٯˏ]k)! dw-ukHa1Sw+$3#&АOj +ز p5khlw#Xƪl޹0E|O-!<_>R G*y$ {p_Y.ym L?|#lHM6[k:AH~9_.#6tn(ɖvmy[?k#Z/Dl?dOx^n@-ΒZ+ +=>}j? gGǟ9^X gؾ&m˭Q9 Iݑ!O?v '`?s'}ʉt1W7uѿJ_w }=Wцe"T<>*m+ {Pk8[ +=8i{{HX%ĽVѦ2͠7FWulX+V?TZv=!PQ˾uE?<{)56[?<+LHI$g,Wv oTB _py6ϮY.P$:FA?Z! ]Cg۸P9Z?OvNވ}âۦ5cٌ1jVzcSǯf rm S>bB0``v+`!O緌A`0A]n"e5mQ`kwgels dsI q>?Rfl]@!1*YîY07_.6Qӕ$ :H&j#!U#0.,'|kc<U=XO$13AWgӫ?/mƲzTXE/4 $+~IMgN|?yvIn Sr gEFֶcb_E~VȁTSo}?:qr0{k;P%x[fKbDy?P y: l!:'ӟqm^p5kTX'Kv|C [y9|;z:XZmRgt'Fo N1ދ\itn+|EEGB ?^OO)_D e]Hez }"w.ņH%6s4eN;),njf {|M&.+o)E]bRP dI-1A/0IP|srAy;ƹ)w#7'~h+U -kKez׼6^]}^wa^ V=D6}^:l-uН;_\S;^ ;:'Wa*e!ԙdj8KMW:q98<x 9鴿6@__zyyI?u#1N3I$7 >G;كx d kǴTݍ +b>x8*c,C(cy{ql˜e1mKy۱?#Eu21?Le^הӛFѰ9O}e~= +Z_ +#rqOL߄ c_=?iʹ7^'~n" UJi1!qAȩT}4VtKlyD x;G[ǵ1ߺE/7O;IW۞ݜ)_SJu җ.8(~L;e^C_?։[aw寶YcNŵ9mQռDҫIߊꮏ\^q4β˵iT= 5֝hO-]0He !.j}K+ >UľeeMwS](sd$0bOnz\9ߠcP$õH2:I'/(lk/G }:rMvNtG,cÖCSE7bA_'L#N/|@Uf5`ӗ?W@ЕsAݍGm2'9GШD-y3zdDF-3ȩF$F[k +~ XO3Z*2 _C +Ps(Rd +> + -w9}+:/1bWBsL94Tm+jc_:L6YtbWfZs;ZnXouId,0fW`EO[4C"[j\.Y:/ɸQlU?q?߫6cwC Pb}HT@OƎBZA(ljŠn &R]݃#J6DqlG|~j;#`׫Gt$^]+?xMHюQ.jĆce19[k!2kbKS$m幖%^)>>؎g0STv/>O7o83sփ]3EN9ZMΉm'n>m)xBnPq^a?D7|-룬ϺCx"M\A!ނfO%-2nUZqO:^HfUvu`2@?ؘG7WB~{ٴ.Im3l{xT QmA;'('8IUj"Y3!a&xD\?.6(2NCN^Ȑ/G$LZ ݦibObrMטq^9Þ Bq{#,7 κ^#/C+>yd5nx*>Wagxh/=c_PsP9enġ|rLo(?j+"'rN!Ȗg:.^Sg~}p78@`B]VsU37Dkq鎱+|Fj~j4ˀ1%_(:`.udRIDQaÚD-jK$(?(΍rɢ@{#0xMv^md_95{BmZ83Yrs?9xA0OfFn bZ ).Y\w7ƹmÁaw璕 ]y/-:̚KmOر576n\wf(88 ηQ+8nܛ_ +lA౵!}r-rin6]&WXEx?T\G}X̕ P{(iǯyC*y;TuL|oUEQ-~bNf9d)beKxױb t ]ӐSC֤σiK{}%zTiG䳜c zA Ɋ..3{vw + nK8ʆ,1u]YiZʓen&OZ +RO +:t@%69%:&eӢ2ԐfSUJU*27]@WT_Ju^/oGQ:q(p̣vtyj?Pų,nh]_^[Tٛ7';{0|?%#ў pLӦO{978+'"8aj]b qn=+O3UkcQSv7ĪL-H1!$1 = G/NO7?) ;j`l'By4yd0 Ϲ?'A:ߠ?!me3\ |ZQ{>`L=>?~UsK09\gٴfn'B؆3|Sϫ D]%'X;m}.g^.^Sğ?4Cm)byuX-}eC##K Qw׬Dy$ekw{l;8ڵJΫ;ښ`={u?Qq}?N%:z˥ݱN8 QcSڻ; + pm h eP?}¼B=_4bg!I-[cwboxӠ|TBl_$S:jpR!9_۹ys /BGl'Ȧ9!q@㋻U չhc?8ẉ7ɣ8{uL4ۤ~'.=N!$`C"5N:e1q!7K GOr4cj5O@ ~&P7Wp^QPHS$Q|J'>/~ρ }M8_ГwN y܉w$p{=w` *F87VpD ~xhD)A39ϖ5J[aZ&[k"[bɯ@6Rعtcp?'*\1|,yT6֊!6w QK7w-3{6Hi`y\ Hh R,=TdZin).R>q" W5B\8PZjwt]0j=̒ )03nsW-5an?W_B>| X#R~QTWQJecZѮ D;ƌݮ-bDmss+ mA]ޫGlOtSM;Rqp幰0<#RK/dׯycc'6n=}L"N8xHf?Lg=u0{Aa!81d:*io?ʾC<0 R9.~w{a""E5j|ڔc>D/G4! S{ 7=ڸ`ny%ȷM[ĕܟGhȍF +4S4X>@jWXexA)'Z&٦ۍm+7hv- qX`f&JuodG1% "3GNe3Bc}.h7D6{m&\ + jl;š:6>sb׿s1 |KLҭJm/z.}<̝@x>/_qיGa龨mJ~xv޷&""RUjqpΟ]r3Pr[l#}ܜ3b)\^ 'O5f?> z˯u?ξ%>D wF[t|G_7| ᵌ[[ԚrfN|NۘNs=Ԁ<&_pϫfbj#>׈ds:^>42*f?8УA{^ ][2Bְc6dtĚ &#_7^Bc#r$t+>"{~IF'W;fM `hNfXV: ]_HݿN6 :{ҦI!FMc[-K{ǭ#飦 12 )ԃpƗx-VcUaT+`m? opN<9(WZR8MGFtg/6&]]C?Nu`2AMm5G + 𓡽X6-yyۖi3W.`-b8~D] +b2C/>=4 771Ԡȳya Qپ78WH9Z;dAN&9+oln>b{gos@3 GhG|{tWZTe.[V,-C 8? %iO:u<=~~r>by^Gu:'={vop*7Z=IP*0VeK+\"u4ۅY +sX+Ň޶}41ʸ?Kr> 8k˾r"u3V5Xxr,s_-mJI=G3TZ`3}hŪy3oД$h#"pZ ư{`MQȗ^~D9!63wz/8]{ކa2Thډv6Jg3|Tɡ8 Ԕ1V]_g%Y6$<\%-jST|cdi>y8^^/T=)h-0Ǔ'J$ xLПFDe.r8Ȟ-\|Gʨn8>׎80|x1چgN[uhb[ٞLsxs0=mL:_nֲTԶY?=‹ʼn ?肘s_i]ޡ\ί~*}~ħoH{CߍQ,`Egwpq֊D 6[q%Nxkחub8NYSZjKD`xM۳'BzժhhjƗbYgAivtgmwxQqjfԘ:K(5eDi~ÇZB/.aZB{r|?-E?? A֤͇?8ʦE?>[⏲@~'#ց%:Ņ- Ӂ~X >^"x_Cm9᏷ȻJgq᭝׬JTY̴kmR>GpNrBo:u\Q ˞MulkM{S3"Z$S6d| 5L̜lMˣқu/9>ٟgJ;"h 7䉽+2QUF#P[c̛Չ!HGؓڢ>$s#p/COքx̯[R{)"[DokI1bq9m!$.{3y3!.r + Ǵ< VL<{CtNX6epy`jt84b&glMR2δxO_|YjY e~/|O%]);6bƕZccʷjIa>}sñ=_NA۬}ŊcS,P1Zb٧s;I O/`VyBX<9H' [2%t/e8^eU-ߎ:c3HobB,y!lqZzAP*2_bKl +ӴCMc _q~Q?)ƂNN<&ԉ#)Hzt7#{_?1Hc#\CA dw*468*¾IԺ%Vdי5i_Z+Vo՜t&NZpgDG}Fɻ @͹\jA=SVjާ/z9)=y݈4xsp8M}H!#&$JhfaN$ +x@pIK`&pu7pOָS(|}bw" xaҷ.x۠uNs:V=^߯_?~عkcT [8AL0``wjcGY{my7,evھ9kKkWM_w?fu_UcW՗c?|#1)b'#GέPRZuS<<e Rc2 +T51dhEc:ꫵ+/QoEz7>o:x 0k-4JzV:&vk͟eƮ|s+gpc}VtfP/j mG=?WD1x$ohR%&.FE-8I7L|w|/4X3LG ӤܸML*jܰ8,=D:rځûp,||9g{WݢRnob/_a]6JDLEd~:e^G(OH$n1uxZY/XDHyM|- {KǼv߷~#d9ҀV:`]8/:Jڙ"(d\wWY޸.}Ԃ "3I\`eʰuѼTӨm Ó@ISCtjCf˜coyi;Slowt񱱅Y/7kMmZ;*{^\Ws>tzg $A;Ch0bP,h{gZ3+;u[`ڿdK̟e:G;M9jCsuSMtzmy@|mȆ V]x/xt}^ԹDf,r3nߎYO 1št~!gKDQpPK!!cCdKqrM'?^Kd˜&%Mާa}hVvOg~õW>0>uHd3ǡlNbcy%\BUc;GSۑUE ;ζ53)㯶5+!x=!Ujv)NS\7gV0]49ٜ7F Zyfw>'_ ÜfӾD^Mv4Af}s ?v7[':wm)25ȡL7Xp/iXE'儜*BC@? yX4ې+Ax]VjNQy1=Ou_Zyù?x@e8@}[d!(` K +Nm3֊]wlG9ކp> ,*p" 9QfG_ۓG ORFeCj65F }?WIpM?ޱ]UާQԌ jxb>h leqn_(*a֋59 +~/ƾOC* +;sYf62M qb_.X<j]O9Yo Cy?a€#T,!MqV̱I +gtr١^7E?Bcl!ϒZvHUy dU&YW@^πJs~3fE q34DQkѸ*/ߒ{]ҾKyܼaofwT9_$~~ٻ&۴ClFL"5eSi*]yΕznYI{ + ~֦R{:MƙF$7='pȌ;U1oCNbzϛəlY4(ϡ9/4/ܱ) -W?jOx=sAVsMYy>Rkclڅ(wg_%m)[ Ki1l۾[=ؿaMIfr$!{_ς~G: G;>R2ۇ?0.72>Z`."Ƴr\62pd3|=C= +\,6^veQ߁/B8 ҄ǏtBǖ|y:$3rA b*gu8wnj:KGD=_؋8D-)͜e_< grjI=8|ގB5`k7g4QΌ b*^pQ.G<ޒtO: 4@Vht@r2Y[UziQk\<~36Glߡ5"լN0 T?L)}J_' +vӏ{~kc+++x}OG_?(?k\Ǹ3n|-z>n_f r," Zn-K~cQ;2ƖF AҺKm"gDʸ}gotbgjf4T6M"]l_~:⬵6)b.â2ͨЈJ;:\'#K~eu榅qew1|a=]+i#? j}!cAy Oz _׍dtՃ82:%s2Q׌19ިqZۻ棗cV R5+&y%ps1EXpB]7'm[q\G2}LjeGq޶+̏TenhIdK ynOjbtЀ9N^+oLD{^ivC/ ?0=5ƸUw^WXΝvق8d~559S] NǯgSm]1k_MYY,5L'p ښ d_WDζss({Z_|6~ V$@Nd; )#??H{o}Kh|}\[_gH,MGlH{q!L#ݱ;OOj,cȩ^F?^͝ :) ?q(h wXڄtF#.u=@ņ&ѐD.]qZ[x_?0. kKAx~ضщO{jr3GW6ѫ^_1ȁ"qO +g;a"Zc +Ԡ_߄?_hQx=.Էyyxt`cA='Mt> L?V?-QJM 9vϙiwBi-^;{jO-owɁП='d[B`I#U=,|_Do6u|ƕ^er9l$y=?V:XMm2+I!4I؏*I5;DygQn lk#pG (2=kwV࿺3tAF|pggIȽ/10 vsEӞ2}81HX8ssX# 7|A>׸f}Ե 2)C ! 7WhO3kOoc_Rޚa| 7MQ3qu8L5yY+icqUt00z>|$__vG4< Ώݿ൸T疞 ,&Np=P~V_BolgY#GurÀ|ucS˝GmL<#fN!{A}#ѥ6֫~k@' s׻9=oR3{Ny>TZWyyh5Ry`|MB~ qƭ3v>c.稨!WhG+:_0%pnwe*-cPpzVÐܛ嬳.AWbg +rCY`Oxn͚i/mR3pyBüg7"K'$t?}!>zƆpRDͶTͶ)H.nXYuևb j2WF_UG2]jAzw~ݙ؀[ a]ڕo0 Ob"A\^MX`%ԅhگ_G8+} /?Ֆ% NbL?Z{Ѷ;G?Bwwi~9jokqڼ489;_3,xcn[#?2|kA_!> @tx /KQG'|wFt;5͗D2H%q qVr#؜Bn7o-޼3m.нd7j+<Xq>~0}܃ou0xei47o+=is*>Jeσ^+ RmmHޤj ;!E!өWw+?y}>J6d nM&Ld$M;k㿆_9u}`~` +cZc 0llK9=D.|sps <K+_7P-䢖"U)E͸πSb8\ٞ߇gbz/9Shΐ`NB,1s o/GƺdeKL`*27$:ql]':03s63<&)ppfl˚t25 547Zry C,j0\vxGL?<4dS735*p-ȶ@-Tz<T' +xۭ>'h-oB zomwa|RaUJjMaoz T[7fQ-]˅rxQ(jF:ЈEnp~=·x+7'n9R_yq`ICӧ73dvyT5(-0^7'zA׵C]ɧ6&YgNu*  1]95J=a%E &!Kmr 8ļQco`n|y ':*)V?$Jz|8SA Ah&(ڇ*oCՒȬL޳#J:˪w֊D%MaR^ վnOW\Ƥc:uog17||5159A鯰Q+yZ}Mg%s?o 1m&K&OrIfLGm^c{ӟ7nMߟ9 稽pw.@0!0H CA VDž+(!J'} {qtݪQuߏJD]c|D^l$n!I}a%ۊ";6nqDۺ( + ̣h`!:DOE6sEaq Eˏn#J$ +xG'Z! +* +5IOw_hu/Kv0HuNT$o6Vu Ͻ[:>H>);Eٔ[l5?Rl󒭲o/b :2{ƱU,=_gcAw +A H~G|BlJx\/WɴΒO++bG*>r;c uՏ5u;-Ua5%Dn2> h}1?f&yM1HduEe άUϛ} A-VzQqpHxfa`5jGs_xbehsF0kRBgl?H6 p}qmqĊ=؋e+-VܖbZrT/:Ȩ,:QZI^|2ZH1 "{N2dm{߻9>VaI&=-/i|^iv? H3WSCwoBrŴju6iĈfв!SlٔΫfMMt='3'. f5rfjkQĢ /H0X`OsD+6c|˶lACFhG3NFnbJ/Iֆ~6|\lQ|๦ D)uY7|ozfd>(xngK"ޤ@:.[\tq=n! +Tt ξDl`iR2n-Q[]V?[Q3{9;XBAONԶ6ߴ?< +-?s}Aҩs DŽ8߈60q2}v{7;^?.1?-6'bJB;"¨)/4̐/J?:DjvlmxӌCC_=4j62#&/RBdݤ3%\Z"['l퍻ґF. PGo ]͌DC}9gЁDo[ۇ<رT,4QfMo `_ +^_o} wMF[Mdsޥm1 +y8! -hojCva$ +H$]1d`ݝtIǩ8r[gnCz.=3pQ9đ;מn֍ +` }A|ag@?w`}ay67q"_t*suojqӋ^rogBucs.y?9",ǥW |=?zJ+ + _?N?[7^נ 0"жt?Pk}լ5sfI~7}۲_i_ +պ91ЏD|V0cbt:%Ԣv@)ĂT(̞ +#2oHYLݤ5\cdrē5&8IhsX:_\ni[K}4O*cF-+E}*hXү#KҼK{k[Bihv +}.@.xC,MiX&X>`Vf3?ncbS,d¥߶7f{YCc c:mx\u#6^3=ƷAtVkp[`ϓZcM 19/%#W=Yڵ- _ގ8ߪۊg[ȥڊJEmZrqxƥJ͌ɫ X\L;>ԿYD.̠vuLj̖Mw~w~%. n:*lzɲbI}jno9MM"WҴwvO|xT#7p+;p}(_wЩn|y7,/=KZWBK~4Vk <.J+?4񙒺CQxYf0]F +mXoaD-\[HW̞@P%wtU(n=B.h{45~E{qéM$;.}iu 89Ўpz2#R?ƳЄے5']FͰ0 EQlK=s|ek2O=/;U ׃:QLP!KNlvݱ5ow2?Mkf ֬fnן۶:|ճfqvYjжQ 0ܻ8E<d i9e!dw{ߞa"~red(b砬59oU]ieP'E]K/h|\n/wu}jm♃xАts>؏>thp5^cxr_J/}ϒ{>Qz u?k]Lb i0K"־2[)sXݐamO:գ9GTMLD{㷐:~I>ch@ZϨ +u9?er5r* 觬6Jο&4j$ԤK0`E+5w 9Gb,9a!ũINpզrc5 $1/kt +Sy5!HNcW.GdQ7W6 obxݒ {Ӂ;*7uN\?㪖?td[! bn /iS\BWl=<ֲ5aX_ƱzgMtV~>E(54 pZEmDlzpJDhMO7.stP nX Ott^ґV!9 n҉QmrZfՖ??qs"g4w)*}8jI0WCGR,[n6/-狊c7ځ߼"e6KORG]t~@n$ $8kjkpÊȻR?r|&5{Si\?<8g❰Νm +<0 {Ǫ7ӥ;7ߟɡN?Ml|g>!Z˘1[][]'NNʰ=?9iCbBfyv77][A0oVXy# RN% "wB=HyM:~k ŗrL ~ KЧCsә\G[w%>B4DY1.2ט) {(nX r滂ٴ@s}}ݠu3yԅ`qA5¢)-yI,¹]O%x|CE/!1ƝX1FyP#hL,e9)kRXo/MvgkD `w^bȎ5AQ<Z &mӴv[7vߊy989xI*9X35|FMN|:3Q¾8sxm+Px9L: |j*jʧrxW1gb,es8/8cɬ47' >e YVrNL$5!Yܿg";jNdfp|ǔϷ:Neڗ;fǢiS:ԂD}9sJ\}yWk\'~r=f\;&I3)~$u ΍MMq +P챭qPԹ݅!Йg\\Mm>-=\7YO΅Nb*_Cl?kG@ +endstream endobj 35 0 obj <>stream +N-A .۲շ%qR)ox)` D +أ {@ˋr,P~} \]fpR+syV42*9ƑuQ5e FPٲny>%tw< (j۷tkN}KƸFё eS3,zušp08>;j|~܊ A=:漿ޓ/{|7{r2LZs9HӁ=IoOw{R5ou.ҧ4 +B3@:Ķ&1?TV*k +m^]!_lス6.ƅTe˗CǣW'_]>BdUSe:lk;:釐ᩇ`d?+Ktż*M p#)CbԺHmdGڠob9,3z |Sؗ?JI_lyKUq|=0J_ y|6ZCσ(k2(DX݂)[ hY~]?lGǀyke[]%7ɽ ӽ`y&րՅ ^j4̵DrDzT`"kk|z.WeOUb` ^U/ʉJa9umgD? W9AG,_jjQF? V U"Pjx^w %K ͷN (GWW]$ m%k<t1!];al$By!Nm!]6%u-s oqQ Og}IKgcIZ6`-;6#Q$Xl#g%TW܆̰ӖgM >55Irvº'Lg XA{y  x֢`C|V;+vb8kNG9mCzrG=BNyCgPIKbXo|~Wb[e9V_)}r$3&8y@Mb*ڸ{uZ*j7O*ߗBGH49"_6SSw^_!.B[;izEwD]"B_0|7(;߶Aeg֧&'ߩ6 \ܐ|jø&U~h%b!0|^[S?>1(subUu r{Ϭ)%/RJEbjWyB)0tD?_<@o}HG ?&އXvȫ})Whd\UtXWKa}7!ߎ}[k}Q0%|U~RE-':q}x爭?m-@ߨmٚ;~Ftj0Ⱦ${(% h,>oݩ.MuDB #^#:ǰsa³O/x X0T94X܉SdIB3 sV'7;[ }7wa\^hMdlIwm֛$i 2<с ~O:P,!4F˾]^mx.8~m1~ĭfJT*b'}׿0Hb,.u+ç'u ?07~TKh+ks6z;V$Uv r&V\b>u5Q_Zr6ڏu/}Gic!C8=- 6uXWGb;ᛔך>ڏ >aαR_xAKlӗ~qE#B:ԡynR B}Q##̅,'\A M_\3pM Kd4fr?ݿQ3V)")mrhr+2ddԚ`A0'" sS7E~a!ah[luXR@)jn^ 0]WC,[k = Co9W/`O:ɃyON?PlAbc@6Z4iD OP>W$Q몍=0'Cg`r"UPx>wqk@s*7L/> QeH<♓8ξ~&3?Dds)8n >9O:ʺ>Og楴/.=:[|qYyqs3^Aæc^y}ϛx$xegnk1wcs3ŵw8טyexΤ!;=}fJCmQwAɫO~b d$hڴ]{>9׿In[oX)6R͍Q']Q=/!?1[MUߢ:y-k!GaS`57aDz:?Yc!?-^,,i`4(?ʴϫyk맬eO=ji槷BƜYԺƱwOꚛ0HJAޕ럆?^_ h[[V[m 7#opY'ń!CF?PvTlt.AakW-n0<o{CU$pC7d5jBv\ F?Whp8pST;l5k;9Yc3giVk(ñ j4}:+&F8TDc/v&7!9N:Js+)u!R5,O_'R7L͠=w_v *eݸTw0xfq*j9dIG9OiA}Ϥ@ha-1s+|t? + ʇ8މ`qYc8O6ӵ1g`vl ~+[ҙr۪EZO:ҁ Yyu S]AwԐV&=`xeb>(e؃agp@@.3;s#{>ᏼp呿D8b70:_]W.hh@Sbc&.6<}pP̜ʰf2xE?gyuy^k h6VoG\L~4|/i 6.jy^Uv>EP8#;$Iەt~uGy=H!j0a6c %w蕥۱%,yN).D +еnΑLH$jkN֭-m;8|c RC1'͑f5PĤQ^?`,ZյY&3.)pE#ǩWoR5+bEI49_9aQĎ̟~qԃ`^qt oO +1j?Z4P lw^9_@q +>_xZoXw7Bt_w蝣]&c=Gshyhj{]}ic6̮ˣJ vv?'負opqՎ1q][;`EU-`wP~}yC_c*M !WI6ЋR((]A!tB#H((tH&6!AJǼΖgO?g暀ɼ }/Z`Qy|0okmmrU?  G +W P%)LyCHG9d@71zm& "5 du%˫>e}Gx7 YVγ`~??33F:Ǿ|﯇] $PJNuTT>'۵_/ݥoKt{,CK#B'!#|8rh,F'BճɷMlaz܉y.O||!<ȶ?7JΧr 6(s{՞(Оߞ)Ǧ|cٱl|R'4U7"x'~|bXcuGF`'57&);sҽ\㋧}Pc@W2nӘD<9RgTJpޮ|p#Z,*əǵ_Uּvf ??ku5zg"󕢧XY?Oߡ hV q1ƘiA.d|D:~G}je&\nyؿIӪ:(RNgϞdF=CY08ymr9cxːKd 28X~h-gk*{|߁d +8܊28܂&PN}-g: 9왷!7AJ6Ճ-{du N/bJ?jqgfߖ `ܿWZ}WC}A~|>.bNe߫?Ǝq{DmM[0e?@~)b=~za9`gcݰºA Z;۫]z_k+~@z ڗ? /Bn3Jb̙N1%^;~(mEd&o}R{QLJCO"74OJO~z Y43;n$ 3ITdUMM =.]xP%̏vj- SN|ںH!yl8Ȭ̸/Փ<3.F~5Gt[kK2e$M%'8mvZmSPGW<5L9!go39 +>ڷV~<_DCMLJȬߣów +{pV:!Zu) +bh@ ̹[z_ľH H+/(/`mbHٴ6YW쁘~pc@?ٷ}Lvh&_+$d ZUXǁA.jr*6ZvN@~]tMB&FxY,_f߁!NPlV9Y +ek#G6]G)2|_66S>]O9kX=Se]zp]k0.1Ohb_>N2* X +QwUg,+ؓ [ېȺ X`]7eۺ9U Gɩգ`3(ۯru=)p@P>0Φ![a=n);?q qS࿢5s;=s=^?9_Ky]һQFZ[{PKh_7:4vV9Zgs>2JNʝ%gs/]wb^Ԋt]hVsi;18HU`}nO D푇`gDίSܭVzJg %9ݺ&x:4y;5CoQ-uXA26/^ݿ*A.WdG~Ю'4P7([7C> j-8^N.}Y8#~9jͅ'|wxtJg__a,^o  ƃZ+ܜ=;w1DDW[^.ܼr;x}'i{W<,_EO"'woW~eR&YEmƂ!v"*00z,i2L*ϦRrcr˜,ѽ(fZ}@#74o*mXeO~`1}qivYkg_ RvغgoyׯzUcʫj+Obz&_Tde xfs_r+"Gx/W -DYd5 HR'}gG.XI~0NmnEpCmG%?gD&6ӿBNP,&PDy"ZgT$ Q= O9)K1ߴ%3L?6,$jQDV}`I}(WګXt0.Q5~INskiyj +3prOܓ\ #Q6ÕY/2_lOnqd`ǦNHt*GԼ;Jvo8ұWg3 +DmmI${b|K+[pe۵h6bY}r`;}/ˎd˖m݉K$8s6Dȴ szO?JL3Z*?pON9N% _3ʑCAٙ>c)bJuky oot5ϖe]+OQf\),oeW'#7 ~kE~- aYo0^f;u  ۫CPC?ʷ7??I#&ooX7[,8|L{t:ng=7ذM@1«_%^R>9_ hSoO#EmhB$WvIIelPA\` 7iЌ2 ͍߳].;Zv69vˋ{;k AEvα SDԙu;(\3J6FE]1H9|<7^15.{]֕X)=Px ?~1'xBՒQOޒ9ƒvF=1շ쇽$ُ:%>7rc|s]݆>_AG +9`2J +Wfn"F +5S G|~9r/bny<޼ymSw_}&ij}YYz{6 Rll6z]A}/cwĤݳ4{ }k;v_;6>|=PoopNuQ#A5i.5Y)r+Qg܉>* tӆ/0/P/]ϑį9]oŗU EASsO= ~&~ڊf%%+}3}AjU]]Hd叮oep{؅H܊Dݜ ё ,o Sg_]ۓ/?4j(#2{WlIԎxSbFsYR8pE)8Ź3fa3W ݵ^_zDjx 51d6Yf-?}(aK!fV!n~O?-ZG߆Y צT4?tp \5.ǃsf'ןk_F>0Oe{u+.7'E o|*6kgIuɜYtb@YF1k2f8 ecc{DM?-oA. <å &$ݞR13:kݣvd R3պB{SkTcKj M7,"ر内. S/O*:+(y>1AO.I">.ڣ]pl`?p[ڵSyMaXa9e:tU5=r_CN@IƆT㟯:QPԦFZ$zmDIgAt-dQZUº]uW%VC*?buJ| I 5-zt]ت:-HEb/*bX`X} +G7ʋ!m(Nu $Q'm6eĨ:=OأHlCx=۰c4Q38>]zcr??y\"<27u M^Tlm5CW0>Ԋ Awmǫe_\DkuXdV-͐!ʬ?+a;uY̓2RRw=aAz ?GYѼow 4WFK3Aq {+ij-oeװ<LCL~I 3K>f|0K~~FpQ9!w#A#jyD9BG2a~Q5{WΏZq۽H5/;PM o*;󿟱lDۜ?iϿuFp<`w !>̠'܊0?pG^}Rttty U3Ȗ@tSb8B+VFH=9f9%R}$Y1*8I2Y+nC45r͍102yaQt?:=u lceNqs]]t:{:lDeVN< )ʞTPL͐ k0I Ƴ?X| * V!mVcMB^C/u_LRF/hRf:s$;Ǧ8%&ۇ·FʿG?IsYc<~s_]P;"yԉ[Wᯝ#ZMW?UMco틷gUxuu*nW*LceRyq-hk/=Jec{ky߭]'o? 452\3ړ}B] 28I{-HEmߔ1'0>N{&#:`~HΞ욨Vb ~IR$eMalq(OrjX~OgPjÎ={hmzE:J +gX'=_ Ld:M{)zFͷ0{o~C]Mz}?sr|ke|r ˡ[^ZZo+۰v'V8s6-bn94|*@A +LUWWܻ0.?UðgrS^?i6v܎|ڥ?5U4==crIG:.K=y3Uȱn:zfDq0c-EMdßJ{1ѵ~I ~SoӚPqjO,hvLPVG?C:1'f8ũf82 j >h(dCd)}y^:ɾv U 3w0s-b@xRqt,Cʏ;΄*v:aЃ2!&n+ц +Tm>t^~5)ڀ;$YwTW5EmN_g;A A{}܊LwS㘬9rƜO+`b.hYj [84!qtNmSC }#[飍ɢ"ǵ>^~Hy$foг:>B-|0[k *8^c !?ĚbIt! F}'c W3Q|?ZB cUՏ9XS|h~W5r r=AX꾎Ք4ͪRqjHM?pk)Zq:`qW}eׅk{1Q `4&/ uQ<( EFX~!Ft R zww7#Q{;-?5ByX` ڝ [VȻ"s,zGp߶[Pw^͛0VYsCq~,ܰ̋['_m Vc()du9X?jy͞pޫ1t^%ƿk!amCmɢszaGmO9ۂ0?zg6>1(X|߷?`% 2C7ƴ]>:0mn+8Rݯ?UNMn.xT?W?Hd3\8NH:[~{;sySUK=>Ty̘rXl^ 5 G+C`AX;ʪb"9UVk}_kk߲z}uN,zBDV9C(rwfy2i`A]2mAu?._\޼/cX}oS/Vy{FzIGp~)c7wjf.[;iOSAr[t*D/< +rA&O_l޽6e/߿;7=uݣ+v͗^u' j]05ޟ#ft‚5.?sol_,MXս>=dSˆ~1b@{♌A\ sعƟ5L(o?319֤3{܊W=![b/w7CCU}c3mBN XuGZ+N3ۿNBo`|yT }+6&QߜcUr'淌r&y+ubwıd%;&)nָ?^(A*\=LMP9_lmlc96_ÔǼß$NvK$m|fwE);jפ:AB~Sx +a rNя}RƟc}{F{x  +žV>ȕCi_E/ڲ&Q':̾_98Ѓښi]~}nÙ 4^?{qZexu~|*wT+R2,;.ECZk5b)Et)IW6=@ >|Xj}ݍ7 qy7aNo ȫº?8u/sǎܿq>Gz#4%Km+E~70|9wqbOx?dsHźݱ.4U.rhN4 , F#w󾆪n}{t6kF,Өulj[5ޡm2hV`f}g:ƸPs3۶[>yNn̏5\ y e[xG1W +UkBUunW&_.H?=%W\V0MvX&'|#XEP'h*_0pfIFj{&xp$̃B#c?6~?P>߇!tlH|rp'޾:7*3ա;b; 'z9p?).-.XCU)6m@n== s:|5o =҇wBCL?Qt,c4mH?t). yJ|g +M>ў8}-,sDzO})&IM"y㩧_O2nݐDƞD(Kv1#q=]>|'|/=V zeAu>rK7jNjPgsS9Q. kWmw9ׂ9ZU/Xpw+n5FZ?caV uYGoMغu2nō!d`\d*zzM|K! +qɣc k7mݺ GY Omof}h~8zK何N|^*Yvۿ kZWYgdD`99ڠ}~ +l%DF=1C'\6 +%+]y$Jy!^JEH%~|eξ;gQ]~u?8 mzaY7y0 ˾짖oIOGa=òn!:P=#)Emػ7^WO]>hk/cۣ/F*=2N7By7.#1n953VvVryͥ;"SZN79~3tOY}-[~M.?Vj6i;rzܞ{eyw_@35i}b :п}óaQ@-8p]~551:ISϾ;| >E^Cu,?e?"x]?wZl[QI)CF}*XK2:fAFъʙi$C,_CU\a/GQts԰'6m(8ERp*'#Y3ŭmLQ;;a<>Y⪝NrB.,~( POͿO85cg\fI0^u]p(r#F1k&>럩ZujTWXyØ7\je84M9Ȗ8 Nkv1x8$Nkj,fS#{]l=2ȮzTӷX!2{@1؇w?"?p%AQ3#+_7o=֛ mHttzz$Q2E\ ;'yoIvTA^~obOSi~:[3~v:ڱn'W5ؖL. ˱y?:|TX_8փsO`l30kl#g:2aDjt:$rɠ̘u;6޾~X2njޱ{ӍPU~Dz$4 +M$;=&= G8E[Llc$w1O9)y#cd\!Zq)5S?gpPguwǺ[jEz[_2a>󞇺ȋCZoި1z%nQȑ[S +?սze!p?EW^ojS^ʯys%gȑ+U ǝ <yqؿrYNj =x 濖;Lؼ nsºشꪎ뮥˗~ ᜞1 x|tUPƌ&B?,JEmQoc<`nq/ ,l̉EMulk0R}X}[d~1ͱyAH/|Ugt!yr}þkUI]ˌ5LVӌRn]3clgܠ#H1f%4ZT6h=&m|\2L/$GٌSȪ-ZU_tD,~$|!s+Fuje=q'Dx|/%ɭc)7q#WBW:@8QkVE̲ +/H̝c3m&Iz{E 2 EdH?^TQ3Pyؾt3=6R 9WжB m0.lOcź8xt_q+Ԍ`>\WAMVj:0ʓ-|;R4>?y3 +2Gr%sͫ``G:\+̆Uhk0;mקd6:9EY*>ӞLZΙv;bI1|B|&wm^ _ \m݇A9Xq2 5wƶ.A뭢RbHԸ!2k|ѦmHT.gh%s2 ք= /\ײu? Q~]!G2i,Eyۡ"/ϩ .9˪XS-:FKq!y5oxyPaQ!ރ8|ybºÁqݙMFʯf֮F&TخkO?ʯZşt1fg 4ӄ>\g\wI49:`al쁜I*TǽGȯw>.uxR ?cV>eO5Cܘx }/Ww;/{pxeʞT?_4ul!Z"2B6RWdÑZRg|2,MhzmҔM|nrۖ;7uL~ 8;t"Nj 0PeE?=\u>e|6[$4ele|s#߻4m_ Z熖[%96刘:Z"|w0pl"U?G2 wN}'QvV.M6?]ZC*\wUqDӎg8Gߝ >ۑtL0? RE荢G%$97}3G=xA~㏤n|9)`=dce#5(QDtGOH(12&:5|Wa~cT{|vW'5u b_>Oe$7Mi*a׳O̠vךoJ™'oGKŌ_*"iF0 y<WmS;mh!nkamז!KSo/^sP7kf5qF5֌A[hzTZ-~-[<濗m4G"y]imҥ~=&c5u:8xp#2ИIʩnQNb Qds7s,uX \ц=7/of~k6[ ng6E>8:3]E>!{u;l>* r+.hgP ~ Dž/GL[?v;_-j8`A-E;)>\0cå3+MF `LEq~zoqu?l z2uȃ%Ӈ>X2똮50)q>zWN1сk~$[?'"srJ PRDоLf>; c#,} EږTO9ѭ>cnҿ( R + Sf~C~?{Ql 2T1U y7|wo":0QoΗ&XMY /QR8q{_VlVD/H"K.ď{^џ_m&[+ai{<͡6U }(.8 =Ua!59Q)VYRJEOyW>Iڱ}N/56LeneB5S 3Vl߽ϡgNq7?7ISAXuxig!0[? wB+6٠6 c숷*Xh{WYP4F>>Atx/rʙ?K7ד6x> o,W1uiy8 +(cOjkbڊk[{`{*Q$K'D`vAk3L.z ##+GKF*{BX b:j T>F5'=A8ZG>JihvÉ/@{ȋ##/ļ3T7E'{ OEu8t!nю魛1.:'oց}ЈD>uHu/B$Uge#cl D[hu6/1.I Q{xŁ1H:'U HiU}fF"~_t+2fWx:)Q1Յ1lCK > v[KԌA7ּYk\K&>MuG|­hZ{\`T1ke/7~)~A?4J-|k㏷a;.Оo}WYe/Նx,aѤ1%kToak[5ʏzSaR# νvmW6Oz>4Ktjw ͏i'c]q\:'Y!MBL%F6MxoT;K?~E[w^sNV' +N.6%kChԉ]vSK@sޱ08ajY(@uDEe EtXw詐$.[4FÎp8kqh~l+vXgCMY8~P8>{Df+M>ԲJ53W csz oAF癿qae's~wb˽am1ݗbcN/nќx5ؓi~;Cg{nճMMȘs}) 2B^jC3.\g>ο=}`an!a|ȼRFl溣̶E|kodBXcl{a?-vl1U_9)Ϲ+wX ,Iݠ4` ہn¢.uNV3_^zL_<qE/U䮠>~J;򮳭_(HE o)=ϛ=ch\z|EǷ_ +VgtD|R+]$'FfKc=ʿp?Tkv;F@k?sV1 p$SgwT,D)^aD=Blq]X[_IWY{;`rۉ)f7<wn* xxƻ|}8;DsXGos/Z 4Ҡ M!zB\{i͘ڛ>u_8N1?Lm2*cT{Hч!״D;!׸gkO;yM)MW ggt}yf)Yb5Ulgd]g W3Ԃ.[+Y?3<1c06HԌY gHѢFUs"Xl?_E3NSAcDvz!gSa?c뺱wr`Q!|&Ԥ~f.YULrd"Cv +[c7?eۓ]ۼDך#Q{Q_oȚ?1B>q,ըs}3wQ^=GLn}P[6A x,؏>,po9ѓ7߫}yr>D[k`xKI zOj8 j/錸-bxB,UчX2roS*>l9fƔ8g4k_Q]ε4 vE3汏dszcRs=Ӱ߲@C۟dI^[),?شz!Q(N htcm_Y`1R~bxݏCy^c ?;͊UKCA2ݮf8[ԏ $rE O.  4"Qrax[ jb439ѸzasUg#pFKpE&ce qUH}}{sb _A_Sz x-o9@3yVg9wl/0;ó!t;w 5'9}nLǨ|\-wG;ol3\B5,ͷNI燶mG F?tGlV8nwއ^@\Ȯ}H =k5?O.zP}cHlkiv_̯uH>|;6^8+÷[LtolZH U?d}óG_?G5{]pHk Y3?M$Ein)mm׶dr=ޟ'D~=J#9XaTۉP/n/cx!~O1/Q]'-ſg%?9r^`AYW'N +Eg8IKRȣߨk<ȡ^բ%cCmv#R]ó.|,zdCucfxK![U})&:u! +M?N#Ey +Ydry}#20^Ш^DORIscI] eאfTӾ{|e|VM\qYy{ڠG'm h:'VwA^?JruyU@9 џ\QY!z. ec3:# Z=}6AD8&ɉẜK3}C)=]UɗDo}?Xjt=z.l=A13=?mԏF?X'(@("\C88U葈|ݱz~ˏY +Md +c_[U_j_+Fnaduj!z䨄7~{ +la͓>2j]X)_{-fVӇ/&^6>M@OrhWK3k4댞c{p^2US&zϙ6o 5Hm6/9kcW. +wo[TTk^ݎ7&ÏFv8"`QǥODifAC߄Lv V:&Ή G%wW0ŜAwli#u}TwIfX!gg RDͫށq0Jp?/,Z!gu{צ?F\? tgWqa]eK;%G?~ĕx+G}VW=xη? lLbU"*-D|. Ї ӶgdڑiֆLuG{߄dtabCU3B32B=0ݧf}4l}O6X}u&^?4|tZ$/^ +[~py{=ڌy\oe1R~N,[IF̗q"Q9P:]l5ݝ:W|#p7uh&/NHqL}=۴."}jSSh)lߗ91C+dCWh|y3sSl~4>Y5k|AՁϾ{Ȓ_d=@s0z ͢r8wFqFմk5gͫȂ]{Q7zYKf_yߣ;f|Y'Rb+ۡ|jaoћu y5JƠip\AS/@_Gz{2CwNKֵWoZKQop\:NpA}]ؽ˜ +jV͕\a:wgÂUc*02& +(ݶrQg)柳Qd)SWĆ47ȽA`3R4 O–]ǹtJﳡ˿;t~^,™AZ^ A A/2, \S9Q{Uibid8cAt1.xZ[9?NɃxOF{L~.]?J?η?z:{|}3ԂGqʨt& +juIɁ K$GsG>p?|}D0ʵ!4\p͇_/T5'{fg]u/jxՃ {×JKT75g׼{{Ii{i'i{?]yeݑAVրKJh:gOh]U#rKԟVK'7NH Fh2hlE":75e&d81f7ⶶЊ5 Mq!]$]v5b.fT6)|LܭS-d;=>$c4U.y_d.r<\҇>ug 1^f.pof"OlA}5_΋t}d3QU *^!wIBO|6o~O+sGg/8ykf"?+10,$`H +.ިdO3~u~(n1;b+xTUA> 9b78ЛRC@M>8`:} [QQ.[^"[ gFs"X!xEˀ- :r2vn$rHN[q6'Q#]y|ٗ^pvՙ^:Wl@sO [ +loqF>rX7~C^p 7X νn"38ʘMmjd=nO]ܲ67-D"՛ԇًLI'%뎍I4TB'!+|.7=~+Go?J?η?p<سfetwqN|ҭvlHFgΗo+J%Q C"+ KԬ*b"D]zwdA&/ܨnE> 8dKf vYG<~sb_m +]szGo[gtZ$/C%m#m7[LoW_n?.:/:f;8"Z>~^+I'5$ /]sǻ8D%m~ &c8l}xȈDl뜩-OEb>M@(PCpCwSNq/␘\X%kݍDm䊪65\`}`|z4(5Iff$r%HjRߐ]Gl8݂%K':@LZTE"{gNl\ !M6J(NK[( +-B ;+ni@d7L&]R\^qٝ9sH$FP/@㟓埗]a`o!eg"tfqbig ?@OԲs?)~N]xN.u|2Yf5ﮜ eə[K/O8۱矝pEe 1΍i5͢ _vxKT]'E༧HYu>z ϫ?!V|]|G- 琙IɥE.}L%;HV>i]NçRKcv>i5炛sC3[z}ɞ\cfC&袌xa/߳#{k"AV}÷u\.y.:5@G +Sט_lGŵ X㻏דUW%vSmwfXujbأubaȷ`GڟewFUSg_៕87.&#%vb׿CY"Ý''mI}N-"s *#Ed[E) aYBTv |zFTuaq']35YRy|Ӕ7RdSj}-M'G~ӺhhGn}_["I+UK="Tg,6Zג z~FV&ADvhlH%3K;Pt }a9 -J{O{vJø6ra."3ݶiJ[jt#a^xC'v5Uqd@::T3n~󼶾D\|F[mteM%p Xaac4AH:j=FWYs77br+60b&hx&Cw\=#quCo?.9`FlQUԇ{lZ^>5[{Ls^WAҀJؽ|A[6t)d/!QAp.?V=زt{/)]]Go}q +?\X o)=<{~"p)G 3p`//쳚sF訹-vڸtʤp}uojF3'Uj!1ygmÚؓq cgy[wz`L93gGv,:ggpΔ/e-NhEFTLv:q!d.k`}Qr%ct?_zWW1k2<Ĭ[%FfsGD=1/-7}_8uUNSէ\}e ?z&/BKmֳߵD(Ek[}>SG?Uۨc3E"7nC_/2hˮ j@ZX;ܛw$-3RLIwS نxJk2Jq8:[]#lNjʡ܇1& D{Ǽ>@ccAw#s +?^{̇d,E;j/ ˄Y!G8CȾa +pEFxlW}T:6˓ V7C=zqFlĎ-HŒVtDYl$]k}Omi%lIAFmεawVڡ.\c8 ڐM-c֏aa[i0F". fVWt'ԶCӴzW w=,ϟz7`!z`Ie ~KJOs-2p6XWD)G(+TK]sA /3de\6{vZ=$4g{oM5u/LE?DݔÝ1l˼̱G&-8Nudts[~ջjOPnzPP^z urT?ۆȃO`9T5׸C~7"^1u :c{ *'[e FhuzpCSvk)]ę'v~!w2ن EEPE @C*cwr%_+o?UxD#8<(88*HWQ=>b,/*6\OJ:u/gM̚&5s][! +.Ɨwt<ǻ[W,])(XX5szwIkѮ~՗=zMZ};zvcʎY0Ш~{1+q]H~NeUSjZduwh^\e.#&:N<;:8w`e!գ&;ßl18m&#kr(eOzVW㵦One4d֛tۭ8B=>Rnاy6;uUQ+XoZ'4{=!>o Ø!(,vg>~i-R)OZp5}e0yovv[>G2oN7 R+PyޣPa>Xd{p~Bu`r&zr6!Yz܀j&gu Χy~ #ګʳOdп"g8! m]I8$#V"lB&E=ɮG6;jI<>.u+?ۜ9 1@wY{e+rDv*z"phQkQ Y iqC9Ń܈@ (xG+6;U533~g) <&&?eedFDЙBNa7Bs24Q1w>??ޅ|NJe! ݤq?yB +|駭M 3ܮw?joGKȍz{R ؑIn{AI(l*tB: zB!5X2\)_'~;2#>*cstƴ-gвI_O<ܐwRUh0޲S'Y{ܲ.'2A>jB6/ug\ /Va$CoЎm]+;qswF3u7k*jOU7Ф Pq +R-.?#eIh(vC:Gmv 1StZ{S<'òz2DZq%͕"N6drwWܗYaEdR?ZƭUY8Imaz/ OqG,/ Km|BRc{[%w`Cy?>%s`$Kb̠"v_\o?wIz cטϸ>X=Dl8M?nJ91~# # 7'*n?=<{~)ؗ& ' +=:v]ԺSwLHax_zwCsXt\훗ߺ4>ǞK%qlM൛pZ}Z;j~-5FmَZ 7%̪Cݛ]!b %ɖM+nD&=ɿB!zڎXr5)QO| {f^+wc`qÐ%!7B5O>X9V$Hq +F;Xo ?Aۍ{eGe;6 kG +~:B1-CIgɞdmSe"G\Q}CZ'_/{ܭ[mǷ5 ylKoZ]dxߣk'ybZy|,jQԒNM}hTZG48s͑AUʿ3':[왑@)pB72|\%jwfm6n\xY}l}_9a`\㺨fPgSMI|BUś).v~oHGFAIgyr% HCj^=|%.iPރF~\-'oiGu\>IAϔ/YۋK6ifQ.z@V-Gm?/aci.m3͐Iu .O2oېYd~Dȹ;>Үnm&~zu5њU3i9MPo Ϫy&SGEI{+wEax ԓL[KPBEƃ3{[#!c5#:7ݵÝn2cShbۭ/y}@}5||kؑ!mWl'jT3,eu ~o?,jpD?Л54/-Jx?e>G>Ǔ.eӦDAS?c^Q.|Fb~8!Xo>n&32*ZhCkTן[Xޱy;13ٍ*0j5 X^emZY{TjmpƗS$!nTm_q,gIz?d\{ ǜÔׅr xG9|~[Tcq|7 'wQwb#fԿczxaXl;K!,|[5ۖ3RBe#enCW +<:pmK\gv3#*}ʽ0{!_:|M0eѤ k.C^C]'yQ|Wh 'GE׿2}u ;T;ιӭ~owjt L*|u렔yi ޴i͍ ]\HZZjj%4p9gsQ}+] PMAx/72&~q KɪZ ­W)+fq#uMF:nHH7y+2U^_巈{ǻo#A X߻̿;ȳ!aܫ0DQߝ @=YA + rƖ 3&.r>,ݰg^z+D)>toQ:m '豾vdPc-"vta&A_iyl$Ny"=̿J +(h^dq5wClTKm!0l JVM6䗓CvM]- Hb=l{G;| ".,s +AqyCؕ0ͫlʰ +˹he!a ~<0Ǫ2ȸ% ;lA0*\c,FVfUu˄sxP>f} iA~e-n7yol?g?J7"iFD_#yNj5{Q(j.;kÃ.J-J}E02K϶w֋ +"m'Sfb͓#QdE~3ԇ|k;z&';{)PK|2& +^YG-•37"T520|۠YVv1g_6XS556z:5Y$K=U9Ӳ@Gd+Ж׉8^\;~YeVEZé.c[.<1 ljU{?- m,ȓGc=#H\`#;mU;[vJ?;_q ?~3 ᫣]8oOU>+og7([wxgzu{ȅj B^gρLvj < +1m\᳙?vc] r)﯌TO. +;dJ(=KvRW^0Zto}糌~vȭ(FxQ1iNפ23CB rB9W!A=u_j4&VC4ؐ1myI7aaS+z{hTۚغN&["~ "b!Sِ֋s+7R<܉Hb/Ͼ#*#Q QSe ݂:㏄Y#PI5#*_<R^"5WAٹQ>؟XxC5%$BSUMp|d, 4p:Șa!&Ȯ+`-XvֵWZ{s]#6kGKI}^?bUܗb~|,s(vFDD};GOFe!(4(,Yx瘴N] wVs +&Ɋq*%uú'v&?|ݙ)/+N]kzŋw|a*X7y_g{%G#YSbQs|KvSG%/r6f׻屐 R1ƥ^g\"?Yf.UNv}_=xVù + UO+l|"GYjiʔK'l%?gS})n &RyLzߏ>.w1 yi .Qxs?4j_먌.c;볗R+@bKZmpΗ_]7\gԟL-N?!s[heYU[;t~Tɇ~|׻9ϻ"E i fXW1)?^/K?8N&{-j4"dupVcQcHC^L` l&b.8y99RQgd+ +1GC})?QqjNAMwa?,ڹ3?O_`물^~Ϻ՗y/#ƚHkn2[wUWGmqKO' u6Y45ffG7T08LB 'Ť%(p/Do?Xă=8io%&KZk'oB%*-%EjvTod;n{2Ì`"ow߷4eA{?%/^%Vߙsxq2&vQ~u8t@*+^؇j$Pݘ +V:6b(_G/(MSӬ[d]huʶG.g rVڈ=ۍ {~[spz 7[׫A×V6}pI?6Y)xs?'`6gΘ#cbs%Ss6cや<ܒ븯{T{ͭ&^klTa+4l|K(r ]in#7&QKߍk͝ƨ<!S]wȉ1M@=|]DFOXb>:8IqSs;9"ߵvz~.#2z +N ~̎5?K]& Ș>DW|65ܐ9^&^C%)e=+EnDy_pWE<+Ž5gbvWmg|YuoL_Lno16n[cl唼+) +c`B Ho%-|dvlz\Ucf/ןs-g|7W8?eպyc2z|8IfMB#ƳIh\FKVe9yڲ"3ݴ*g*ѣ 0! C:N녖چBc(FQi/ s|'Azm<|~0Hn=f88bķ+ n嗗F#eMM1J[_{CiNom-~RLߨ8̽:2sUكWc¦\}exv!" I KSs7%ydknK݃'!9 $7^L6&ZO|m\)~L1QCNa̰]FF`֤@*FZ`qu5F ㇉>~0oXI.1ݽB|9vbyZ[h֟#h!mEdۓhS9BՏɨqysvOoHZ=Eۂפ@ytsS,aS`*2j"RЏ}8:Tg8F<=~F5_|N}i#uԼؔfMؾ%˃ýw/|c^%BÂUV~Ztl'i$ͭ;{!B {҅h,ҋtܺϾ/3\4 QYu M[o%V̿}B՘߆1:n ;1`MMe +Ǚ ם*ld|^K|~[*jT{bá-K3Tk*:~ٓt!!iy~鞼T| AͰ5w˫iF*կib^ƗSv\>i6{{H C>]B+cߍI [cFC;W֦b/~/-ټ4 ƒѿRAd>/BA}) w9׬w[F^oK2G$?toHy6L }4 c' +{Enk[\͛cMqݭZ?姻N9'qΚ6ý(v,@7d0!sQi1x7E1ƹg11E?g#E+ǃi9 +?E^v OIQ@A{i`yu|~;lEL[t1-$w_H VBMb9?8bXbK;u%^8ovȢT3]yz4? = Z`f̏,!b$kX?:~.؀ ax~yy_GaZcz=+Q*cʪD&?d՚gw;J~}.5?P%GLՑNT='T=k,L^ 5HЭތn[N .bk28hM?% +Z57BuN1~8}L2c=KBrbN}Ճ+R]lWP[m⏬}A$慛?$۪2ȴZ'`yzdWe!5 }Z6*.op苡I :, /f?%{Z Kd$(i~\ꤒk]7i<9 wve$ &e_svٌ]vw~n7t6%15r$Ǿ@!T"=[5'u2OI{ǕI_9}H#@0AoeYwAz A]MehZS pRY-N|R#>O]HFnIS%| G~>gڻ^JkW/~vn3}REطq_ln]OZVoqknXs3]F_jYь= x*=z?7Gj Ǚy$s؅ wGϾ@V&)e'EG_!Bf`0:I0ڄ=j9/c/0kǶ koO0lryj?I>?y5ٺcOұ7f|z"}Ԯex~4Щ[o^ߜ[n 3Yg,1:$c~x~k,[n闙Su+P_̆(oeK=g=y#<.SsMH)F' + "jߝJcRYtD-N+h-o?s- 2ϮrsYV%&W8k+uTMR-!ZÊ༼Р#*N^s٣DCFכ^Q-${]@zp"? ` Y!V8CAY" t]QgfNT+UkOF~,1=}oٻ+xm70v@mFų6ɗONdCI vA|Mzɣ+խ"h b'_M/r<.1Ȱ~5o:w[ + ʑ?LG\U&^@M;G:ϊЅ*e:OYq7ۍuՌ nTZqISc"?i8~#붯ɤuWo:K'ru 3sdxKzyf= +{77ͭϽ5j|2Zgn#OߜK2wBM28"=^?ar싩ɓQxP=~-Nu\Xf2 Xbn?\q:*3I+XU,N517mXa?c}T%w:Aó.ǷY4GNed|i0DqiYF;:/헹y闵jv^7v̔}XCU3ZwVvyz_r|vpۡyy1!{|"{{o==#2,P븦Fd 2@,pp®;2>A))jI:оz\qa_1 +q_iX@+ƸxϻOT{-6ݽfM"S }`ö>b̼Kܞ2ݩn0^r⫭~0ZmҠ& lbFE¸!F =~+}R?uU ;7_xaٳ+a^?k U%??`C'ccε$iIZH+GeK{j#~CZܶs/3=pS'CKϚtGWyZ<&C۹ˍQbq!hg |߬۵{gh[)2{*(9¢T BsoE&hM~֚kZZc>n=籖w?i +>ﺞo:yTRτHW ~ЍSou*iRkŌOo:v/f!k/(ϹJ^cv5A׬{ړEH2B^Nw{/ޮDc}a9jT#bU3yu*GzmvrHBĪ:ݟU6rܧ5|o&o*mD7,6r1eqQΨUzUf!6/ +~>A`O`g-g=0+cA/7@IoAF{{m-3|snJY'A52֖jSív^6Tm@ʔ#ы?]B_ScnK@_\~WPIF]8V P0SrTML#V󛧧Y#8(cYU'8$ug`LòvD+(T ?T}@Ez:9ȹz{> ]65_ C<ԯѷ7\^#DnT QSQaf9q嚁O1UPUi5Tc={x{9;.Q۱q/ckL+^y#V\9;r6/]4\{ݲxMeM{#X/ؑ?sB?+nO0`75\3IYs0MMu l/z>=4c7Q;=Ї>M %cU{ǎ +FB$c1[l3Z%s$iQcrAuo=yo mنgx^?o/\fU1??j>xߨg Pf/y^95`-!Sσҗ۠R1s-(Ͽ24ȿRQQ-ٹtƉtj~\H6uPjKc݌nTXWĘ?]*ZflOh!"'kH&d}SO] +ZI*򬋸GGǀ̵mJEr?Aϕ>]~]Oa䏬`gݓAp#c& =2o-3A3k*9>LW;dXynE9 ѶЄ&D'8k<沒uTqa|HO7=i%=?_?/"~G0v2oT^CsQ{~-]߾{Qߘ]ONY`k0Rcy^%?G%$m vn[ _k>i=OgM gEEw$^P+sȘ)U՟/]Q+ި2B}ufՈ-uo֗dW#Y10!d +~'~.-b9K2Җ]hU*:+ +|rW6,-yAfQym[rD%ZZ̾Cmk^^s$x=|6l%zQ!zRX~(2ȇׅ@' n 퓴&ǃחO̜!Q?ۢ@&]Ľ\!=y0Pp<\<"٧Bȳ`D>À{|e4:}/#7#r2W>)8X4$Ir}ɼ~Hi6Ko75taxT^;/>,oQ{~O}+MXR n ?!m֤){2|7g|Уp#mkJFVdߞnCwJCd=yoEɖ_plv+o5A= ֨- +G#̗y̿2ijǿЋq@i u/G{-5S2K̏1K12xOylzIbi$+-5dZZAvfnc':7X'k?;/GsĥO;n6׻E pIYuM6k{ȧ_o?HK$Yk{>8KL;tWЇ)VGu{3'x hjrnp`?\o8Ϡ\[᳌߯$h3&SU}]; +cqڲ=u+ 9jНׁ3ap*՟}&7׿ʻRy Axpef*Hu15nBZusqIMQS+Cvn eB0;Ǩж狡#E-"_oX7(Q!}C&q>ךH*Ωfe:lدE\zgF>Zw!\8)_d vd|7z(ZG"vEdjߔj(Jq_ΟKJ%Oj`r]Pe{ہ/Yq̫OVm\}#QCOھ<Д:l_g^A'-+pk#69py*ome?}&aM&{>x?8mro_:K?VG䄔c - E^f'7f q12_{Mٗ1xec^o74Y&@}43уR&ˬ@_Y +)]_vm0_Ձ1G;>qqmÛ|ym~Yxigí~dwd:Tyj3\ ݙ9hk@Vܣg/ vxM:t2HzY&%cz8ߒgT{|s?q~HTQe1fjgt"GmR5;n`|A>E_.ἾeCQTNhȹ Ejcv.УƝ?Ȃ/B/E2>=^?^MKNx֛AY>#"w-z׀ 1Fd4ƔU^w}^ԏk?@iz;"uxbB eXօe{eGC'*73C\(V>å,]]>^gVW 4!q:y4ܸ'/=0iڰ}=!,1{OD *lN>;C76fGlb0ɿa>㎑j+p}DmQKɼVt2oaNyk3Cc9_H&Y:Ay^^\FVXjJQxz5DV櫎~_>SRܾkc^U3g_O96xŔ^:%׎bC![e\YYF_?8{.P:Z GnSN<@l#A?pdygga}rye7#y Y?d rqJ>G ' qx̣3/δz9yXF8K-IWTXVdGHl,.fOrAdZкkNDl= ]9վp,qN(eqiŬcѮoxX\nKV5B*Xyb#As+j?#e}9\~>29Dy?4DU| +$C3S>R: +U/хCK23idXd +~4&[ ,;ٞxT4z5p3l%vTYʠRW]bfu;N3S[2 -s6AMb"&YT@!A!Eaye?@+ *֝>MjKm{>9Txۀݽ,+G\ )?!|dZs|_otq~uɴ 5eyyzNܬ֮Yka]2!wÊ۵5I;ߤ 2׸lZ~<ոEMƍ:k(m#eDVʀ[Wg SU$D0+]wC{9:!b ` M onG|Q'~A}1 ?d $0BK-)Ӭ#`upT0BKđپBۡ~v=Z.oؼr•/:xC]. +_)_Lrc>CkuՎJǿu}8 ºM;f ! nd}m7ƚxyBVE&[ DYwXkx;YĝzET[gy!yǘp'WCiBp" T㢗϶ɝze1ʛ!"ɓ(:\ZX'ԉ/Xy5fp`]`m }QYfb[䀹%0:<)ny*-$ TX +{HsBNZ|%J~P00K.rhK5GC8itjꓠNFLLDwߡ +7̛U2N QrSa%󷵉Y(mjr9G!`;,M]OoG{܎Ꮣ{V5u,sCҸ@5ے!ׂ# +x\q8WuD7 jQV_{WAB{'(GYFI&dED>ʛ6 ~^vҡ%W\rp>7JY?P_N_3ַWaHhu;ÎkoJY@PJf` C#Tv68A>ζrGHئ/eu FtfdƬ]W}NGsq^$Рb's^-EyY%oQdDE)eƺ/g >60w>t'KԢ +Z'6o^ +8{=o}ghk>?7w7*QuE<7q +`}*M.QG2;23s=i;RnM`tnWdw׆{;SwpkGʽyEKzUJ!)};w PqAJ_;yd{kTO%:j'-@[hkyN^oQA?LkvuJnZ3ف5:@j}ÞjpP^f/3@)+{xv~mF3e$5Hj#%j/&R.F!q$-cUmu{0St5*mź???8cf;tfFteF|?;U·WUգ%JP羾y*w>y|{7KՁ?v e7~-7^7|YxU???ߕs֯Xk=lYTiڒ~չ8geJHfs%M笚kB +ɏq=r>*0gtsB# Ne}0ozTp~ĔM]iC@kȻձZ1>hF@Ol)[R ,(ǵ!jsH5Y%Lp[Fdj̖zkj?lG=0҆uܼYyZThDBc%],hs}Ɍ3J a+Ɠ}65VoSӘH`XiNOg,rgCT?G@܉i5Шu?kMCٺs# {|:C*E(%Pj/=* yصAnq5V=yNx*3z Qp̑T6;g!  @E=S{V,N31jzV-+~k?qc+ Awf$۶"%M-jHB/ljo[";o+yx}Su_\XygRSD\Z'}e-b.Ϲ吅>ATؒH b&,V}ࠝ EZc]2"ASi1H#B !IC6w #,/kl?;Rlx 7H]*>@}T*R H*ALm~ޜʴx b6s<ȹZ#498aFZ0.XAlC5>?`UILs+&B7ԗc-߮$ 0V>g_O1ma+oqKB"1FCs-ۋ":p7~:Mj߯b챇UHf;JM$+mY1H=~}ʙK8FIfI۷UG+Jl]zI( +u:ȳ =UMd-p]_3_w˨?ov~=W;Zy$1PX@`! ljľGO׾j}|ԲZP#PY﫶6x֐σ\>Re8j-bsquhbjum",]of5]*~v|Vw"憢`& N keS V%ONݏT\ i=S1; .]$RB5I[I$01xyz?>?BɃ:w˗v:Z/b5w=;opLsFKg;Jg;3[y|ݳ;wMWrjmKzmIxzl[w62V2\?먨Q}QCֺ[wo׍PSJ>,y3 |I`s{H?£_N8j]UxE1Tc}ު~nu:?4MS +m8\`fHJD_'g|~C)_[Yh'+3ŊBDҕ ҉bR;f¯㏍Bw9-{Ma%BmhAaTEι7g9C*QßFt9޸\[s~>Z-T0g]=_q]I#*^*q~Tٟ,:"y)>}xC彜~;P;X=}͙v!rk|FT[s3b3(]Fp =E9H:~J:Bqx¤Km{縜`xӫD;ڠ1|9W/q.8[]ܻ|*Mr(1IOL1Y1}l%]X #[\HcdC;qόcPHۜ]GfT"c=5r]uLŖ5Om|He,3E'ȅsCn )V*/19v^ 8͏TB$m27_quosԽAST,7`q>V&Vs?o\|v}վ>g_O6ܹjE.]5G}ŲUgLs7]Po1W`oaYH0uZ9(R!{L}+Q umdW/W;C}} B9'2^2$ f̓ϿjMutO>C_ϲ]X5_^]w;P )a"{{, D寄CJ"ĪCXdn{];V+p\=mP8=-L؈JS}c*shHmrnE:xhtͨeS*wGNЬvBngv~ppaӬuf&5Θ`Qn"oм +|sf;mɬtWsrVc<2|؅Y3\-MʏsbpJ)x2oG)@NC|"qw9SY-q"I@'}ٝ}pվkwܒT<ך`>q<ֵ²6M*jS3'Y|Y?ܶۦuv}7TbV=$S䝁dY =rBҭ,Nӷ87fXO$]o/OwfTyCY,r)H$+`[QOo7*uq҆Noձ  3V%$j߮r}L&]ݾ񒉶v0HEm#.]JKuX 51e^о#VAܠD (˷=J~*3Q_GEݮ D-H_[O%qr)'ylvl}K./K'9R?W\#ðV,Ph߅.ZTwmwWo^%͘g?O1y F?E 1_g]3wS{<;V.5}47y .Yh֭``f++CKnkXO, c}dMURȬHtN^ {ǞV5eӕsCd0oZ/Vaǩ'|noJL{?jK nȼ3}j=,?#"&oMT"ؼ)6!S!Sђiз +WH'UmN"2)zUΡȩNpJˀ7Qݬ1 W5R>1:wfy{faH" ތ[ۋ4ӊe1Lj$Y*9j{IB|3Kk..ˎ9i[臘܆Jݵշ:$^d7ܓۙ}b=^Z#T0|}xςwr9~qߜٳv򹍧O”OCX?>O)Yzn?`@.۵x\p +(d)b*h<+X: TcS`\a>ίg"W>@ N7knʗZ^rq$QY0P.P?lYQaZk|y|p9{YS- gXdxّŐm]U'mgʽ(E^ovT;};Y'TI>Ԋ Wr>GM{Xy?2P*ﰟ0U~,]\f өB6!UAvN5*K6-dT.VC&7q\ߞ;  +Gy]]*GňyssL*vJP}M>1) L jH uSMuĚ|]oɎ4J!8\#96BB ~7hYܼE̠|$JWEDJ[~χ8c٠*X?YbŖE"N`Ua Mm278AC;>NݵVil*#o{ͻvB21Pj?5,dfc|Wkցo|b8L)Zwi(7-1yTay>?~a{hؾbhC_Gn0(i[~|VZ#sJd]ǢNZd+p +/g,Iy̿cAw5A!G8AѹxGb|ZuWwoe?4.j^lY8F\4M1w "yFp<3Ѷ}._ /)$t[xbd-nSQZ눢ٶ6ZVN8n3B_K1[^6jw'VSڦ5w,#}+GfdBZǖVբ҈fIX33̓}U0qKgGոN oS#v[B +) ռ@k-pfڲ^LA-+_{b֜)vQ>sPs$YU/]Ūgȯޛ^WxFzW(wn~ +S[uneН]|Inx/mShANt_83ǚ/dc| r6s3Z7cb))9jh b<bA}Y?WC/s\zFAvGK/Z2zZi t+Q4oƜߖ]8sї98d^g34oH8t򃻊%bfU3߇/I̠/1còBP6Yʗe6:&S>^C!Dfc<3P죙UlĢm1i]Cj wi{Iw& f%0<J= BR3 "@{Msr~k@!BL[Yo_aԔ)O>d|ogϿʿr52Jg ;#}Ԛ" +z񇍟 0wt)wYtxElfGyĨ rkll/.Ub f;y4F0Ȧ_嬝+wuIõNɝ;~u?^xiGg/ - ɛjY}fNiGf(m1p](i@0|=c b$uu i&-8Y٭Z=ϙaF:VKg.UH$-ZĊLp n"/+F^( ak) 1GNY,qP;sXzt i?֧\A!!wkjm\.78:UvpP_?9iRPZPSf|ê &O pxoF/,a > m-RP rSG`m{R=G˟I`n Y?RA;9u{ߥuoGwo**L~?tQ#XH'I-i$t.4jX̚/% 9uY==gKǢ 0SGq}O8 `WfsB*}>+ad~N7=N?nOy-P@eɮ Yވ +jLw V\lVP-w/GmvE}=(4έ̽w7G4'9Vjaћw8#e.Z$pA'8꫷MI`Ǿgz* 4OK*k |qn,*Њ Iд>k$0nDiZLgQcfP \CMx3وQWYGOVtg9#]a|-rՠ'9Tlz!oeʹ]gпߌT둓mǙ7U0}* +ŒSn5^NO5Ͽch|?kr43 ֿBUQkүv\"?7iM&ug? W6e!EcD^rvlPc: 5M}W[cN"AOv SiC4bMj@ko]fV{^Zj~KoҌgf('ZMod_}^i +V.שc["T8)X?h MLѹswϡ -/a볢>jiJS(/ې& t>؆ +Т}K:S3ϝnE70iJڗM5٩ H r_w{\J3M9Bp'~^If婸6*5^fC:jֻTºRakV_*.D vŽ&<8oUb=p4kxi`㢨GS.-gbiB<{5.lEd ?G3 +%# w`+",>A3r@R.xa]:4)jhcmpF2o9?D +hɹ9iWwt֜vn8PY1PՀu#}hniuw<ۆjv$cd8˖4 HPñL5iOv< b=Nzr7>\ptkc/}Q"RB{REqz, +`~UXlۗ8݆ίs^smbB2h@/{|EQ&2#̷ =*羏?39~L 14Aq/ {\3F&RȸOS53A~T{*IЫT믩ew׳i|MZn!MpEuFEѥ]:ƌr1K,38 2E:[ Ҫ]'>CbO-j?PY坝1$ɿp(޶=sm4y{ɲ.Ø!|~)ԔU53Ҋك3w~kчm+0@MG8EK7PA["A|{|-/J(f +csg9?_>HC;YlihK& 2 ZJ*{K7bz*[Ak>yJss4}璲:5jGagX;7*|?p_OjnjQ(_a!:^$ p6bH0 Lڐ`Vtd=m,NDž^3 +`}ѸO~Z^4Kޘ',<)qysLH`/$A2)V>36"*IHSA`Ìn$(o>uȹލrFr2>>qXMW^^|lɵW8@53\YFP}'3ӦDg+ҹr&#iu?ߢ^O~Y7to=P#4x$:ɥ>;q#?tшP1 [yqȩyHu 7˒7壭: `+5PXz>1sأ ,Pv[jV$,x=nTfTX%ܞ{,V5)Vhjږ:$HOtF@84F$eI ~CbK.{gT SH?Xh<ޮS^Nf=gJ6_o7_[Xo7^ElakhQq'6=T-Uj,vڗlSs8/:t' 0uy5'T,{Ad_NtYhxP* ڂXGpA|ž^2ݢ-=m 푺5aO8Wf?bf/1,#D!TYS}p' xbZ':<-Y[5nQ"r %&ӳ9"[rQ\RyJ,r {.i2Ø8ú\S~yܫu_ɣ!3g_Ͽÿ1ru=*ZU=;n&6ys=81w.4 + k n t>7?p.ԂC[{{N) 쁵O7[hvt|jb .gk|˟Lcʧ0^L2pB 5j{'@Z:s3&Y|čΔ:fqYPƭ@%=w4l_7ysQgal߈:bRj&s-=)$\mυ 2l+l-MEMD$V]$YufƹcB8^8oj߯dcb7'1|qE9jp KRQxB5%͡n5y)"o=BnsOZI%5/;tx5mޑV{B̀bL?`.פ#V]]3l5)werx7r˭7ԋ65Z1DПqB]JM枣,BV+Üt3-H/ IM|/.wTrgh) 6&U-/4pcs@3vĸľȯ(.=1csxc'd?ʘ+$w;\)$?^)kF6G}ӬSTbvݼG>'5;A2LH z_+13x? 7ojΜ!}neo`fb4.y߫k֯2`+6&$ cGvs6 ,sӼ I^=d>ϜiӇ; dr/jPOpzx =n\[/+ Z~IC,x~44_0K#?/u$;J9<ԋq mgc_R6"AcyNXJѬbq Z͘vvT-NO&kO-Z!C!ƓM֒ȸϢcI`i8B0J2Y3 9~:O]FT +lo åݠ}Fex}a:{er`NۋFۇXMDY"oznX>z)ٸRY^G%_pD3^1Q\>|mʗ:)+WA4$eh `]ocזxߠ;Ωug5VbS=Yvֲ86oDհ ōOh5ѶjbGh;}Hn Pǣb^lJ: UNҍ T|-BrP#/*[ǵG.:' +\J;-p&'\2׺tJ~-> tdG;Dc7:gF*06X +h"d6t+Ω# Rm]%0.@}Nhs+hCRY{d{*J).2.* J[&QB_?ǖ~/YMWjmA-Ixkjz{0SBe:E pg#w|9bVb+58jJXtfUly9.dyb5uXXhh^9*p +<բW1}!ҫ]պu~>?x>XBE߅Vp+p:ۍ\z ׿˪g?p_WM?{K¿<jPQ}r JF1Z/ȤO??9F_(dЃ#G",g=|c b[lWIj8NxcU&$tX6&#(%=-D-dRg":XO?+7YkIl\5tN&T]oEZ{knnF^srz'c⥶p5Kcq.9&C븰R2pXYR6k-.kW??d恒mV%MlH ;j^vPqŴ6q? %Jq#V.gGrxbQ' gaԒc!k;+#d&ˏ +[P3UděJjZZ=&r\5fZ۬Q\7|cϒ܆^K>sq%--iZqe~˩'}Ԏ|̩K0H٦mb׾ X;Umcq ӥZ$He.tzg\<<]prUp+2} l +endstream endobj 36 0 obj <>stream + F^P <&SKRc]?C7 N,&\JxŒ|]Q+*S5쩰7E5ϳ~'I¤TAe }wc(M6&]HNRZ&Few#87Ŧ/wtOy`r=6^NqGEz u.Z4h2A۹>2 +2 yq7Xs_ Yt<4{oY?Ιݰ0SxEɉ +RmmtBd1ȣq\)"l{>82{kady HXN(x{Ԩ,{iG5.5jwTm$tƜd(z`XZ{|=P[.O6-4#X0;Nu>pēBĉvx s'NS?6&'֧'ew@L0V{X_Ͷ3UX< ȡ@x>7ݬ) <Ɛ~o1[Y,el[۫?j֐}I _rkC ~j.GA9`.urkt7ΓwetP&ZOx׬s.&GmI ՜6EҦw>:^uzH\|&dž7:96"?S[S%~-f?s!j)eyI.Szoͭ?6G+My3ϵ|MBe{?w g-KcxZ;fkjniFRǘYB`nZ+ +#o[h@[;G6Gos]Rؼ$[qٕ 7-m\k*cE0nֶyG2_.8##,u_HƻFjrT&ȿCM~6d_ 2Ԝn3ZwȻ4;혳C<;?*qWo",ΒYaT^8Ïo݆NTXUXHॽg#8oF-Hj ̴+;CxCKՓ(BypӰ 0cVhC|GoIh}EZCK׼3FA>,6s\7 oIG~'|@iѶWxP ?h(/!}9L衺q؁ 6$Mhe_*4j\Ɜ+Ӡb6G0TU,d*@]bUU yUpY1| 'Q{ T}kdIX W 8Dk{6.^yP*5Ҭ*X+Us夯ɍ;/Z۫Hn'<ڄ)ForYTkҷlq~K?o6x S+oַkCuj]ʆqq7_,U|+oX#C}3m\l܌ZwzP/Y?/uQS:nGzMZrӗbI",;2EFҒ 'S~*G?\JYs +y|L^_[+W W滅Cuj|p5ΪcX^v4c L-IR?LΒCi .~Gp뀴]j(gIGXlsw'gpE))O(V\vc{ɶn*QHWsg0IaHq9I'"e"W^8Z84Pq5\~44@~E8dg7OTH~η3}9"bYLUtvS<aSǐw&ܱs.',˿ZZj)㘣]I|zI㳼y;>n/V^1i^$9u*](5]zyG{k&mzo{of+^7 =;?j!%.ֆ+ ??T4%i#=*?6h!kib}3iaι/=|9b C%&k_O(R}dp>>s9'/^MȹHR+w$6B$f1/¤3ې+GR㌭+k>58>|@^HʵG^\gO])<ϞlDK}㟸 +X8> ڸÊ6ޟVG>s<,.[ZP[-f^ٍ\:nE|Q ݦ5\Q@ {S+{z5QzWݐtW|kSYSSEiFTm-—nJ:ېN dS+>bq!|:c_}k5cW3_/ǵ+)9?i5p&bvy\M%;dNߛu=|c!-PD6;J2ۖnB:t?JΏE~V$hebd!tn'xljM\R.#/|)vd5N~ߴvs,9CU#ڞe=FAyr:ЬJƑgGεn9Ǿ<8ή <;&\NO?Q>BdzK˃$! mŴ6ji.Q6uJ 9;ޝ[ aa8?t@Ϳ*(RT8h-eP6Q2 esIX'AȎ TQoF[Bޯ^XM B5l?ߙT4҅8|ǬI='RM)zULIfcZs=-~[|*k?jy^??ɶmըyD>^)KCR6 ??;IⱜnmeCϓhELrjjTDf~BĬo]qv)ڛ>J }J?w/&\MQ6ThdLꚸf]gFF)%ln"z~`ž˷yNj1lKϑERJM8jSe51_(4Y&'\GesZ+L/Dn+9kkL,rp}ݡ{`v +{]?xu`| +vCdJ#YQnH 4>5gۖ&e I`#6$h_K=/mhk bKݓI?V)Y5Lzᠽ ŎHTor%{ux{Ҷ=@cb۠=S5D!Q\tt%{}suQ_%totϺ:+X/R81Dۮﺄ6PJʖd>}؋e='Zlo3A)ϿGA?J%-kKg:CwS]ޟ?J;0_:|aܴjTc7Y&}9dSzY֏;C'_kChmiPAxqzǸ\Cm+3'xDX833!2-͗uys肖Ǐ.fD/E-T'O1y7wK-9j?&zy1PPǘ{XK + |D5Wd_+ +LpS]ɥyԶzk/"<7zcGH7?O 礟q홥^u_gվ_' M%/ _.:أ,ș5-484Ÿal*`;J7d6slQy Wd_e>m3-WyH:ʦp52+M"+`g*]-t@u>ɚiI+Y:x辴YrjRtR {f{:3UX2}p$#M;8ߡ^ϱȝ0wl2쑾rٸ`A,PS]=#%俥 I/ /~kW:<h?ZdHJt+oؐuH`?dz_+kcٟҝT\ <~M vLEz#`M3m&"jB,n X!>hA:ؐfG*ȏIۇ-v&=Q>">t +'^dEZC%$j +>wd* ?&#d8Lq%B2ā r#=h sbDZb{g><˾nKoaMad]618+y<'BTe ۃn[oI5=>g0A!)pMHo}آ2c).M^ +yڼrU7x@d1?$c‹J:caFCNs9r'ZM94ˁ/hyh묧.]\Iۊn4琛EO=s*6O gve2Mݢ {w5dx~_\HzTkwmycp1tj}T]ұɹ\CVrz3B)H#@MUӮꞠn`*}ܹ| |G׾~Fv߸w|+~~jՌ=E]Ulo5LF}<znO}@%-=^*Y^T#U?+1bh{h. HԬW+ 7^Vʏ!{~[Gmץkg>#c~ψ Fnom!]S3}e̸߈C|'#VQSde7ᢼ.>ݤ?vj_vC0_V9I^Ɂx{gZ(ͻۋ.ʋmJnӸ$٣SO9~h+ոFv:Q䆫F/_ɔ{g'$Z3A:]._#;^+cJi3ba1[`grw=., R6g N7 vP+ 5iEVl=J|ЛOşgK2}r  "de5dZZEʦ}۪DJm?yҹeصՏL4$/>Nv*6ht!heHz6MZ| UG ;lMUвǓ͋k7n P57>ԩ3W3[=I_ rXo~%X +=.v!Wa=A6n'Yëī>?fc~C=NɶGԪt5NЭaw|Udw"z0Z{chLHB$T nSgڎ!jHl>l5yYQj`sYc֨\ܪY̙~XX['RWc]ξs6c1xGIebj϶}rn59G-͸Z ?t蒘C@ӷپ=8{cpmc,qbehUAW9Ckmäsgl=,r8WO +_叭qM Zے L-QGM(Q[|TN!TtܨȐj2}DŽ鹦Tll͝[qyxFk iL\ܒ J)2B0yZ?j!n(r,(o]0בSgB;R㱌3F;{S$8gRR΋VQ/Q^v-w +ĺ%\e ;L 1KoO5Q/Jf,y 'a V=6,kiewwԱTQH>CC&g! س}_ѳ#M7}Ʌj'4!z':sMjΙL|W::$ʌnHS\PW.G{' $ginF|ܨgVISm#V %hrHO(k߽cqQ3]Tv 2j deR@:~ Ԫj&o!뺌}-&LX9{79͙5fi#[-ycu^DtTK[276a72oy̫򅍹cgEsWK)۟BwIcV4abBe5K^PۢVt[T3c (!pcDd$$'V]1xwzyXLS)Qc*6v$@0Ble{eB"rbq4l|">T`O켘΃ +#R0FyCbiwuMسe&]=-.{CdE{11t{B/T,'t'ru&[(隽4j$ƾp4k4cf&oC3n/77ܸ|1(4E c#|5D&/`һviwT/4|/ӡB߱!Kd{¯ԆOas{n` '&e9]Dg8X&VMH9&dt Uؽ-C#_J:=X +Pπ\sPCN-3woQ[J+2 5V9M5̗ 'Q+v4%7?1iDK.#D/.`=:|OCCrB_d׬C'9˶Y@-t6>y5cGs9 I:*g<;qx>ʿ_qY< 3#[z0ay!9/: jTJՀ\hBg_=tpqy_>?2gr~Qmpǃ= fǨf9@χI[|WϚ}'`Q,E}hF|X*1霟*_o ۣ_6g!Re6UYݵ>Pa-.k|'9Ɨɫ!%//Ag}dz҉'yc@YjV*^hɊ[!Uuo@ ?xէO?O_!vO:z2<0QCmbH6Zc΁?P1ox}RrD*6S5/(leZ6:MPРXڔ+.k̦8<8g0Hw͑TH%<:n!$/P@ML9zzYMzkՌgwr j$o[C-mŶ"5|c-fs 1(ԞCfm)WO|_L7+,]NGP&`V[xp\Q1wW^J{9\,[fO m/>&{%&Za9R1= \;_&gQ90țf_I]3v_>dbJΜ~Z>Ih:oa +eX$ o9r4Q;u^ 5mgī&^ѺeJ Cog؉:otww]76? o S? +WA1y@+SvޚSq 69iŭݚ@fsӋae?{C}v"(OD!n,[c]?J1YU59f(XbGlOR<'gaPh+ʩLߵ6Uul^&ZTY+2B.$IDglU?{E!4xGKW $Nz]w5=1`n!۶%!ȅ1qqҙBE|}{@ L +KP7eɛs7(@βE,WW=Gɷ8ebgq{T*śQp g"mNv ڂ$_q XI/?yYѡy/<$:Y{=HGDS6ƣl_tʲB&LyO*D7(ZRS3߄-*Uh`46JKyS};}AnpD:N&?6 wr6g0c?y/(aˑ+2Iz4|?g[GVWw[~X' = a\)[&|*)ʣ|άg!O;lػƞ{oO;6qǦ{{ЂϟIzxӴ [`Cp/K!Ag6۳է94C5juQN q_|㟗CUjS0W˛ڄ왮X\h֑::[ Rޮ` k\;ȷ}O!IED-X"n_@Ca_$ҙ%t1a=?=1j굡^tSMM?ι}:tΟDzЭt1=?j~Ə=>}o$%\{C +COckc͟N* %+珼ޟ9M:ՅfzT܈J-L~JP>Q#NJ2>V^ʋ{=WPZ/"[H $9nlG{2^Ky.?ɂGEΪ[sky_'Q61]y!MH۽P]cQ:]NimtXkD1cc1C _:&۹%ƌ{m7}n86>%d:=ۯ% \2.'H*[KWJ GIxmLX$옉b9'Mv {#:HMNM/ش"AMֿOa»[sdiD%mHnI5Cw$e"%B)6|NI&]s,,!Gc}#mD=sV ژ >tAI_ kk"N(5[׿Va)QgC8=`ڎIκmz?r߅+OhTz +gp57P?֍FbͶI0c@ZjF~ZZyKAXd:svU5mZ_'55{eaalxpȜ + 6Flmb|Kstd%Qt@Am̸ͨ3|ާݶ;D6j;| ~9sT-Uie .d?5۽qv^:f 2 SVGutd7al<#_1#r…bIi$M{t.,mcs.bԾz#E 8$S݌5^3!NJ, Hjb˸mp(HƦK>}b/~3Ջ Lղ`.q)Ǘ`/kG-<{5r>3V'?w5i.ƙqQgcYV. bA4,9³Dl3*i$V'0frQc8pw~RKz=tm +8=`b*{89WpЖ-#| :GOx̋f=Y3(*=,l5f̀lvkW]:қqN?e?R 2. ]I{Kp .T*Ͻ>$:\ɫ/hCM-u%wPM_;_ Zڽ*ۺ2=%1Hx+H:Wf6/Ar^@{=6D!m*H2a<CWMCdދ?>WCG;s*27t҂LGsox|emDT`d<(RV}I7Tdd44k'Jc9?ᕞ36眈cwg@UǶ_qgCIXBԜȃ%VeE!?UsUD\%U:sX l9p qH%FԜ]fu[] B`7D-kl9euY5?4w K!4Vܺ) "O(ͱߍh|<؎(3&yEer~Dh'™=r Ƥf,Ş;c&xLXq uJ,0bٻx dF&?愚6S3^"^BM\T3v_Y'Hg?yLu-Y{i#.vL`Σ1cIVր^Wc[z pvB7h8*){g, 3m9 cJKn).ў'fgP;Q=)2_./V&b͡wdfM˕AN+g(m\~*kΌɣ3]"e[}ޛA7SUm; bgp{c|ͱy`>g&.Aº5#~l͍Ϲ7đ#r:"tô}Ck|ђtcK%rf+]]?kftѰ= w2nj#`_.ٺ3'&-_&=8uкʎo;R':k&ZΰѬCFP$GSn܈CDLO`af0p뜥z~D)Cυ}tu&!TdhtXǧ {mUኟq^A\EF&p%u SpnZ5I;;n'$v}vM >ܰ .._j$"l@rx&=$h$ +q>T?3qFyB/x9w+m]tnˤ)wE1= eG[JL7>alX@_mZc?CS2nT4َ%dk; 9ml./orwz-///R38?IYKvV]k|muY?ފ ?DFl ȚQzy>VHpW1&WϮ9Q lEλ?݃Y>:;''BQtW %E'{]:Nî˫Ƨ1 <'" p.cgM\ALؒ{54yooҹz@qϮYo!2wG75:wC_A<)ꋸskV% 2m 4ϷAz]]0rt$ 8~qϿdeI?v3̭VnRj| Gv~6kUh1baBsL3_#pe'B9cwՕ}0ߩ|*kxx7έxAݗq d>#/K뿪ԑ\̸͘U=F7s:`{[=Ƃ/q'lZ 70LmȇUQaI}kA +?ݿ.)gsZ0ѣrye2QMv줟 nKYfjK NwrNؑkLx5ݻ)눓;s5nAd\WW~^77\u2,.[差>g6u,9]D'ݿ(vp>1U^k$^XR\O_k:Tp@}Riܡۜ%e޺OIΜ߯Gᕱ.U '1 z~)>йwM& DU"5I[]˾#sC޽_m#k+`Vh uA&,5ITی;8rTu~L/ؕZJǏvῃ աwl~Зjk]&pM"}aOҏH"O2GJ~dչ41oSNnY|TC5R֤Xm ռHCB@8.=#ر"uR"I fo bbPdvn=)ɥv|};.?L!>KT)i6ixﰍϞ\6wr F.GPWFk:tC҃>5=^6ɯ%ʍ Ua#R\b_t-uǪ +5$l]H/1&/9g]٪\nC&\+ϕ.{;Ck N]c'T?{|yIjüI=gՁ $cʔw?wab"l/d-tN51w{_2@F;&Ad 'n,9akv8| .$#]qڂ'6>1(͙L}> &6LgkJ&~uIZ%-ZMŪ؅1*z,$ cH :ƑE,=y䏘 ṱnO~mc)`ΟK|W@x::VR@޶""ڗ 1d<4̺Gd8,>'p8M wv:FP=cNWcH`$:y^%+)zTA$@W( xvx_޺U/e۪#t`U=R{~M +=<z5&S6:!UY>QTkhƎ-_yuynyk9OwmK۩m{KۨG\֦*{ワCub +9DsUn-6[3*nN^܍}`0fM;2])/_׀VI RȼfrL,K$wZM揔m^B?%ʾ VtؠD*'%DD"< +DTm/]>J+B|Z ZqߑOį =[~Dʛ zv:X\pP6x VdK}^;5>MK|\[ m=(ƩrWMH0_[>oж$oS⋩N*^?k9.2xTΕ[18;<Lvmmř:9#Eyޗ HN k9xcoG8 +졫|u@_g}cMm\0/gDO[67 +rĒTg_z㛷kq1\"^Tu+oj\S5H rj3eՕXkt`:S4 R<,_CPTq_6U_9A]@`xV`1>Sc MM*Z( +qm OYEܶE+1B/I[C'R}Z!.%'}20p]ڍٱXB IЮZ XjY iF2NiPs`@wdH>9$A֑BNP=Ą4!;=Xj39V5`b#iö8#FƎ1 GM &݇_W+ڦGy1q/AM.;b~LUvwXmMq9 ѲNIt0ț|k25ﮖAk KXQHpX5W󮥍qdcn| n}wIx*'x V"Y5o[-0'ێY_;‡1_Ai+n158+6kPq 9׬|'1炨 ^ꕽ8QʣaC*۷+΋]GZ5_p̆I`DD؍s7<|XU۫L"sDvȽz=C|%7g8z%,퇑H2՛L-u!PQݩp2a32Fc({Ͻ(Ҏ!}+_Rţ?%ؾn|M=hdRVQg,3u|(2餜H3SK㺔t#u&t%iq\[q͘[&~N>[ԋ\fH?AӇediQOO|C2[1įy< Bۋ3> 3{7Qw2qAFޟsIP;*kf(Lq{U׃ !Z n/7x AHRNs<]ؓ~vˊ +rmOi=?і˫8|ƈu#2-vyQG8 q|X_AuS>s]O*MA9TM_ØQ0T)6X#vb /s1TA&_ӼD<8W94Cg|?Kn;#V zbJPU(/؞{4n؍Mc7D*OF)ruU,4f6f>nӻ [ +[QCP%)Ry<mclxt̺М#sNE^bՓQg<80[txeW%A !l[UD>׹u">ܱ!js5G4]԰)ncAJ[|_+ER|%wM@>gN@-NmF+?;LOL-ApG[=><,`adݑUc`; JZ;ڲr=>Xr5?lZ5DMt773ȿ?9dmFj 1O7j|{FEUW'B\jjDr=avXYGDv +<e zϟ2ޱX!)!_h[նv`mPoRNg[9H̜اls?"Q6Bسl3g Aαe)ɮrQ!YJ3j&K9Am}ˆbۛFcۣlO M7G$ޱ#NUC۷pv~KP*_ݶtH֮6{2=t/)Q:C#Y>2=:߇{5m9r(=<~˵򫨍zMWѭO=xA q46`Mxp>FH)0XFu hJЏn}h^l3r`S&|^,$KqF]>*̕yf¯M ne.*17bcLR6ñwpeq Q]SO=ao}:Q^gIp5Ij?xu5k> 5"UĠ>Edl{lsgxHő26vY1_$#p#_s(>&C̣g6FAj"<Ğ-<`(+ٹn͒߯^?}ct|h|48[0G z*éƷC˂~9z*BҲ43^ s\lX"gn,\3kCg +^U#tǹ#6WС +hЈ|<ְ<]w+I;NauPx3/7EuGCZrL'%kFmA~ߪEtσ[lɺzOX/_žVAm6AYm`\⬑.˅킲4߇fmَH!j®S7 rח40)&Zdڢry]PZg~&PT{e~l+}' +[_=vmO;  +UX7L?==1 \&28}+TM l?ǟPytpUvH(urCϫ;3X40sD#,$:\p'75"sWq:߹um*ig79K`'dpHNzSGM>(s8k#\jmc!Ș +,vsDl<˝Uȱky{ZXM DS>cx'/찺6":iB>6AuA iY!ȵ6⇶Cc½J=sSytȽ"[d,_2P*v&A~R!ƂX}ɲ|WGAndFLmiv)"Y0jyu*km:6Iҙ1Ů}G6ƛ-A-,Բ:70%ѲUbZ!wFSҢz)Z`7T{>#|wĕ$_XńX -;!=_>rKW:(w)ek[N?EӜdD%mIJ-)NIwK۵mz $u)}a\'jS OVxˍi_w>朑 }WWUMwoXq b 2SgrG8\yuӽ8SĤoYp@X{x#6HPI`dxlڤuW9c\Fy%r՞_Ud@\ҿ6n)ӿA~lOG;*15145Rk5I{Ugg\=* >6\:ml)gβ$ K] {<Syr=ݛ7_T"և\s,jSW^M6Gy +}"h< 56$'ɿEmQF:=(rحN)?YJNTZ1_Ss*knն8h8vWe_zUn|t{7 wr{'욽S'A[NO_=>}~vm?]5 uCekmN n0~e񼫣sn~F_]pBCF|nAy||s|uA|ώ-6{|uX61$vc ƺgh{wlz@^8oG$-iQ URu_-K;'v,YqGV뾏Hh:m. B^ ac+Ђ;XmrAΕ5Kzg&8u\q@x)_+Yo 8Rv.c3wM.sbrkvھOcnf9Zqڙ]$zɭnE@nun( (U|uwWܽI~J +=Rc4TuujEs&K ce9@`w?ԥn n"ۋYrÿа$R(Q?V?|| y)k96G<Γ2aV/x]>ORN95'̟xҗv˾+\:+'l}=aىіeg 䓺nu5Ua]}:hX=$uw]syS ?)ˋ7ӾA֮3Wo#E _B]MIWL&&|w'l/2~S=!rr_IӮo-d8M$ +À1PsRƒ\hB7?۰p8EAl59z3p9,W-j}q2fL=PGz_#Lx<.RԌ e߲೛Ӷ.S%젾?X1Nښ?B[ɋO 56W.~1ݿv2jr`ߟYs9]t" l`^]bq?ЁN7.u PZ{v&5de|\ja,Z&P - +k5s-y~Lp"! :0c:eʗ[J7+la rI=$J'C}g?H[I֧ q|8^jF1xW|EӃt8py,2= ׊cq$9#WJΥm+^cw]lR^gGcjyֿ:U_*1ro7({'rY*o+wLpȄ{My/;>k7)TIbЙx|Yœ$ruwD)G<ǭT2`Q^I!A97<%]с_8AU8*2T}/<[Cb呀(yF GSi1 9x)uIBHyO!iQq/H+ݿ)36Ty;J(Dys@T8r]csθs;yoQ F0*5+G2qD*V1yusG\Ւo_ߟcJkL6 Kס:un]tRw\iނ+Z= Ǎa j6`=~G4eF._E(~|]>W;X8a=]Oܽ8n'96N>Gzo:|`ׂk=tOrФ6 Z +IÃ7~#|%{DqKo6@D.lK,e v&5=ĊŚ cI$6,e}aiߛ{s9s3<ɥU/)y_j;zeM:yXأWY04GwX^ YyJ/5w2X֐exVW{!=p ^!ϵZ|Ec>*t(~rf{׿ΐ6M7I +^4DZ>3(J} YvwU_YJ+qFsHIa}T1OĂmx|`m^Yvlp_~EgF>{Olq4}l2R(hRԧ{BOی!axFٞSZ_=+o52ݤ]?uJRTIH+zNޯRSD)3|`"6qOQށ37PSQS84#K7z(ƷMFA^8 H)|9ѭj +` {8*(*/^Dj)hU!#2l*O^c8p/924.\Ma >Ot/3EzC"K)|x&YS`TG(B8L3BY-ѬΣBA%)b!e69)'3SeK(&(28|ۼS{Qŧ~Oݱlo}ܷlqJu M.?*nX1шYej˟ndbaOP4'U /(.ϴ"㣌N'l=>Y6q@IEEۂw-Æ*e=@Eyh[ -j*rq ^#o;}pI&'jfp*bֿpT{*4yB&Ob_䥄>=vl#>q赾7kc61'gIIA OA^V^ULSh `>K%eB?  CE̹̲>,)U!l٘CȬvIgu)g%u|6C&8DR<*DFYpBs1E1%%ʊE⣋£:>,r\۴n G= +zZ\,LϽ7ao;{6m}xO"CQ܁lzeosdYQQU(4YJhڻOW{7m0:wDGθCN|^\S\'/.yʗK^(@ʼ-A3<-Q=[#/|ΓR@l?_C=+*_[19;0jKƠ"蘃ӜNF';@l4L/zDU=BW3YMJ|:_U3'lŶ`R:K +kxk`{^xY/ WCdYnjҺ|؟wc&87zoދ7wլ;yFz [oCF0I ֶoR!\1Njas;4mU#aMd{*Nw9c xG5:z7ވoH> Z9i7kv_0*a W=Jc<&eXbSSCOȁXc:_vGjq cg{6⯕X MDsþzO|VjƖU-Hے>aAoC$!ލ{@f [FVO r+p`ފ d}[.K&UQHVw6C~:X2`?xOؚ=v1VЯ:8{+hb*6w@^dʑд1/yө.\%g' _"SI (SwW5ctp!A͚49,b=#3.4KC7$kLFmX{bs۫ +WC_ג_kcsR؂!x}pLΠ4-,Sjʉɜt =L}k +:zC~0=ƅTFJ + +宩 t &g,tw_zg*()]SQP0jZE>MV_xsĂNU)+ZQ4({h< +JZYټP +1gn=H֌19KAN%mSې9{?5K{X +>ߔUgyG7Ydz7yϵn1d.TL|ԩfK)M=5#kKqi<vTҕ?E +@{9'nnΕwB9PR݈tjI͘XGW;^Z/[sx8EA7Ut7veگ+ijjz8_h@2Ў?Ok7A)=tü|䛩!Z3/&]XGwy_Q=Z8i FJL{ >:G'/zwzHm٫xl4һv61ɎY3Y3+'tPWxoݪFPoQS\Z5z@ +8kai#5ٷi5#9h%X%F 7cO ލs?~ 9zT%L7 23Foۆ2mZwRt,I%N@&뗝;X +z`ˣh_kTǞ`ժ^9BR6b&eV~t5"ƣ}|˟n߻0"MsV)>4Dx6aE6O~2~2%S zRzakDC_xtX7eX+VG;w$-K+ {Dܜ5'=vRk"G +I.J펊jDOg-&d(4Fp&?b؃/Pvn᧾s ^ā0l$u [eނa2GƳu8|mfp'U| taƍͳYBճގ:;'mȶ O"ka+ѱS!C'iZvgaW/mA#SrR+?L?%͉ l̍%?q7 :Iܹ.GZC#"24x;Aadt+4 a}Kڰő-B0TW߆o7(fVk_ z I14xsl 'Fm*IoK$,=.{gs<ax" :&TـUzSJs>/w4'CV5h{3ȏ #h-Y?=,4S!c?HnjL>0ۛ*-,!=g_aaCIdr@^HY3EdJLkb z~< Q@8,O=з 2 kJysm1L+XgLF|>R= E9 z\fznHRn#  gAuN&UzVuYu?qZ0IGz5nRMN 7,c樓$CU:+UfoS^nԋt~F <+RzS#ԟl,C6}ɴ rAm>̤6RCP2 +D4zY!wvrЋ! Dɴs_݅\PKk>Ȥd%JžCvvXݓ~/ʦ Lko>d3CXy`2W?Ldw8_h2NA :5<ǐq`gG >}u E #L6l ?ae];z <ۿZ>xE\`nmYd|ㆍxغQ~.վ =B+F@ڍ#yNϿ s;i&I,TǶ$x?_e?&/!6"Z}~wM/M;+jx? Xs&łNvyiMkm-̠_aĵ#2jTx٫;͚ JKOÂv[r{ĐL.%V.$S3+3KeӬ'kofT- ^SN{1Η7g ?wԵ x*x:\'jx椮5V}N6aC-\E$c졶w 02^ M3zPR{MKwRUw:,"D¦m]NemexVQ쁸Hd:Oa +c|MnТ(=_R:M##/}OѾ)A6e5c}K +|-TأyS.ݢ>5q~lBG⸱RV[Pe#S4C剙'۹"c9Ҙۉ]u3L_2 `scœkO +jOd8'Ub-,g ˖ư~ +e&cεŜ7 +_A4tYf.D!m[|y҅!]؇,}xܢF e:0GԷC7&ˈ Fw4~3va.d!3~>ǡ6d}!yxZ'z-\BǩnفcŞGm;ԖQ^Gk6R}Ӷ딁۱ GeT*ޘ4MbRښ;TY".n1z{rEfUHӧd`|R;1áf]9G'QcO +Ag Ti`J;dBS KbQTaJڣܐ+By]BY,?i +%2|&S.7!ʒ?_ +;R]:kR^ʒ ++^wՔ7a۲>a bRƮf4ῳvvl^{;>NQ+ +aFІ|- Y3S~weAa#* e-+hv;kOSvu +rM#QּNGyg=XT8ur oU9kf]_6jxjؾ6XϚ׾ݫqԿ3o!=Ic2ӥO ~ƍg ߮zӣP-o6/rA!v(bB7,?^⏿=w"*A+#AYNαG?b, cȠ'bxOt-·M+kImǂ?gU߰%#[74(^'Hd+.]CvT*0J59BU^Q$|^ {8C/7|!?ONZɃ_v*W>^ 21Hc3=\t"|ɑǠ˳I~$~d .NgvQBJRL"TX%6 #A@$[};^1eHʜ "\*T^$X-V=X$MDc̥͢\&D/þ17I&0$מw򉹰YW/*o}맼~/?,_qm~.!`]P-/9sCFFCvu~aPΥVgo);<}Ƀ:wGZ?u|sx珂OM?#>Oei#.L?߉j]]!lHU PO -.߈svY~y0)w} 7pS7=)*eO*#fEߡQ7(0F)(T:e;s)2iio\/3n70HٓĆyG D]uiG𝌼=NO~og{on5QWwyp׳cס,nO=E]sw(dCceOG^~"5>ܢns 碯lwKQu]qo5kN7І7(wٕox"5v_91wؑS޶kz5YsRWK,PD?%kG9f?U_ ry}:_?' w$H.L5˛U {Qck7ߺMŀd|q싅^z?7zC+v^Ɓ՛1.6KV:s~5N Uongo٘\[\{LvЙ"ȦCHk®q`[#ڱ0 > >'F.2>Hn֯7{2Tt!CƫO㏂ηHl;cR!0( \/b#ǑAD+t+vWMo[,Uͣ&Щ17ɰaMʟUp{ h?Nooz3m(_E2C1t=d8YĝKY#8!rIC߾k' +'X1b7rOqt6M^Loa2? ĬdӉ [)'{_r]nLNjȞxV {&g9~<GN߻fx/t5WЭ{u8(HIWmmP!(J)Rމ~0\oV>un9SU ^O$2TƢ :Yp1jQC}̳c3Z672gKw©U5В1g]~ܞŗO\qOZ?=Ng8XH9eЎawG9&\>'lqDhqʄ[AMvCԽݩ2Dxϛ,l>8<63 d_'y}!@Q` +N|>nd82N0 " ȿ T$G#.NaEŞ~淚'S;8q3md8ˉ=lڱkZ0rdFKg7^Dؽl"9Z>EͧGΝ]'fs=ٟxghE3L~Ng0a>Ig!?@}7uK +;L-KF=hS5E(pIXIط拐}1Es? [AG8a[-`qꋩT'\n4DuEz)O?vފ ҄҃Κ1^ev֌<MVVORj+_qM]~ʤ㽡[? QW\n*Gzj)V{)QVXZq#~mCp8+pXk6dM{=Lx@oGC0O{ヲ"AyLj`_z'keG/l& KlJ.5|1pվ{^љ7zĔ܊nZYg@Bs[iK l߭O +ԋAɥY;%;;UUE]'$神T2w;ŹDB#3Ux_kӊf[ ž<}0l>>~#HoyycuR2ImT!"lmБQKw^&Rޱ"}fȎa8ws55o??P=mCwbsaPl2 )i?Zf +>*ר$.E~XRw!O\x{9&ذ>}oEcsyC^ ]v\A?udXWj+Ǻ7N$[Cqku*9aԢ>ȃ.닇#=kb.TmaQ@&ځ3)l | |\_eF=Ǿ8y sݾm#ǥxJ2tlQ. 3<=1dv7eX+_sk6fo-Sm:փޗkX <k_ګ?ЭsF=|I+׏^%hև=\|(xLAt0F!{')Z}0t65shR =G#EYARUz<;S5%ZѓDV)S8SӰ o;^"ߞd}-`ʛ+>B[\?qVBi_[gs?B8,Ա=Y,BLO+ீ0 +elF7 =}p0UA l9~6]*? +D^',QʋOEaJTBïiɒ/՜6i͍:<)"L +u264i#a8ˑ٤66ts0/::^c' ʯm~G?9x, + 5>7։*nZ"v7wRT-ot|:]3#Uu6"qjgAV'xDgg֌/T cԩbesCd<' M6Ovbڏ%YA ?#nŽiׂ{GG? ;|ox?QO/yy{1+nȪw7s~C&'!|Ϯ5/ư K y'G'`ƾߔϤnb3sG eme߸}K.(^ܟš>^+XߦUSY'>hdb\a[DAnH>nzuC 走sXb3.%%'"ټPmR6KR{eMnJxuCY}/YQ)ybi@?k@O2=0sb 4l٠L"T`W"_ye2y i }N4J!h L}1@N``%_($W5WÑ1%"|% t,KQz^(+ +$ϒ"7Nqo Y \%D7 6}oWpvWy& Oa]V5惖e2}xvBz???~\f|097_;é]K5^|<mNf߭q ya{U+G\߱㏵srWS܈6uz rѕK@>I `vF6bcbWqxkPވ1bՐ[ce=?dAZ㊔;؂u)_phK ˜>ԳEᏦjBG1^! :91b<O~ +h RcV3Q ino<( ey]ty ;+ϋ9ٟ?7d*5|?ػ!C I]B# Rw?EɉI)";t8z +ȈQOQЌFɱr6% 2u@62d"g0LOʮoL >bD_hWS<HfX4?t7lL&t/iPN bRex1j+.^+7ؼ67_0qf Vj6kU[x|{BY/`{f(iغ;-cb'sT QІ2ca'zX۪L1#ٜŰk߶6 RMz8[B[.GwEYDC_U 4nVS9'wc`7[# +l=Cگkx6VK%p80 Ozj ,UX ΙCf>ġ`cIm^!ֶ)vyzם/ksc֘D^ak.ɳqXW_k7VZƜ:ZYm? :AT{tGxX֏dAilKs#.i˲#?۾~~J ~ޱoW˞eTsPdvֽXB&bnXd49()/˗{f-A_#¥yCʆ0WSٽ~$s88u>e⏏c2W߆?p\6`ǰ}Cn@|+k-w@G^?{~ 8x[eg2;jsߢhM&Y!#pKy9/ 5^OpݭЁL͋sO?g<|:u7/pT744g#VL^l-H+x lxoRվce zyg2՞ְ״|*g?-@lG\ pԹY1 GF\`^2q:u/iسz`oy%ۏ&%еamnMw&`x.O"X)V7aԏ2C&SCI@^GW9 +H)T0S0d"bu?} A~Cv/H+s GIg2é.?CkLy>Mț?jFI|a1q WrΖ4\qȄ]CI^f%[73ەZ,JuZs xZ[Өo"ARvaƖϗS? _NJ07g/S=1_l7ta9? |9{Wuiہ?T{q31plSm`73_ +L{=v d)}|1*.8g񵭍x+"j6Z3t|aپy})6>T9ZPu#sW0^YLט*R<f4gmغlfw/qI-w h =B?#kN±P2wĹNj J!Sdُ;#;sc8er* V aǎ ة{%ʰSgm(Zՠ;"J=^źrFAD;l1gra;{o7 +G +\()ՋEQw@*rؘY_x\LZ 3˜#%;60 f8Ï O[w+ËusB_Y"i~Ȇ˅[K|w-kkAY.wC_G+Ԅّkqs9]K~ɚn}涌|zX._Ɯ&h4v%쏡?_>;ߟQB\^R qΘS>CȤ?#t9l 1yK<߮+bqY[3}l]Io0x#qA$le VkKc+档QAW}Eroӵc{/ SNJK鞲o! xab_2 ,Ut'|` _\_q[)1 IAzd0c2.~{A"{|g5G~@[6oz?#z}r8wϑvzث?+unmvt 8A #??~˗~.Pgģo@ANܼnxCO7\E٢I%ϻi[/_!o#}GUB}xKCgyҷ9l>Lx?A{rUq~9]<‹6.pd织g/6D9{6.:dÂk{~uCV]_8=5{ۤ]cپg_o_we/j٘C:SA]L1/G,)ߎ 6dϕ+=W^>J sc&)-OCWmD~Imv]9_/m9K='GB} ק56 vOxzطÎ- 3P[Ŀ(sDadx k19O[$ +K/$M.)C$mzr]gwȯed~8SFK81EAxz2~)xjYCt%>˒}=M(#J&KSN-->ōl!1dѭb,a/A 00óg]^zK۲&~>ԏ-LS62beb2Q}HTٶApRZV \ mJՍ-I;.X< C+ia}|\s8eu0;нZ+>co[P~P '/-.bDG)?> + ځqyḂΚ8:AOڌ;t:`ҐM?0W=^,ЎF-_sgk>RIp/bD;=p8a֌tؖjLo@ZTK-́xO$yݿhBVy `}2}l]xeQG/䔌fωD_+&ӕ${53=ϡYm7^Ьf0C6X( ywL@rB+yS3>?aS$ 95\%AP)xqKgzag 6@]QX7WݮPl5lCUiV@~K";Gd=n&bDFK{oK[I.P~4~;ԕ]s YF5|旾"=Eu@ڤ]h +Nz<]WҰLNBBKOvSI95G{Ϯ00$JǑRe\6RW^L*(]z:UmLdzd5wbuRc4q8m0.FsYpۼ4O"ef~ (QR=7I3?Mb2>x`ogzLV:y|Q_r)?L.Uoq9IY>q-Vxʱ N9cO0ykzOzP^#@e^"r +/\ -],J!QX"V%UM*gv&k"ZOvO𯘤]=p/pQ|om&V*H*s+Ϣ}\[ _'YjEC|!s*MM4Ԛy6r=wfr*DZ܊٢ϪS5L2GU#2jŎ^/r=#qUY5c|" ׻V T5h2:UG#>W/Ikdİgwaײهx= J\%+ׄZ· xǎ]˾A'Z;=z: vC҄˥cSSmj{J a&{ )\;-i1,yvd.)byJOyJOE+Jl{1.z0F;6ccnC;1J/ILd] #^4T^jY;.b[* ~iXksX#F%jeYŝTab|'oq-gÅG b|8=r~WP_0Hl8"^qb<)͈= +@ثq5"?~qK\nHkpbſib#ĻB+^<ڳGn)k |СS} W+lN!zA +A|X) -# b"EG\bmI]scUDYQ}` ۜU#-dUp_(]S +J;X.W/gsm=kQ?fDMpMw⛘Cnd׏&gx&zn? + 1onjn 5p "7@%pvo:9̅&~! _s:ǰ! cKNү_MIms_Ln02=ut?f_2))6/IvuxZ٪ tSc5LYU OoLr̼T$BwR)궊C]~:*D}%=o&[C.e Y2la'cQm{tf|xqxG +-+'w"CMSgu\VY7~hT1.PA$, F@n ;FQѱ@1Ǡl)QkD"^:x8z?swXs8^u7/H709im92g,+2$wINSZw,'xrsvM{|MֈK팿!eO xJ1'ԭ)LKs}<2y'$!cv|ۧnh뚻6lo+njUԥfS[%TSײlf1_1l~0NuN(9ig79܌eyYtaZK2z7_ѣ<ٰRu6see.f>1sمB&}$bdWW_>~!6+,HnFwigE}-J|_ )cۗں0kXi?$Z9l\$3 STY#5\[0I_tV߬f 6AߖduMf2#uB'Թ0uȔF=r!/v^(}ؗ۫5@-uRZI[G]sQexsTc7 fLk֜S bTΥ]<ڋصE; +e0H w9{tX +k*=X+Hrl^ 3idS =<¤P",pu^zs-ϰo;>9`ܰ am#w#L5!aEaWM^yZ )cͷSN4ds SW赚qV3ifLodw\k#mOD5i vWCCM; $%+gf/es)ӚU(ZZK_e~BU55gᠨF Kkal҇-ں~&>WC3EuUlf[HiM*["ώ 8|i=,s:hu>!fyUloSU]mQZG9ryCf|ҹ$m5N%7<:{lX2W[)Ϋpv[@N6@^؈"~¾[n1@{۶[XU1{Uavnv;`霼GYK:DŽ5rnF;=rX< }ɻBJhCRg3ww? Gm$テ?Js Bd?+wC{A܃\x {}mw?-x=S !Rr_!cSksJ߁텋rqu4ʽ@a)]|t41~t^Ing3wyvy{We#\DEL(aoN%&"bJLI]/ݗ()ߩӸA\I]R=tcN Xy{ g÷2WEK:679DҘ{gTcVxO4{V|ziq@OoL&JIh*C'OiRj8l|ٸ1,_YqWQr) xPOL)k[=H|UVr uEDԯ+fl=@ 铒2 +JϖFbp;o(ʫ, TVDpụ̌->2(~>:6}QzG#ʯƬ k'Q~!N-:)nXQ m~weg4~{pYNcuݿ,r +8P`=Z(8R򰇈??` #M௎x¸7~}YEuī5W+jj 5*PΐP* )ϚhqUY@V ~jo Tј`eʛ`VBj5ظT_~_):~|"*M5|T|JJ%B'%ɥ]X{U9nTqi@B_4EBO+-pQ"^gYڒR"UG.R G\;xV1!h9k 228y Fn.e2=voF$@?Rh7 8\آJ>l|J3&d V +hpK$hrCQ_KА憎+xQxx?y4 +Q>pm&+fDv {:~;gLȋG?$ܡ~[oY2c|}bRB޿o-~]_(ċ'܂ ȟ 8CG?Ig@w'ҹt*\ 8}/<3׵s|5lJk~ԋ*DyqibۇT-Cj ΃_GTϓU9?*pǯDH@rsnx{\@)d`ZXXHŌǖir9DAw1Q߁y&]36>]{O1N%caXȲM`"'K"/ɱRj\[ls& ovN5~VzvЇ[꣕]l}G.>s}uyq=f(by葶怔.^= +V7p.X1VDZgPF3A}n>*%1uy3LJo=ѹ;col7SVKQU*@r>2ƛ#UU*EXiCճeȖaw6*Hϔ-O"jk"w7ll3Z Kհ͝LakDdݗKo玕XQ>51s9UdX#5]|P JAcO+qMa*BS&ǂa㨤ͪbGduQ! cRr׃[1k_ϛw|2+á#X,DfgSے9}3mLTa|IN*.t1L8}'RAM~*| JVCgKQU?_UWۍ}7N|m޴^Tw_6,@mZA%VThlr<ë{jHxRo`׽媩&<_X6>ѪYj;v wQdT;EYc3 y9x*5 ( _c&d4tIn9t$j?Ԭ0>3E:N!Ms?tO?a7gZәw*09eEs6w8Um]Nuy3Ib +4E upսX3J4,Ǔ=hṭ̠+RՓȜ}FdJ: g*Rq>ߋ5lXÓzKvcRq)ow=ӷvq' 6u+۰'owry˖g<^S_x#Wk:{^dnr'+&Meo%I&_̍Fk_X,kI^8S8ezgnꜺŨT3$ )\io_jse :#d^p=/{Gs{Izڈ[VED,RXCgn ?ڥ0+4z4Ely-^WZn-s_ ${COy/sˇЭym&'ӧw9]v\c}8/};y +Zv|GT2"cls];䤻JCWkZ}dz5Ho h6:&;sӝ6,=ƿ=\eF6s{gI[,0raoia#y#~Ld#D+ 2̱b #b>-y`GEy.ÁO0OΝH6R 6ߜ坝W/{xo18}M_!m;Pk3d Wn/M>ܹ moĚ6 ˚rͰsYYճb),~/GrRL^2.L{ﭏ܉T5vN9}#yr/~!d4'[ʰ)dRr'svu'R2Jzߟ7 74%agObE[,~vg q/>bm)TsTRuhY*C*TT /V^/<3ti.^ݥ);DP{ {Kchh~KkSL6n-E\w+"jMLp=oqTlݙ +6݊z UמDM\37vg)vlAK>5M=WλlUxgPڥ'ZݪM%zl7λK>Ebcu r9^ݝ)v#%F|,G^9rNs]}xuir1Nu(H;` Rb߉ZRȰ/$lT4ԫdZ!4 D3L/!vMZӹcqW.`6ﮉ |y{+p`\9;<5ތԯ&?<4Iι`{҈T|Gsmݥw"Ioͪ.:IDmdȖ?nrʦn0d[NRl?C`cxJs8G~He{'}R}:ǎG*#ClҚJ/?yW9@$ )ϱX|6xn/Gȩm +KG=u!uH-(^ԝSU~68k +TWtZpMWWf{`'Z9ECW\ZY @/M]]sdfSFp =,6pXh7hLfU>+h%Yyj#L )=}:s߲-w~0VĸRKHcqARb'ӜYE%>r[J=s=[t-zQw ,3y#R>~F$ L%WGRI\Jn.ƉbJz` _~뺤BAx>;.(nZ5/Jij|ڍsD{P+Iʹ{;CjsCa*x>B|%Tm+[eV[pQYaYuE /2m}Eŭ =qy}]1jQ{}DA)l0*bJ ~JJǔF; mܿ$K+x͝,=ṛة(əLj\ +ҝ:LҹCJ>OưN1sv7/_MeUs-6X$,T6˷ a¥'_^]|oHw7%_R3\f{y!t!}{IM]^]k&ƺosA@x8D;iZߚ*B~}IVq ׅp{sؑǫbaؘ@~ДMn黺qsn SN"#;&)isq @dF-ļ˻?"U.3ӛV<`o UF嵽xyOFVu޳gԓ`iGU/N)U5ւb>3""~ܮ<̣U;=Tg;ub'}}x..buo'xO[#ʡ-V>HY${:K*+Ŷͮt[x5^oNQ d{~-^FJ! sB n>9[C =7e|:Cg/Ydts/` )= +2O] +MG*cȭ '=?7,aD/w5*I2Xk} L>i —wqw;~AoUƹΣ{'nrLH cl J;.7UkmCc',oIE/W}sU8ޚ44s`Ϧ [=bΨ y 碈q]D9*gk.[2lZg)b^|5aWYI,e*7ImHTh [,H )ݻe/=3}(9h,m=ZՌmuH'v ÊC':*W2v󹽟b~c'jFya[F<{7K9e0Ȥ?n٦h_UK֣_xm4X&c~>Pcyך93Hx:ܒ2[{!jLE=9 D&[ߝ;\?0 ńYj\QJk>(}-(jk~K6 +! @%]P{6VםɷmJU6y3#kd+zrԝZ8q뜟cl>x}KuHyH!Mv˞m;&okl=d]Ν(\ǜbc, RVis%T&rCO{9c)^yyds;i>_aZ[}-wH<2nyOGQ\?80c{/P_9; +Q ^nBߕ׌/0>칋#O0zx>֑=z`_"y5%[f0Tg۸r +9}˰9WP<qfu3O6NQ~~8%I&D<>0[qOZ'ڶ]{!,Sڧ6vdVohDq;oW}q_IJDBA/CXv?GNrGV0D?ތ4ؒSgvH`aHdxҏF0 ^_%m0i#3AVADFvK+B [gَYTz6_v|B~5 M&0HӈiI3FT}Ryܽ)CaNdkġ%K <j&QL^-R[:)E]ޙ1!-5tÔKNH`ð9dGQ]9z*)ah:RĶTU҈TJ֤gx* J3P΅OaWʳR]#c)Sd6qET#5UdfT}+jc/)cʓ~WQG!k^8sL/;wor=P_Wܗ,1.pk VZ ^rEx~U((OnUG=Ջ6ܕA>RQ2=DydsSԬ'G4ꈱj!ƴltfӟ^>+Ѥ+%S |7UD}Gwt;t ]6J|ׇOIV k*vN{3~гo1i-)?D obc(U%X>_TZ8a{U?_Jlmo45R5 .`H=G[vY?O)nFJYq_ac +=Cv ئ3>ݸ[<=[T.ڌlNY߷#TP]_[i_Uހ=b^#/Q߯kcp=u5U[Z#h[SqlL Y)+1'ս\pI3i`-4,0_Mz#_$?e|h@鑾?pMnrE6Nt4u|z@}@Zz1bŞi=kƶXK&c"1S$2L!,)G^\:w9 /3eܤ-(ʙN|t!a\:~$\ѷmJYOXU ,#)E*E Ǿ|/}FE]j~OzI$O@:8?KJ!gN9 Q-pW#-sr7I՗վf2gfZi +k0wR-G +9po"la3O齁{<~wĝ,K~vljqEgqNjC*Uv7;gaHHPO䲗oN:+4~rLOb ?E:98_ kQY);BI HC,y`GKYSVzַ[ƬXE a2OcFnebx(͝6:~ekh {41 A`˾m ۍ t e&sır\JtICc8u,iKߝQ6.ls{dY57wK_lx{#W\ok4Z =Hj+7%$K҃|Odս(WO*\UYrJ9!u(;w8ޔdxyےSIT#Cg< D <1a~J͖kT_n#UCLv88O@Jm8]N6GU"o˞g\N\#wW> |IV{{\e zyAVA98TEY +υ ?ndvbԊG4&5<I4q\Vdi\\y?پ;,`3~O`/g> +;xX;X:=*0R "b!>QDxaW//B7]HI&Kr9[L^D;`ǒ]y4%7<;&Ąm-C<6z0+ׇ~>pa?¾/E1l|u ]NyQn]3B[6DWQ܁oIgYS+I5ؔ*qLH6w W-ZIqq$6jAI7X:gg8FYm륇ϊ0FR^szk^IZcrWlYϲ6K#/|2q(gfNۮِ-G)I +v\]8rǐ@l&CS)~,O+pοGKտSտT> ;FqCk5jEB+ zMk7uH = mH؞{U_9;JK {?wRs?ᡊCC&ɴrk}9CG5?cV^? ?3c2AO=y|#2xns53YƧSlx֘LP gwDnmuka6Wr߁h"D_ٌ$Tˍ`;p K#<=-W9vFx03Ʌa H׽AԾ6嫫"vHeG#dW6'm[4xRU2yz6cr'8g3p*6D\G + DS~@׌ϙg|lkw ߅_9WB=\ ߏyycmlp܆70 k$D{i+l{oc&rL'_uW{' 3b}$Ya>"F\V`>bCw_L(gQI@r LHֳ3rAĆ[7y(G< =[ ?“>@.?Zs5I4+AbYSʤ4eǟ:1u8o'*QZ73{z7nTZ,.0,ͼ쮀?qgI6>gs5.;=v.֒^+lgcВf^'eyfo8~Ym4vT-yzl۴Jwke\vP9@-5 :f]iQYymEJ:޾Z|~W:m=t]g[H6xDQ @fcJ%r??)oQJxZ_뤪}l+[[ޒvNүABF$' _Cќ4;RnJ A8er%\x62}k"#R$LqKj?g µ3^<H.\aunX;ks_Sp!&r~m:ٻfElZسQ}&WUm.:ny;37&p]Fї_ \Y#/Ѳ&t1-4do;p}3 v*@C,1AE*9I~&%JT\łrዮu!CF2康aLZ9ҮU.i/CO?=u5G}bH0qCՍ Kc>eL-i\k5υv$66_.CYİ CԮ1 PX*'Jَ~fmIVa~{JS+7]:kn܂6!R?Q0뾨9HJlAx6p r"堭wDIDp?)@ܯawz~~먲96X>~Ș=7C<ؼX<܀ݶ¨Mda*'R:AN>`_Qȑ.J4vV')Ώ!w,` |-2W;m}>%jʲgB"?OOFXly'$ 秷^Y/ggjKg]zSRoC:m"N|暵-ί~*dQnЮ+N<6@S$?v873SF~RK-~ ݀ |ȱWsHbϱ*ϋt-4wJru7u-fN~;5< +P,{󧷃3 Ʀt HWo?4]s7B౗W=9FprB[*զs +{MJ% +D;}ܦiг[gX RZ߆QԆAaLtJ~:*5Eh&7 ϻ{gUyWn߮r+ ~ZQܦY? >^|EϬ^>o觲e $Dv/ooR) +Xڼۄz_␢7W=$5LFlm=#ƱasU7@=e,OS}ؿтvp;ҵK0}xT!ǎ}#= ?k"0r.ӧ+"?COLNnK:Kn|nTPE@ĻϬ136dAn/{xkR6FkUU 6S:~a@BPEc,Q_+8/hÒCN^;sF 3gꯝ=̅Us"+p G8e>l/Ϛhn'FiRUl;J4+7^ +KWά*Yuzi Y3ɮ)u[҆J+gVήH_ϫ7R1`N3xLJYy̯7nᯕRX~gW%#U0Q~*gϞi +WRZ1B4WO8euc/.HV[nDuSm99Sk-2j8-:~Fe+yruIX5wrEJ\]k";GlZSeGa46/Rc/emOk +|G}uT_ +^sקBB@+j ¦usj +gWVϮW +s ({y:ŦʛvRٿK ^qt:Y +Z [ 5za-;wr!_:b,yWкQccgW> +0gV=&Aw!'!GT8 m\7a +1_kE{pS@d|^==2 Rahz0ӎKl RyLkj`Iu_ I9Ɇf<7u_8?.b!đj׀z$ T*OP(+E]H+OYh#spIs!!3ٚ2zyYXCR 5!mPXsR L ڗ8 h%va,gTn}|#n?Or4>}Md N;<&L9H71 r=&Nt,ZGG"i"홿@W-xh(S +lmS]ގ1GN+XC \  ;ݻp*;#lſ;Y).6isޖaX-I1r칽NiCI9}?T[֛ Ԟ)mQǽwX+BӗЃ5Ujsdc}()ݺ/_Y-'|;CaaLJ}L&L3jtsP3BpZH EFc?qgKSɮ: +WozV9'g?2 ¡wQl&=T~ʓVgA5CZk~n( LiidoCagCa00}iysB!Os2! =|sFMKC5VB';  #vb]:c{*0{*PϺ~^*s9>QnF°9)y6uծ% +endstream endobj 37 0 obj <>stream +^ӯ0F=^JMҜ3b&R9;YFMze]I]=1U4]G#=q ;/kϡ Q}; <ʅ wWqEN)-Soj;1Gt"!z7^> ڑ`X>>J}vi&Y|O,(Ȅ:=FUx+K=x+*$,;KkIcHRb޶JJ|uR@Zȑ/Y6& T{+0YǕfvЅD*H2+G(NsXm~TPΰy>?N~v094` Lyg`\vRlg"fR6q3#F@? klc_s +C9HcvS_ q'}Sxc(³G;Y=Hfu?BAQض\}DE +9G$T.`#1֛Ǟ9|`.g)κ(Gz' Sږt.0OTⴐ:Z]?>?tz9+݃#[]S\lC%5_=i۸P ~9UYzQ/~V'" | GA'?qOICZc8w&>V,#lѓK'CNMJkzG/SgGĺcRg)'me#7 +!ʅ$,}؆;׉*)2bUVNKXoxHxVC"Cw'?ȁgLa&SĨ yb}PP8lC{.K1Xh#{6-ɚQN)I= +w+r̂'Ixrg |w) UG~;y(%:.GYY%Y̍rvd}]Xy~_Mܓ_ +~ezSl_OȆ;`x1J _u48/%k.J"xB#RU)Iv6 {̿OģePYdM\O_Ƅc6w^) >'n~E᯷z D=J#ec#>U*9*JWO.9x!g]HI +|;?+Ք$4rLڂ)yu^״tNd0#*R+^\C -coFӇ WG#k#>'Izsce'3\{Tnmúmm| F٧y7*kfyez 43e8ĄAN4Nс%*y^.rv&SBp_87wL?>\W<NQRдB6!l.;&W>Q#\q"b`'\c$|^n>Tw\!m8ØNyuM^1_r{JE—%0x{P<7(ϭ⫽]y}{OY8Raև{-3?L؟-"AC@\9z{aB;PW'%ڇv}V,]j{x/>>I+r^bѠW ׌ͦ l37z<}m/ymS]Ų}ĺk_kAcظ[QvW.vy@Y=r3aT?>y(E˜YȰ{.}L>fn5wth1xrDq E\NW'xkqi{ҷ1𿯟-KXܐU"ڴphco1LN_C"o,G?=CD}w.uc#m-_&$:w_|2F\m1(4 (ĎQԗQFbO +s!ʉS{-:4{JiCx,t){@6{9D`[*[IyX;O|Ht[B\[Z(?o!N?Ρq{ q@y|EƗ"( +(H =tJ !@@:KH i ER, UZDb=6rݝٙyߋ}6BhA)fkjł +/z|{U}OT$iSM2_޷Ǖ^r}Nv"tG: gUS\#q [l?]#T?GcfگCBe?ݭpZ?dh x_תqηvl 6*2鿬 _Q;=Qݯi 8؀ro֒-[xϹڪ7Ś[6%!oȣRU'#B??QS"%/9f1ۿU%:{2΍Vs#w3bxVyYoej5c?:m]-~0Zv4iT]IJWsɞI΃X+) }_8C!G;1ޔo_}KԤQڳ%'gĆ](wqE,0+>n} ڱV>HZUN&fG̖[>93\|E)qljm^C%O?z?Z!|ԫOvj#޸,7~?kIvk1?=ʮﳾK{VFݬ2cI+X/(41mG32&Oen:*6c?oC9~{|wUϪ<Vߗ|ggCnhR _WڀV_ 0a?ޖ)9"[k|Gk@3CјVX?ʋ裨0:R';;ZvNoUo9A1}^1윻< v헦XeNh~ڻ6e2}jX#tNYZujsZ'Ƴq^>UV2KqߟZl?yB7¾)A̾b<і +1qKSU˩Kxpbg}-' X?Zk+-gT?.QTMt;_нp,!_*Wh(*rT)Y:^{r-jW:=~󥸠'ƶ˼YNZXP$2 R -f?zȍn[)d۴1wJ?{3[/ +&>;RgnD :gc^bc1wHlp\|4cVߩ[ X36/j2]g̷O7Lˊ8昤wRAoH; ſYZVˈ"=\W%M пv.+ÞͱV I>xS|g_ ?7Vm֏Ӯv]D2*csM׹qF_}U̡Mg=;Eƽўg}f~{5KeHOys:z+}{{c,'=e:On,SilHuj@Q1W,WGG{o-+\;ua+5*ZPbrʆ,k`w0೯9(Ǎ+s 2Dsog.U?D(c^lxÛOfY)9? oA>zMi#&yP'uu&N?CY9 ަ+2ljsEOB+ͷ9X#sKs3ۇ1L뢟_8"ΔT>o^~Zo>̶R?:oKR7??WGm.1C+~܃ڃ>4>lŲxV`]|aZQ?[} O5ͺ.KC Rudkd,exD—Q'[\Uy؍}t\jÕr˝予ozXx#1KqY:did+N 'In#12 +J;Dkja?}^YJ'l녣2}+m>=NPZBX&QcψPViki1Fe:҆msma\}R=i|eV UW>-Oȭp[7oJfe4w˯Eu.j4۪:0)cT3vŜS=_71OCg{2a.;%I5p%a)l:C=_2{E/}xafh{\?wDI>"muqVuD\kmu>zxlS|vL6 Sx bRG2i>< ?Lۀ͈o}ar-enGY K[RrAnp3n?^1mN_P6 L_f[W&n#)hIy"hw{lpw7^*`N55ew%gdeŻRϗeicþ:J f,z|;8%MU5ZW^"rL{L7<'Cmg>1=(tgy|thF !!?xg5̫Z8}wGm}}4M^ԶoX,/ ;kҡ{W [??e[wn8tK]J9m1;;+YGRusgڈ+⽧4R?91yu-%V_3ydxjT̙@<Ҳ|v'A:ڜvA=l zrjrCgݧ{`Ρ6YӰ^bQ}yW?źRrZJz;ןFi+Kv9Hi^vz_~$ޤG-Gٹ=jZQ˖A u(u?K~*fF߄~Y`[[NU{`,~-{\^ǒB~gԍ@ O~儢Aﱲ#ca>/"6{_U(3߹;ѐ)FzKy~ɍ;Rw2րxHiOngb y6ǻ7||’?.oA<#?m١4eV-ȏ5*@!Re%ZV#|R>ySj0ephYXתuY]:~^Z2e?)V#wO,ۅ|]nyF!jo6b ?)J9%$5N{]Ki)m{imkݵԱE7k i-jmuF[LaS<)_|y{tAo:t$~|[F|bȾǵZ?!MgHA{,*xWfݠ_3L.8hזke'Xzl^+߉rINsoЈs:;R;iwq>~³[A̻,E+9^fP;uoо${jh瘔{< i*ڣHR{ҕ~T2Jo[ԏ+Km[CWa~fp9) +^6>%|(0a!0q Qq#7רkR#+fW";W;ҵ%'VernWՠ/W+0ux1 +zUe?Tg֍z)CV4PJj`.8/b.G X({-qᕉZaCV>a 7`0rKqӃ";Hra:[di(ZD􃆀DG0Ws2s]f(a|'y1ū*b#ff j&e~}bҹѲxrjk0?7ֵY֍ Hua[1 ώRng:>_.exWns +Z$n7<=Q^{T&aO.w &'-e_%߻Kg)@YB[ag\qԏWDݻ9 -Ip)~vt=6 XԢ3ۍ>]vɤ &RwPNN=ot*୿ow뗷^%]e1w/EQ}M{?Ac_ʷڥ.<5J]{CPG\J8~FMuT(s\nQ wlϛ+N] +^q|>lqalw=T1 MXۍKi1A>K_KҤ F +[2:i#fμ3w6*RITrƠ])VqhU0䜡?/&ҾCw.'c5Q}{0NcǽWTE> }ן%;^p|F~0{P̥< s5?s 4>[d-PwC;#Pn_M`GALY^@W~]ԑ%wVD:T=c{[ޘ8>YiLW)SVԊؽ~?D7Bxz!SR\zO&+5E-F4wω鳃r#HS9^quLg1<;)g}!VF=3^yy17YYwVEsE6~5N5#!6.U69ʖZ^?lj{ZVmN6`9, "ΝVԉtuD;1 <7Xcu#ueL5sZʨoPٗbPG=ýKWG{{w!C;ɽ.ϗT֋oE둺q&Ӧ5Wn;־NjeS >x崺LG23Z\{4;ݜBΘ<\] "{/'V:N~@oAͥH.S*'D Xc* [R 萔%iH祠 #oR;K%-IKb;*[9TgqCIW'`IaC ۟qqIx-+12?JH ۾}-fy *X7& +uHYL%vu8uʼ'f͌1S,bieDq?mX1esUc.nήx|IY^ظ qq<(QyfYqqʪwJxNGZ>Eye dz,#G&e4+>Zr8WGQI٩p_ɥJU4x& +cP䤠uj|,ہjuv5*R(^%MiG޿_w}vJC-oׯͲZo8+Zr*Ҿ ZSFksr yo|\Vඐ ̙>s&+[oZ~LԅvSg풺#=9&TI}|Z»Q #Yo4Ǩ#as,6yC{cΫ9A1rJϊ^fW +s4W_3~\0|Ϫc8~cvj&mX;.崱s-o^v>Vd௡K]5{|Rnڔey6 Au]Ҿ{6ӡwYi㢯(}ȵ-VWa鍒~~<'Ɛ^!=tyE̦2]QǤݛc/_[u4(akNJ +"3&%vh "ͱWujRFt~X*MC~X;˶3jˈ]=q/Q:Y>~$o4C̞;ñ1Ww)x7Ϫ~Ovk[)cǡgWFKeL02ϊ}1g;-OWF+xvsc55^#;}v +c0J1ʺU]fup}ЄF_^ʹ[ib>}v/]LsP58_=4h}U9ơmĻ9oB[gM 3$x/1ť|oH\]+yyx@nA -lѫW:aߖA˷o +U#UQl1=ÆŹ=t* Ms.:ZI!4(C7oX7gl%::Eywʸ'>JNv~ @A?^vgTĎ9..GW"˳VР @Ab?6#2|qGYxOA!XbMUڀ [!/={IKMVDA!bMiNBs5;n\Y.yZ\tPBy@ Dq; /;jzPdI؜oB!(ncwJ]9WjMV uQfF>+z?reqB?!6Q➏ӷTGQ~CuqreA'=+c2n-0Iۯݺk13 9|ڔž+4E! (@ǘǘJ,1~v|{9'cSˈu?n պ ֱ(xbi1c>3W/{O!BMaث;GO w`mġJ0ûb}SX* =]`k܊x 4ݕ߯^ykHɹ!!& sq h̅BsS?vE4:v q{wH)Rc >|W͞U'Bh3MkS(E^锍kJ!(fØ̥}s}Vw|Ok34iZoͧ}րt͝B)dPBlPG!<zXƸK5>fƬbYW]~ {ձYߖt !!NJX t*}3k̘G㰐 —9TM?&jٮZ,|Tkkk,B>u{c.(|6Σ˙M?yE܇ -O X['`I-ӼWTJGᆆȬY]'Xki \gZ=BH%Y[c ߩoDېF1kY0}c +SGE |hhyqț5eZq;B_~ٸBHAAAH.~|`OC̸zҜhݎ-߅];̧4}y2wMp^-uίi-1\O R 3Mq=h0 +%L +~rBS^'VAH^`LR92X6`BA_B!b-t`/a9`ϙ]wy]0Zի10?W}@-l/s` s>1M游XO"X5{@&םB)LPuȣ-[zk1V=5V#h[Ȍ*{[Jb]6֟0##Y'.0 :$˳ p&&XvKA0.%By?y@{"NEXcDEs_WPH`$xWkqxHp#84,+Bbe}Tdg!jnȪ~R "3mkia[G_^;B4ӘD+buаص7oy} n͔k¹?!6 Bhcw\IZ0\ƶ>4tt[пCG ۰KM; !$K?!6AAH%y_|U|)]4_o̖Ah~"bX%!BMPR,AQNzu\؟Sm +|m_~[B!c/ 7\GBHb;P{;`ޜ3gxk7 qu?(zАy=qpvœ﫭a@-rud8kA)PBlbS(y8%-vs{M::pĮ u伢}<,ӫ4<b?Lx֥XW؇̀_*3aAa}֮d|O|Yӧ<BBMPR|siرK8 +}->V?\ ?c+dgT+ΛB  Xq]1hOA[C=֘y/ 2ѩ,yΝc#ܞ8tXih!$O !Śdm? X>>oX8a“ CA B7y?ךRsPL6PwRd !ś|} `;l >dhi AHQboX_]J(u ]Q_v? b/sB!&?)H6x}W}VsO"v~gh?'@A M7D E|OLT6!N:lsvDo3|gkнOF!!&4[ ׻oawy?fr~dwJ%"ǧ1O.|e=n'1-)?!6q?Ni}wڐu6]5{V.;!ŘjL?8ȡ~4~HQ}٫@Ʈ sF}s̥oRd 9Ǭ}qXȆ~;J!֜cXSX_λJ|_~E `>h2~zu\KL#2#cwkc{]4}R`hk苗 !$!EhӶNF#z0lv.uj\yX=h0n\0na?j^YnS$z*g5jB +7^m!cAK<|=qlq,o썏.ݹzQ~q|X|K^1q5c̥Yst !$XZPBɰAZz +5 d]qBlƨ;yOcKArMXj:*bջ,dUCW؈I`!$Z14r/l;lŲ tΘ9;"vC!SĜ+z@|-L_^:W& ǂh_Ś%^m0=^:V@<3~ࠒDD[B!E+b MM} rxg/ƻĸM/!$0\﹨c5m)hh&Vz֘1t6kKcw\Bp-!BG0cM}~ 55, y E_MAc4q:`{=X+g_ [g8́h;z85ͩO!ky [/g&p,c\1d$gM{ߡޙXǰa6)3a} h 1L!~%zhLu 9f9&im9lib>>0K7B!̇xR\zBHc\1Om'>oF|IGJ! !B + +:B!BH^SfGQ';/7sr5vi2\qdgz/ܮVmA˗khMknBr4g߸]*ޠ.N]\=fؽ)]\k56ʹd1v7Sutw~Ύs8A95'kf߼]>Ύ] R̾cؿn_yF7kMh<ݝ]L^Q}Fo4kؤyf͛=dGdPķ='L𵺻ZSi]NNS&hhguowvwQێcy8=fTg;GWW]nnvƺMPĬ4s +endstream endobj 38 0 obj <>stream +"n60qЋz=57T|v5x-]BfRK8KcdJ[y1@5s\ Ti82G7?횷iu +&S/V̗ G7 MaRg$!^ub䦌[* qh]A,Gw^CT}DP5,HJ.Z%6sZ\FHm>к75ePG$t^CEr%B$Vg.px63希O>xr*)j_ *4tUɍ7ꌁ|90 ij[]t8'ȊW\ŊCkl_AS9A=B;gI=~qW(+,K@H49R`r@/Cm.3VF HKy,Ǻ Qyh"2jA}"/!1MZ[ک"jfF +GGѦ1kt3-^ s߹)ٴ uʤ`j!6z#LRs/4tT^,UkG6+G;PQCBR] q-l` Jzf&)ˆ g +iCHRaA]n$z.U#wS|S/|zOC/< /&Uzڹ"f1բXU+n3Ox(ˈ.`-̅g MsbvG.$HR#A$}~(wBzLݮ2Ck*z `3xiL'BQ wp uo: }k QQ8=VX{ls . aTNNLI? qCw+*d͡_ۜijcDRT򨝼j-bZ_p,7 '|r;tnnqz<JtqݳW[Kaӳ4*H₰=CX}+g%yh|Ͱ,(L㈉7%)|xOБ |׍ +th tĆ~ @vI%HE_ճ!Q3N&H]r5(_f.jb3A%I+|Y}!mqdtJ(TSOd#"j'|[ЩRelJyڧ<^(pXBY",F{A)zq6t:ٓ@J2I7M~@>O0]Xi*҆EyDoSbE:D0w0<;eZw1Psu"'?Zvb1DDM08iIAiÙ(r`ifrp7*43lsʠG}ZG +| 8jX' GkH +Y22 +Fooe,4=-rVxJu _vH)i1Qgg}H%= IezdQ[ WQ"YY |N=,C=b{zl9Gu媃XEa<'ܲFC&+|!w"%2 D^@H k|(욇`){##8U4wtSŢsxEd\' / p/B p.PԶך$١K ]%B3+4LO(؆R9r0 +x~EMLO/Rc/в++$/]f!}D}|M[CmPk J}Ü(LO:|,DP!5| s +x \[`JݲcJ+] .$4_yX{@lU}{㾞&2E7, FM"΁WK_oL#ۜwE%*fWPiQ{|F Ts7*=>;SEץ8IsPLOZЋ"֕y]p{x}!v!\>ZuP7c+@I;5Zi#!>?D_jșYxvYkȢ vóܬ-ymhs*}}qß_`Dںg֨X  !a܄%Ƈꄺ$I{]!+(h B8+a9nrLc4FQT jKSE-pI7(OD%ʀֲ䅯nؒ= W0L10ϯI[ zF" )eo˳P~}oF;ErSšxzj 5ˇuӑp6a +Mss[!0Ö$-0Q(?cα wXQ8jdk4n8[rT^:=_+)_v@ pG#jB ks2! +}BvBsQ`9{cp|zKSpxD9B +MBMÓa[7TEڑ;< 9{k E u,+O!*`/YVSCϧ{A+E6?J봺pāGw*-܎La(Q!7,@ύz~R| Xd=!+$ih;l(r#i8+k>kpc^sEIڜM9ƶp9>%€-'c{Z_|4 fÂs3QosEV="K)+{ڟ~LK`AvO䙂Rh/i2fI@ :YU gh>phv.Prb@]O9ffKjU;W5صy÷0T٩Q!`%(%{L*'1Rt>-=ܸ(A V4mx8CxڏnER)cBv+@#[d^@ +"JBc(Zw_o9(9qsXX{ ᇨ# o "#@eV~%+uRq1^+P6ydO"+_Ȅf-6~տ?f$֨]iKN(ױϹ;٠?v +\2&C +š۬o7Rܲ_';#P6Ӭ> za>~LQ:] R6XOJZABUf}Sw-) Wxo3F׮XE[J `.4]aE'.9n=\-5+$5c%ih8BYjH ~NTGH~#l!nGų(GTuG7Miߨ{Sua/rSBmFso_bdl9bo~3A0IݧzwPG4 *e׉j?]GDq*c6ǁT)1ms dXF& iqWpO7@e‹V^yNyFW3?hhX.O;Hu% itmX][ pz>dy=t҉gJ9{|pָ QAbqCQCXқ +oY"1uT$'/ZmM/(1Lan4R #Ι`JLx4zEȇ?[U)S+s͔T=vU稔P +w`},zK7Gdž^xX Lk|KshyK$=NTffXސx8~,oh!RAqҌuFlѮT}+#BF2ߣ<FƝ٭yGhgb6EOϛi'z%L(z7G]Y"r|*T^_cĞL+IޒCGaa!<{6ųj/1$nl`w\,1BkF2{hQMޱO/!u;G|>*67x&;|)šPa1j&/~Ctz`V@ѰTg}2z{ktg>+EI6? >c|[d̢lyrt˶&d/nN!H~*@CBGU=:*-=f= hHީH +y029i𡡊i.poE IP7Y(K T)* =h{kLP5~\x=VT.بٳ'Tp^ʪݯAϤ cV)}O(P){1?r<|EjmTcT "mJ!-ZK7M ٓ'YK'D[+yr'1qax3nv 9L䅠%[oQZJR +>M3i'!ϻzl!B>ւ^]S@QQ%eRf?Q!E Rcʣu`E1دb F-2zbvy{E0_A5k~y3v>5 6>T8л.$hO!e\dMM;ҙJC鰽FӠ{;cITPż0݁ 6LIrKVs-$)Z˫o(┩==~ܓ7SouSTwr&un ae~5B?;"=yWGH z@Ԕpzu1wj1s@ɇXR] " g ̎-G|4܈gLUЍ=n/<|vL@"r7}‘W(3 l +ݪe~4Vx+@7L\T6_g zl G~}p/YR;_=p0X7xڲOdm6sxsZ镬q`p=` cxkU}A`1cHNf+B~PL9Y farN##rwù}|{nr >l;Jsv^Dc^ֻ.2]L/C֏O붽h*9W+/\gƶ_?_}J^U>?%,{ ->.q+K-)n8?v99Irw6N?44>8n8As:]A&MU-.@<8K%n"S陗pM Ph= @â~bވ"|z͸j—'Gt鞡pV,ExH"{7룁4:UEfN8ЭU[{ѱpV~4p:D[IA)o 2P%ك ё!lI"WϽB2> + |ٌ|6q^ %k^) Wkt 6b_ӈ >mZɽ'&J1\⅕؏1o#cE|%E0EŒa-3f v{+ +4C衢 ؜kTsxy`cTǒzjw ](fg3Vb=rܐŖMIO0&-6*x(83/s}5|m{GmBSK-l &H.q@4+P܍/-Jm"׼㻡aJLmxb,V)#4 3 %ޛ+B~=d8=:;ỷ1`Sm?áԓamHGC'`b.x@BH5J F:q8!0U B=u`0@sx @5'n%MetaʂMҏ4j=pYLA`A@*|[?Γyk@Z%5b@kCRGV^I ?Dyd3*ν^pꙖ+NM{=V 1LƱR< ; @dK*HQ-֬|TҔpƮW^'G&rqG0q`G`Ni{n^Q+ԗ9:j-<Ԫh>HA(![n9|\-NG!Ytjfk +,IOR } +4: +OIA 菉kho*f%m;s0י\F3.;A{,7K=ҁћO b_\!/ǴCqJsƽRme]K> !E.XR)=[$Hldzmپ^^bx.Vn\fHNlxfНTcQI8l +$TyI5VuѼyħ]8!''t8o쐩>!Oڡ)4uCiUf7=H/gk]D1-b ]-%s$N9Ìp7;`7`OV?FJd,qxnn: H7jIfW +]_65B݀tu 3YeuV^6μ+v֧Rk9(Z^l#B?!SjBBA{/' EŁȱ ˖RLeVDyZFwD&`M +*I~ мZ$@Kh'ӭtn#~¯Z%I1H~=ʼnA$Z;k$]SDRM), o\(bZ˞{H=ESHl@WݻۿQ2#:CeD)D=q$Z]3{}-Q B9@71{nu_)@&XR(A_%&:i'6ua=qS ^WUu Ԇ#Z:">^t*TjX6vӻ5 &i oYB8aRD*[!ܪ_n9i '>'1fSZY*ʯ#? DIR=^b-\v<݂eA..z@JP?Z)⼺JT߳XYJrZA՞ng\0+3>q *nL-_O:%M&I!-7ƥ֕k!q&l4YFD9橹|LU? WG!SQeMU@Td&KdWYa%d$Lj|Â>pz5=f|ߤQB=J*<ӎl5hQ!M Ci;Xa ] Ǡ)eD& YRAyeF<}tAzA +>\@?My1m\I=vʲ:qMU9P,<6iVfs\\犧1@x!c10dP@ b1>ǂw66woWd L}TYt91QATJƩN<*mF@|@SQ8Gw \)Z~k9XN2\J;1e0 |0Y9FDL0ey:㫼.uq$_ cû|_#lXHO+?py)z$ɒe` [9S oX@v@>3d8`yE50"P8c#Wlja #%Kqk{7>0/>#L{PL4~ ]oj|DF(JeIf}M 8`Fd pTnTd蜘ը@"P{Dd R7wPH1kV9`i@e?hD% +~ yAQH٥ۙ7vmhcӺkWqJ6,6jTֆyא+ i$ Yo U 4`@6gkUgĹ*7f ^X'x-рGG}A5;ԋ<cCN݂{|` +xMtq| C$ًw-D%)FHn)泳v!Aۘd8j.\O +m4RΊ]f*2=ś U2=* ce{=_yD., QambT5DJH4F [Y{ PuBb<*H oN0b VeC0U}:#bw^ȋd,q|#;2Upꥇ~گ3uU XԸt ^X =.A{Q-A:-]=EO7kQDL>!D-zxM2Tꁯa5..OEx$UQnək׳F>p-DDy#3?HQaìCaORl|ڕy16 h +EMaV`O(ʊpG ^?ԈrwL*VmOݣf 2 A~KuNVֵBX(};M^k>T:h/LKB񓰤Rck[ї@$عYyZEvSyR+IhX::.zݠr@$ͫ^ 9Z3֗!=AEQ& 0vQ^}=ܜ"B?=PJQMdY:eto ˰L)=UP$ZC_QV`7 ue`cT;묯]^s雋Fhyʷ$KnEx_'1X}1/ŀU͉B^+^8  4E#.%¦BY^ ٢,0hE hWsnj"e@}&q + E}vVB5B.3px9D@!Z (*ʑ!m.%|jv<{o% +M6R]kd}iW? WI^S__60RlɽH!k1p[ۊ#Ğ(xeQv݂HR%m0{SFN%@8w+W0hҍIp60}C#IȺ i^z3$Ѝ5PѢC? G1v>5ؚTFg9~abãJu|D+i0ym3*6RoG$;BX'TT~&M!ٰKSP/;^hQ ɶ +D"/ ?¤0!PM +`1W7gy qrH1x7Hvdh +zc F. ܛ`8AIve6{@'o9)|/*! a@ O-ƨZW'R}D- =ŵL|gd_9j ߮jf'EqzIlXtqŌ3Pm! M6 =LK]O+2CQ=lf"?fY:1 z8;Cb#gſ.W+3\"8:RѵxSF(f,MByCb5oNqgkRe"݋@ ص#Ҟfp6vR#C.5\ư2^u3@Oc7:TGyYwХ8 +TUFRGHdrl}l,>|;x8ڧj'6-@gBxgw ~0-"{.gFd tS;i%4PS(QqlIP"[['Qήa*p +?dIqd?KK Ҍ&ߑ! (!9!:3˓C/Oٝ'HaםR +';id#"G{2?YȀ$JS!}a*s^,BP[ʟ(H) ؕy}a$Ȭ<8[aT(/!}naM@CRjlth&[=$, AO  '/ziB3}TMU9JJ 9H]"PGsFeʨ׈`VeoKSHozgUi5Q Ю"`DeE8-`=ijpc@ +~y |0OvJ {F^|{ y}_Em 57g:I3~N@s`4$u(-ΕYFR-g +zIF'j9XEK:7r.aͽ:16jֳeP$(o9)H-9IEYq؛mQAPo|q8혶be o'Y(h }3|{1CcicxNj hdK{)v 불CCMDHe}#[ J~(᧰ƻ1] @ ؕ)#l$v9@kRhĵ!9& )lϩx^G#@/^ T~g``/$>@L) ]Cpxة#BeZՒ-)&4XDZgP#~#%Cem;y Iٲi$n"؂"nP^ T*&"R-q-0 4?j@Y E"QմP`;p#Ǘ}Jm7y1C*(dwЅ"J pF/BݫZeɎAg#ѣù"}3m.=/ƆN/ +3BaC @4~u~ @̇`de/)$ <ߥn&#O=gWRXTKdlXR.mA]]:@zG!XB~` WTަ0rGN-el~]=1' &Q8=ߊC`laK2 Rgʕ=)p0֬dvhh~_آCE:7- +lH9OlIW*X. `5+=/L@ e$J5DYWz0+J{RKÝRҞ02zd? + |AA/LU=A$!Rk-i1PxVpl^|F7A*8j#h v`1oFTw$JMɌmRܓaMp0;)?a-?}J|)9na&댝duPRD)x"Soj6:&8| :nB.^0EY!5R*q?q"#?kDVm p:PJ=oNQ1N{ {8{JOSK zȟCϐ#1MLnJ6f Z@sK@a.^N1fnZ@ yZNbzd + Fwā.F .?;q +RD? 'jPV*<3VuGYad~f!]4YIڨe{Y톄pLRcnh0Mtd#D:sP`BRٴ#ݴ#T ^8@pM >)|eRTyٱ<~0ٿ*]ҐqKCT׎WBLqCCr$P5ad%phfhw0KY8K +,58r'.{YLMcdn6]FJ킄u^SXĪY$HgfI)RS)8BA|vO $蕍@66GOQV?+O5g4?8Zf؝RY;먬svָO\ӻ]P{G!{VW 6srT/—_c󭫣4r)hyIj #_|X.S$8-vX3)|ɖ! eky7 i=<*2 g%gkB&bhRނ(2,ZP+}G l'.ef$*_AåȐ=%n9CJXr7edųẖ´< +.jЗ[*a7bV\Y2K踖c"Rf +ʭeBQ f^:x6Ep@TOÞMlxh%]5>aڋ`]sL̈́(ϟ#Ȭ%p؎]V& |~Jv_C" ު,nZ2khEL糆I}|p5@ #ߢ?\aIyNdoӇ0r3oZ ϋa|814'nV*AUEK6lFU #0NH0PѺq.3BKx\8 +Rԕ0+䧦yK%q2IFGt8س<w(.ޠGq j1@Szɧ$)b9#7r)zYtc7쁬fYP"ϯ.4כ\5]~P+Bb%Hp(U$UZݭ}#Ww`""iqn(h ^n,a~WCyqoH^&?G%(ZyI5f=q@ j܌CR/M@yX.AI_>G& w\#|y+6XpvQqVP~03@N'=#Zه^[$;mel⠳wCRzР)J!-0Iy*q!` 'c+b|ny[LM.،H-5}u{_|B2nKb#{ի̃^@6Tħ droXTQ~fLP{_6jx6 U9{3}L.,UY(IdiZn\2%m(g\N%'#*- )FMYv >21p㖨h|C48α閭{W'<)OX{Sa:U8Tb(CmaA@w@ + +YUhۈ1c&N IHOuSNɒWYR^K # 9B;!-+>,4%`asCLٟ=|m>SpJ(K#"|Z*~;uL|ؕ͝ƞv :=@{1V!m`wu L?Kɴ( +A0CqBJW &|4C7 "`CKN%adkįOɰN^#ę"δ(Hd9]07أ>[1솒W9d=CY9ƻ~$RsO:{֪nAXG _ +fzfn& &[_HGr g8g;kPxU8P:&7j-+"ye|I` }=F Ԫ`72GתѡC.|FTw O&WoT!ٛ5@aHSa ]GI[M|5X!JADbLCT7%M>yA!{ʧ~9_{} n7P D:jqqrTlkG;$Nj_*@)V轢<0lNѠyd6碪ڞdbHoK\@I&E>UVHQ⇁ɟj4(oC/!T9DmUk&9GWv80⮪ANEcA9r{M]v"H7}( B8s*լQjl=,9Kbvkf0VƯDL PjMz\sM"T G8D}֑y=ԇ!9Ӷ^F'i/EJe;5^$ԵУTc hV~!#,/&U) 7 65H>=Bȁ?{ +ʹ"1T:!bcG`%e +82 Q8{3p-St b , w=OǦTKͦ"x\nYŋb2&~IEް^=T᷁jPh+ NZJ=, jU=?25\Tս44zBi<ă\ ;P yæАB{%5cU3-VG`aR!H{R^"P:CȊp.T_Tf|QB= K9e64銴[ jcc@FlMJ34qR{v{n +HCةXC9{Po״~$z`|DkNV Ę?=HD>tNҌ0!P{QQ:w=.p`;T +0_F%Ҏ 4E+)< Gz:^F9"r.R|yu+8HT})#s>p}2S)˼tBdJk#7lXbYl?繁GV*E,ӣ[΍9=2,rRZӠnOCgn e^\/XS s){3ˣ(dGFFfHODEjC=@ +Ē|YKDx2RxU-H&;nǷ|XQbgSmC g痺y,e7G.'#XP% A'«e\P$q(*lZ+ y1>ٌƄ.Eo/=?{^gxk;ցRe 1 h?_ vPy +!aoɑjt!I_^~pB;h|'.=Q!MfũH6NȺ>ulQء3sQ罫]vB53w"G.нPR@j(=5\`۰Xm+a! lB/5>W؞`*qP1M^?PE{a)ahV Y Oi8tckLI4'AmG!nSE\YXg؃yQx4pޏڞ* Ğ͎t-O4؟ҿ"v4w6#xAfƉ:AV qGmI,tppJSΚi&W5=/([zwBmZ*<r +rEqkM,\(q~,Cw; ٭cDv=ZDjJ +}O+;֜Hb1ҤA:u!*1zc`QfnM+D0TJ]l +^[;) in3'ND"W`isGV/vD|9zEqk̽ޯе~ +lNExЮhPor Y3NvlB9l +烺H AKϦܠJvЭëLHøA{\V(VA +x٦z8ͨ[=$pXc#B{wduX,&,1zu alyy?eW$;|3T ew3JFC5z@$)@@SEH>$(Tgٺm1o ![3SРb7̓n +>fc&.xzA.8[\"cMlx2ipߗi%N4!!`2 ZqKyO`eyИo+p ߠ׿h;I^gD0}Fh^zᗮ+؆Zؔ㲩@[o/'$2r3E V:e$%VpdXQ/}@IaM7|ߤ$O$@y,Xi^1+J{2s݊NwGn[R~3槄5IFx$y4 ? %?}77R`0ڡ"'`JC ^&:+[Xf_~٣ qFJ +jt{՗@Ip8<4G(/oٖ}&}}T./ +Q|2ڼRgnCXOHsnbNK(U}JponSٖn޵p[x_aˡ3òZd}AQ8pf ;4O*YW9; xC|:(Ҁ\Uϻ/Y@IK6= SBviYkhP֌%d^!1wṳ@0H ḱiȀҮ}^+AdAE;瀐%:A.EwW[ν﮸7a{--r8܍؋:*:OU'U١yA%0]DѽQw@%0@{hāb_QytqvVyPhŠJEZ5pV&RGk0~-8&xe:5<.0U}A7Tl;1uÕm(pɕ{Gj@M noB&R#@xteK!0E -8EQډ]s ڍU A[Wpu+lL +EIi@kʗjt(v EPT]ĈPQPPK ?bhzP/5u0CD&g?U̯D/|\Hˀ(%$Qț +cւ+0;o|WyV~}aDT`r<;P6BB/~ɇY@8TXLpNC,Y_r7\Ե Ewjy|d] `W:u{;,?^8sNR{;?ʫBRț^ V z>r̳\~q8tX\UuJ5w /{V@Y8P4K{*%0Bhf`Ov71|h^5 QX rUDG;(#yX(|^ +[r>?Ėy9atOiKՂ}5@+߷ +Y2X5[ {EϣeXd]N/PQ\Sù'sFyf:Bifpz{ J7ZNrQVp]IDs2ВwdJq"pE)tOud2DR4r}.Sp@xm' TZgw\HGJ[;PmTi֎0,7? n8 +^ޒPV3Sȑs n: +:wV47(B'78/r?\4,9,8["b gLAҹD[O-ZCw:DdZ t`Bvmso-GkΚxq[%˩90<7 {ef&Ղbѽ%؎e|"xRc'I|Ş7Np/|Gz#e>??~߉6_Gy(̧ߪ0灊9}{GOOl6fK`0J59Аإc`j ^]WƏ穽E=FzVX^FfLQKߟ)L f4=_v% ,דUiXr8;Q#(hh5> +1cZA)1h^ IgS"Rcb?Bs P$uBC4VGl.>A(+Haл]2.1=pj֙rSD\bwGd>R+WH_{L;R+`)q~'MXN0Ln =hRvbxtFDS^`/yp.)''J z:=vpbM8Et·j7?ys^^s#n @OM -N~ aO[\a1i2zA<$#HNd#_ZtɸAM,:x |N"f\9PsAȄ3Uy28~ڪȝr+\5哨O.IhDF/nkplx(@SWI(tkTZev|S>.,j+`*@ +W`DN mYwFsةx×͉yG}> !/o[da9|ϴTrIDG8 hVHSU0ƕp͚VC%JfU<3q.UȎR{4`& F3D +•"L׏je>.d :6ь]d~d&!tOSBۀ_Z'+dqZ$9p~Mdd\@w}A8#̑qc)b Z"'Z<N8~;qq8CvGJ(DQ喇I5Ýw GB@| +g(5?:Hܹ(xi'h\ GQޏn=D3 gNwIM_]>[` '³|?Z3(a/m'0S ~`t#u`vɛ8ؔyOXB̺4 ـTIiXf#վ1K]~ǗJZ$T{8DD "Q7~C2uuF]X*,ASׁwQ#rTŦ@%1%x}kqh@ԡomEVCM; y-cĨ6*?1x/i9j׮VxĤEO42T%Ex8Gy ,&g7dpIYxT")XX_},,E&=BCZi1:pڽ?GX7:&n1zʊt{w{hc+;2zjoQN ҔS\pw4w:[}IZE9PTpϑS~tj0u;|w^9zG\l߱^MDO\zUkŒ6]"InD}t&m)Pcюss9JquYl(ʥU @h+5!Z3Bn3hѰ{ߕ7,,?nP%[Ӭ`øj>Q,h 0B͌È^#2U&\AU<6eɚ"SrD :J}4k̄Lܮgth$bӇ Ev*|wρ _ +-yB}Wu4]Wi_GW}Bu*Z:- +yx–hW}gZ#]uw[ X 'o'{} +ȋמ8i\aF, l&*oqIH,/ӈI45B0fo eCS> ٣#_V"`"bLɇ w*y!^ +"P +5P;vY@(.O8 E 3 JT1p# + ^azsYa +r2 f#k@҄TT{UK3 ?;Q*ApnT^,f U]+:zn]TT6iY9YhQ0K=(iMR?3A-SȦDep +fؒ_ $ RYZh?BՃQqU/Y *r;Y& FȸB~I{'5( t'vBHǟ?|jnj$eU"t%JdqdPT1~L5j8l.`〿|9sp<2մA(騅<&SL2 6@ʛd3y$ֈRDY-/FRCF qe;nh,ټ}]x2h `͡ raO̅jk9!@ lJ ~?R";5{zW9u9)F Op-t{y2w$H8W+KI1L_DX\ϺVuL4] qlaRPZJ"鎪`P"EElB\[K@5zt7Qڱ &^G M]6\gYS`d۫z  /!iC[YTjH!^$Ww"ߦgE+O|J;Ɋ'bsN/&P +z[~(5Vrq! x{ ?THᥳdXp6Y@L(-$;4RDr{nJ[Ad*{dJIU櫢u+tbXiL +ywL]h;`nryFз~M 1(g4A`-3tdNj;_xt$z:AX_Tq4gZ/d2jHI+qCIT.$* e=`:,Y}@tyY[]du[gvIPyu vl!]hy5b%#E&/^U%<(6GaE rTgBuz݁1vY}SWul=`SۡdHc[w| Jt[Tٶ 'An yFH4PՄ{qDfi y]E`*W'Ӎr{;tީܝ@l/J5es"ſ)̷gg\4 +^cm${.祣g])_v +)%5M]2mn@W&x=f,}/-PK1Eu!xb_RۃBEG5_uiW$OHFSTj.?}|"&+0j քw;DS] 0 +ɩܨd?!R̗[(CƋ^>E[4Ĩ;'Ҿ(s w@LA5"|'荨 [Gu[@Zgo`:&AeW +u@&Q-M!HF`- ⢦o6G,Q"jzqdPϲkJHgG&Ô@n{-3@r\S8C/ql|ٝ^Ga-pCe/|;LNxfXw7%kr]j_.@=bt4);k.' 2g@)GLXݠqϕd=aBdy"j]aǎbMmgXcz*_ +8]xܗb_'B8{-g%N~# LdOK,YL 6zCn'̮JkDS{嶋}X*sC0Vx+8f"PVJ Gu׶H␛2N(F͵ 1༷NuN"K4gF"*\Q @R]]}%O4¬D.:Rtvâ&{RSDq#)ڡUEGgq<EadBj`PP<@C.ɛ~ !_Pj|aV*!${FWn60*,j,/eW +|F^ѕ0ޣ +_@/i'KJF5AQ[!\j2 + U4t297swс ,Ɏ`DS݋SBlM`@W"݃w4Oe d׳uN$j5x&ۉq"H~nL+eWbRlz\ZȱR9(a>~y^"@U]/H}n:Kڰi&CfI?QcM u)QdJ:ɃFƅX.r7[4س ̠Bm o=Z]ZN+Ki ء' WY-^rH x#{{0\ga ={:2kVmP;P3zP@#^PJ@&!>+ +=xպ/3ᣯiTDgޱؿ9ͦw=Mb="k$U&"Xf18tmvd/(#RͮD?| +ђXR:"ҍdh%(T`=N=U{;߃S0|< xM3읗sl`FXP!4Li12(gߜjz)fK'\̒oTN3{qNᲽ_y„y6tR.[G.Evgf,բ?&yq+PÔ@rG#G"^&eT Ai6AHOӯ3D!BY=Ւ.ũKTUh)DMGCX\P!RԾ)n o9rOg=K +U<=0.:ݢ}Q_0ן_.G$8 { Gxtݔ-{ز:>LDfbF%"%npkNrh{-TDc5+<{>cPT {v^]x=C7P[z:g^p!VvJ:[hp|#.TWFEH{H4&ַC)hK% }F㒥Ǝ :GrP|Lhq"J6Bنnh{o RLƟWшLtl@K1 TQSK܇NCưxT^#qBXTϙ*[/:ݤV>L.$]+1Y|y s N";@0uXl&2eI|ͱU8;+;<Ztvt;,Iq~n6퉈m)Z)| |$?2RV2C9+S!/E}*q1 +ldڽL$Ԋ4NG%GS1>JW{q"+ys?0 mƁGpfi7 +?~G;Ytb( +o*Ӟ3 @ZVU\!ZO$0 |@S` OɴaͰ.Ku'ם7wcߋI7M'_W Jm_D@>!I42ec=kW.addoڭӈKTS\qX|S!J| +> wT}(2䣟.x7GV,x`kūf;{"}rВfj UFUv-k&I%(hOE +r#N"}h\&kΫj4GwkQrcܸ)t],ъvt̔RC[&x0}bd֙lg̰_9 JKO $R0{V14BvDhkDβ-4r8$!RN 4ae"/8t:_J9SFB:H +G%ix}N_tZǩKSBBQ&D~qb~& -n-1j!0PB9$&c +g^WԢۈQߕ|&2-좁"C뿏̠mh{ &I UX6BP7OKʘRY9$}-]Bj]FQd"} h.[r3#1o&ym|%Bz63aRЬGm=z^A܏5eXs.L@18q,&'VNCF\1qz A? xn(q TTI rXd!@.SFHԺTn +PNmS5C?>i7#w=XVۖ6_H*٠bM[8z}-09FQ4QegTՐ|*s(`04)8#Fzi +][m.4A k*Άby^-q2l)?@9\hdw>)^Zn?qS)0!m? M]ѿ3e&d_@%h F5[1ZBzN`?gLŎICoiwsqQNHDqӆxka/`-R$+4n}TA0kY,|q,q O1d +A6&CoXw?M<%us-HzHjam+${ wv෧\s)a*FLMG| + t/ErS dLrP[jj3%ku@~wC{mv*MADM9 +kJ6xtܻ83PE_;_<_|~Bp_ +*DԒ@~p-#FH0V~eE(rUFy#(]yF#aͣ#s ++Igc +05kU v >-wf(]^;9J&oJ0+?r܆2:a褎 ѝXl*++e;"iҝûcO̴ +TJuʞީ8ydODi:(K:QP|֗+,^s{ SJF~^X4 +14SMNѥ!*sҡ駰6~Lruo&<)%g("nwT*;,?F@գ&0,," J6="1*4S  S {ߣ h)F?ҭ\ ɷm=f7h!0ʁrS``{XTZV:u pvf{!'=Cy]zw&}r|HnOl/-*?#hx3ywϣ=>Td5W2z<ڵrc8,}8=^X|L=-ܐw6<ήV.9IÔT@`-Y&Kcj =N\Y3l^g5 YyXAG!4 :Ȟc9@4קvt]j#5%uy±75c D߰WۄJM3OGzpvE(^R[~HKD1½4h ukSZ[ulƭ.etH}S>+8?JX*>G<'~?*R1sc-nl6YKjHͼ<T 4MШmOh秤* %E:D9iKgRE16hqk(UuZit9}B/4gÁY+:Wt$E'ZS!͵H7lPYޟe- +#>.gIQ6Xy# +d(+..Nc5~ZC=S& +0mUF\*v"%æ\D|@ +֔izT>4{ I|=%[,uO5dCH}LvD׷Be|/XAdȔg|3V@ݫT7;jlQ`8?GMquj)%fL=aڭr7⦬ܗ@\tM9;] .C73쨀PDS|oFކ8nZ4'>:8|Z@;i|?ZC8L$$#l:/Y~BFL]ivDז%ty`E_Pps]m 8/TCp('4O`m$@"$@|vb}E?Bܐ;/Qx#V"`sH+6v({=GPcDqt#k`QEXD0[r !οyԏSf)lпЗ[<-tEЦWT P> +J8pxJ'x?_afA i| "OxKp d>6(J+Dt߽74iAPԴX'bTw{ lxgH?&.ot;&"oNW.#zT ,X Y2dSk [~xvPڈ/\9AZ% N}/LXRNkyǿ?~/^4ǟ_O/?\7w}^7/o'ϞLˍ_[>WcEM$~D3It ͏m"IkR +cDWMc{'=)@\2+)Gv=E6k.d;PA{Ëw;ēQhWlx[bb + DbH 5Q oM{ jtꪌG&5aRӒZ F2&7T8M K!sᝏN[OkÓTeo?X͵䮬K^oJs3,uL|TY n%`pl+cDb+lO*&#ګϞ{*#;G<)ݠ#03aos`!TQ4TGܿ +R !VHgv +ܛ=#4nG;0%Y~1ؕL%hekx_VغUe>K\{}yccx>DцzTvi +;пsD +Ͱ|*!kg~auN}BRy;pPawidĢvq +LyBwE0ƣ?1Mdh5N‚L*r+fR„_@e1L ( P k-7ZU8^1g0|]Σ1Q._ڧI5J@ME U}xTLSzzOHC/ a"bOE Cc7)6̴^O>Nc|p.3D3> (y".ٝ?tv63c)A7T$rR{l+ L6 [w]^%SI0xĞژvz|]f(Rv4O#H':Ue4yJ 2:XSK cuG +֬7;U}M?ۢHpЎسQkBn1eGY#RK%y/oNTqa=[R/yih rA$E̤kl!Qۊg$A$HdM}KtvUEv8Dq;-P 1/ߍq M^3Iw: ʃ8wtNGD/;bY]:#2'F0BdDuKQrr{~q~|KeD`b}AFKBFg}35݋+=JUL_tE ;x : Rmg +/=mn{^A eȫFAC[qQ$A7{:hqlUX_{^蝗&hp}4W&Vy^(3U1O]]z_IʽЪsX^hHA9yLj9bmM@!*Rg[qRd!pUr [{ʯЎ=437K!u"aa͉骵]Gr K0& G39|ݨ +Hj*K8}vQoH( HLU^Ղg+3_N\QWLFʡuq2 8oN$$RbbHBR-Nq#J%ux`SxKqȃ36|/-=HIńK-!\loRfRLNGVe =d?OnGn)g@˗KdfYǵlgbL WU-*Js@ #>D۷uDNÿ#| sYufPhLE m!ꖉ/peLoNĵY.h2f vS2OO`Wg}2"q9jc,D  +H;;,ҭ{Cbˀ8Z˶[逽X;*t;b"p?bF5%)exo/ՠh𙷗݃@"-c|/fiXOBjCLj?&ϥ>]Fox9T"+#U!\q/Ԕ#ݾ9x+CMNBs?==ҴP~F7L 꾺dMAyT(pm Ҋ#w} {N(uHRH6m/Q?iCqE"Z7 + +jmҷz(#B:`2GJo;ͭ0{z+npRG l';рNӰFJdeAvza\vFFi+ũ0βW+Lo2GEnv&Ϗ a7_T#Jpäϗza]%m zՎr.٭ +},aɤKfpIu2jpKpydę@2+l33lHجp9) M5VNTjK׎ber ;8 +hH_.m\_ø~8LwM4z*$YWIJDs-Ѥ/~Vh7=T[T|?Dk=jT2@4a)@w >c+##BPy'FoaC]S6P`r+"MD,S!ݪR>)Y$KuQAg +gZ0_I"LjR #c y@Xz *52iѭߨ/Sa,le}^a.TdYvv@=Ǎr}?A|W!KWg@THL>a*U +3]wX0i|~t ȏ|9pMa&C +d>cϲ ;D:Ȱz!thkY0ȼՎ5|B +\7+2;#|ʔ@,9RPi03M Fdx4EkZ3PzUSo7P3pi$+Iri8B$4OQ [jK݈cߴc9 +i.U{Tt&~O/ql%9!׏s\bDʳNrT1 i>?ǰJAPP(nYrYQ)ًxGD$#w4*uh>B^3ބ4ҿ3i~LZ}JÐ̇kSpZkх@V}ׁZX{qZ jKqJ6q"~"4Z"2V!ՆV8Q@?\ҏx +𰖹IOAMצsYc6.ѱP4qk"S((2qx|fv,y~=.G9jʉYBgW[o퉮VeVK 祉爕YV +l߆VU~[t).Ta-d~ ˯|.V38c13P${DSaAA*y׉c($U].T$X¥W[3I(9CXct Ko`p9%e,[O>RCZYFߌ@*?N@?0PhQaќ2k؍y\~>VU!YFL`@e'eu zzm.YL=+18Vs57`qk}wIG9ⷧ7GtB0jw +3**nb }ýN1U] A[~dKAи_FI4$ + tOP(6l(;d@*bNg ZjYξY0)7 Qo/X + fE ,g{SR,쓃F18O#IW(|!y"|v=5&qTYZ*Qn1#H#,:"aj\a;`. 8Q \F<2G8oKtFG~[ۓ=;B]Eyʁ{NQR Ha@c03+.zPU#.@@•/B'&1I霥Ӯ$DjR_A9ZV)`VS.t ,cV܈fPw@1?"^ S r\[#Fsx<4ȸP+s!X5}i$}m,ɏJz4bӗ]BQCN6[>QlI +*QG˗XyJ zbdQB">`@1 +.8$atbD c,+}H;®%Qmm +HWAa#uԹ@uUer 5j V#`3"8B ta$7 W^v玲7 g?OPp2dj{O<Շ^$Oadݗ[ʣVM1U+;!%TRtĥ) waY( 9kpE|cr;ψ?ZO#2nHyQ9FJ-s%Q(5IdQ}3C.ɋ~U"ǵ4 /]V78}ThR/s xHTe*nISOB&??6.͚PS! @-ds3IōC"?ӯ! ,?ғc>reGf@h#SF6jb3[E]?=8z,TUp/.u_1B*ZƈfUuK|˥bEOư(԰eu–x"YF$X^웛!s +,GUC"d]ٴgF7`!Y3 u#\C{Y|ǰC+ 60R !^͓MAloNTG!ja @Z)fĎ9z7s6*+'g{fҐYǀsfA ~XnGImz%6]qxC6kFM1 +/dᕌw2TG2BnsXOt%H)OIJSedi3z5g~oPQ)L\lѨ/v%0\+ܽ͵oFs{+/:rn5RHŴHQW6),E|a}ę{f oNyY(JHвcTvd9x)I5]Q{BMvOϥk~C!!||E&YAt{wt_Jښs/XbeItZ{c1@K+j<^G_-ŕٕA40n^vȺ|+rʋ\K`@q f~ +|%?J׋ܣZiQI<"K<= +D!Ѓd3tn@N[@|dI%Eu旨:P@sE?X]v簨g<8WT|lT_ |R 3k8Cң5Ex| ?l%?)1b9<ЦwoO4q%C![Y%"OpP{`Tn!"{:X3VZcB3(WoF@8M×OÐVt<gpZ]FDfO +;R+-oNuW+QB@-{x߇!"b|Bج%qj$2|L ^$KӉsk`V&]6]|_Q+QQhDQ*QxD8i?2b&_jQ.'2$V/N8q2HKŸm!3 _]r$u"$m9hAtg;4(/iOR1?+}|l몉@(fY>@tzpobR8ջutt_kvȓH+}` J3d)jAdY7i̒RgX1(0Dt PaL,DsGP;鎠G'ɸ9.`3#w`uf!$%$b"=.v@/L\;qsi MW`C JF dj/!-ϰ >BÈ ~xӊ8׫jDPB.fMS>/ɞ\v@ЁinQ[EjGAD> H҉m6 ԞJH /=kۑbždY*+)̻}{@Eq:BM"YGo8ԸLO~&$H䘲+3.VS ?r$0 9B*.wQқQ$YTܘ*X,1måS(7’r\/Gp]h~O0ch $Z<~F3aYca/qMp#kr4R\:Ѵ=)tl''>U / k݆by$ܷmg/`Re`aqt$#;QzM])2}߈ډ-IkQ44 +3;O1Al+թ.eѧB5%__7PJ[Xޛ_췤mTFaI\/4ܑPR{'G `%p TQ}ͯS"Y0Jq"H"K! ,z+@p3(B.?-XC )6Ȕu +Y9(ZyPvSWԮ-K,O.\2»u#Hmk ;dF0d=:#.!(쑌Ї;pI^èX0t)z\)ey'wفu[@0}/D`suD28.vi'@S253ç-cerZ! +ێJ?lic4nQ:?w*EF !P_`FnlIU48L D'7\ 5 0N1HJЧV,@32~tdha/|AzDHpytHlfлwZ#3dI$#k%X18#f#5%5h/̅jY)e=pLh%o48r`>L!kbS񣚗?j@f*YB + zWu  +W~ +^ςܲ~yWwFмOTӔpiHU@eHFlo':446xx EH<N/MDp@i@Z)"ե5 uF%v ̣sIk'Dv@y{Tefˎ!+-T"Td0&fTю:28/=k.6S@Sk3%J6&rRTě[_?n4@B<~K8YaB ѿ?&_!$i9 ix@g2 NQ0ʍњ&]}IENYO7 uq0GI_̏TLWعuZUD4 yXўJ& b% GPH)~@tXnuzikaXa-`Â3'"1 +3@Ά |XLس*ЕȢK)nO"f2nss +EҬ#E/0ZWq/|nu^]NC@%Jm"m wo%'SJx;_"` MC*]#j;}ȤLr/:N +af SR$A(E6.X錠lQ՟GcQw?#K u01a<+zA.T4+\ +|OX4 #Ң [&H3@ `ԯJ +j_uj B%2%ePrYA155eB9좀:rv.> %Df7@  Ȋ3Q0n/,!L/Z(eӛې9&{z:Xp [Mz5#1%TU,"BM eln2h&-<`x?{zw5֕wC 18&qL2h8P&4"ޣwk>zG;LAPgSH&1!0w,tc-t8UߨPaT4M,}ϴ?mKܠO?ГdrmBT mWJ4*9~ S}&sT΋jfѹ@h^)-,4U{o.9+K= /iro+kH9,s;)Bh4^zU^6wߦY$/x9`R~-7zT-e>p 4'.h*` |Jk:l(]`y =JnHF`BJ+Cх;BmĈ؋Ro K6ի"d\^6Pj3%LJ)v=>bF@PO *t\݋0FMMxij%kKN7([)4?SC.AK!'yu.](Q,1+XaiME%G\QN,eh4xgd } +ZDa&e(pEPJ KoS{c!P죟0#4v qJ%i7Dt/ٷW Q#j8xp("xoBɣs^%] CIuT0G`@|/4C#]et}$(ݗ4k[•YG-hi/e=(`,wJv-A"l?[09hޣ NxlUe$,Q+U{֊^\{ɫ6uAC 4lȇ@TtOU`[v~6٪ˆβ8 l@ - 1KBo*T^ Е8u4:N**jE493i0m&Eݸ5U-W BZN$[MmPZ*}H/+ҍ@ܑܱ;V6KkJf[_$}ٶA'|H|(W$na{ds#%*gL}Ar)3Mel5S_OQL7s8,Eq}[6וv"]qpk9SyFiRBB[uwzϩG iUWm*"8V\Od3Zp3^bZ +Q8$]ҍ%xEΏ4&GUibCWX*zR׉Z/ #|, XYd++Th;`t0P`\ƒ?d˼st][25KO;oⶓ1S5 zcL^֊ ` \gZݍ8ǞzV #DxanY]r=p*lY8zR?ڀj o#LimG7zu X NE3Q CcS)F[:Gv[gp:GiS^4Rz;#g(5$px5}pČ[/V?E:ӽ,zPTЏWHIcW Sda|"C 1)-GDKz!ŋ3!63̙a)< EUb/#B-q/z#(o3"g[ȥӎu1,7"d +a9٦(,N"J1C/)AJDf +`$h9;RHH *U4@'g=%yP{Tii>8|v +T#"< F#hUwQm+rsH:amD3st/k0y8EE y_/𰤰xvH*`puq+ètCҁнKN' ){#LlLU`O`ESmQ=CSe0a1,,'5 t٭ڻJʓ파̡1F cMljrKuݑYkW-`7u̪"(G~'yP#/թ )bM ܟ_s=R;"|$֚tKX_0Kx"Vd&h@O 9ցʡlę?z4 +>!Pdzk@5bZmPzxi#<8E +ș2l[rolޖΕjmcBpgi@W`?lm15܆ ~RvFHp3J ocwqFX_% +aSg] + 5S!`O{ y{eH6 MityIM#S!RjL"޹`U*~f%(ZtϾޅ ֆJbR}Zِ<vdTl/Y3.jG7&Ji^MQRqmdnwpOHlWԨSM jr2S* i0pjoLlBӝϥ gFzbr. +J{Lж o2:Cͫ 0A]ʘt4ZИp&,yr3$)C%fAa?M*"iJ$u +\;=!]CnȉlH +4O]žm%qi>\+{#gRX,y~Nu٦a88Vpl]Vk9Pɔ z_ðBJ`[2+BU@~)e=A cޡm+[RցgCC}(#T*4kO}ﲯk6VFFҴBg0}&M ,:gC %6Sc4 ^frOs@9u[|*f.vpwS0GvqQA]LI +dܐ>kh~rA03LDf +3DE2đ3Ǡ,+c9\!Vgjh28 ׻=y3ר菥31f;[WlKo!}DT8(7DZJ^ + ܙπ$,, "#/cu#^'w&"0oUX_#7إY<(F br&v4;fj: vg8ّp]??w|Kq5G1ɧ/_cVA ڴ$"nN_O)7Sx lCf;CMԂZيF(iԲ^ _1}7[Hsda-Mg+  l7c:n a|{di,y\ 0th]?Hó؎inV!dJ/$gYDwLCۆam +sMr* OAaN +`Zz>ۓS"SSǛd7ԍ=#z:A6lN4"X2i;snikמ +ƣU-9G$xo)KT=e!/5To>xFV+nM0Ns\D(X7ڑ聐=B\JSLƬ869)J~?č6M3\`GoѲy>MNMym`«q*Ю_V:V)Rȑi"& SMw#- +8"1agz(ٶ* HO`EqiҞh7 N꤭E?H0S< ^"wI&z%7VNz +s#D0*> |-9ݐJQE.rqEx,,Q)q,K"L6s!4", ca3Y;ѦsBv![.4r& Eע|91…|}k/AGdѭBYb:ȾNx5'%zfq47Hh+C,2Yv]}`~۵{h3m*ohʉcA}_{={5k@ /zP,|3N?GP + 웳 a89ݘ_ %ː!'aw۞S(D2ey3Xy .IwuRZ{ W,"H_&H #X0`(tGDcsk`bv(y=O۹ZjuAːmM/1z h'xs73~à +Lӹ?A@w;">%0SS>}-pk9Nm"X÷!ұBRH A YyFfnpɧ!wʑ[q./+:3=% +`pHj nȄx (/wJu-Ze!+HLzZH7u#v~}m:dlUHM6S*aMz,^7SI80̭yz9 Q/%r}{A +:=\̍xH9cJu SmҩK yo+QPUFs_x`ǯH}5ϊLP7X; Vl "]Xjvb?*N1MӥF ^SRo +#j\y(R9GR ($_L^$.=8k3YT-y"g8(8_zo_yhO%J]F>k\Eݐ`8OЌ@췩 6VLoYz)0c 4)8Z:'W)d"  ԒĞcvP:6I0X()k޲n0s2qHc} ++F,cv-xW rs'&HA{@4|v!S:w&q n{^IS|ܭv݌(sA:p(fNl9 +d{u|е /ФC\*Pّ}Ѿ싮V3 EYrzrA`[zW1?# >, ð`h+G3岻G"դY4>%1JdM;FYV =H3(M05ʚw^80#.훈37WaHޗɬдdyg9ٕQj&xGE0FC]:lK@V:."|XڕsPTn,K-?,IʰUW-GXmP Q(l19KL|y}ئ5$Y9Δ%2*ss%}x^T(̽}s ؤ>julEXfX{ҖFeƖ%jkfːD꼫r + [kΕMĺ~%(2x>tBnwֶal׸05ܧ +G0˪P6?|Cu~:k؄!xbx@B{ r3d+!Zײ-`u|$)Kġq%~!#A:thegzu q9>VT7ԩ0Q,j+8/U7%Yi3itWrʐr0aTSPڞ?% +)p^R{x>՗ow/=ܮv2 j"˱C7 6>j=i^~~g8TxʞҗbZ';t;!i-i{ ʉoAY9Y: ~豰~D/TS}ڣɦ+!2nF;_3CQп”>)nzѫY (8K\T~dǞn.@-"?Yw!m w3͗|{ดw}?oqnZm$ ooQ.U_0.'bcC+%*V~P$-b4g~ dM'4U/VvpI'No,BA&SSn@V/A!5<;Н!GĿm'O'NW,`Kd +-xCW1@le[/==kR \,v~; +#->[=$pQػbnq9Хb~*}{}zJЩzB@ cGDY!26)<~F,N 31SPTo={>PQގX0/"ZOt{C+mg+0r(r# R u&KTy{{3Hi C. Jd#(>3L!DgH'K6]""_+9~T!OyPcİ=4cJvc +ywݫz-j*pݱPD'纗AFB2p46' MIC1#ApH0#_\ѥDt[Mؐ{{m9QAW*v|נ}*#g^\]W=BZ#7^\ XAD(<`4ʖ];̀?E"CuuvBـ2,ǚRGyMF\wH_ +yԓ +;?"T>V[#0B[TE~g熄k)s HJ"r@`7 R}>8ӹ+,jQ_ڏkB]T>ynxPXRMuRWǸGU&BU9\y$@FgO O Z@1VDzHo/V_}?L,Nm-AKk3??e0P(E_$ o칙!~wפ/+v~GBӡ1~3L̖tܼBx{a] 5ɡb IjCؾM#oYl6P6]&#1#!A9uy8cՐK[Lnz.љ'$(WFG/9D$?vt-| [H{ͽP$0b:sLDet#p> Aj h2GA #{catޓt-vșXJ9az֯3T\:WV?oazL Qhѕ +CrWq>4B`5?} <\us獻π ȧ4X08Q*9pY}W8d$BA7"P!:U2%]3 3vX9!{Xe-KLjXg,>E2` d c^znNaI7|1oG+.'hH CbdXeEp}P^@P/UiYNCZttC1n.,GH5!F iĀH4 MtT M(ރY0FÅ>V# 2;߷\h -BP+ +kǣbm%DQ_SX/ o"Eg3a[Ԩqi⍇Joo-Lf='zͷgO}d2ȏG0uzX}:Bت`4#~,Pb['9'F|i(*)H2$Mފdڲ2HIGpG/kпCPxC?i͑SegN"@ψu`tUՔ-$Zk`X +žKYoUPCM[ѩp[ӶPS)6>v0M[g`#" e@Ռwɂ JOTy@d^!Dz]k]!D 6/E{4L sbDUdu?tqr F/9DS2|W.Ϳq"pFX*D1h3ݢ +ѝQZJ5 ]Sbm H;tfzѾ}Gwk@Uæ$= +:[ۏsQnl9D{ s%RP;jlD[h(#zRk<,+NNWt<vWf!LF (YdGF>z :vгa&009Sqؑq@.]z9+H{53R0 MQS9a?)?̣xMԑ/=ߨ>|C2]cX1\_HoB>GXK|6BىE|Af\A9/?>gD0rQ0pSOiӰ-aQ|̢:m3.;Zja#f.TENf&Eӻ/ݳŽh4 $gpfq\)TӅڡ"KeQ0{:d2}B'\㻈nƃ<綼`ڦك ryBwlN U"i5 -@Xc>׻N%Cb2o0c%O`QX8PzG4e%RgQF +g>(# Q|t.z'ʿJ§tq{BC_֙*½P/YƈdzrҬjbw{qE3]A.. F+'"2g2s͎A]Ę鋕 +KΕi'iKg̥ؓ fvgz!z{krDC2Ou%i1Ø`2`D3<D.z W$}n b0}E!E)4d)!zA'o;]O| vh[A ) +c kd(\pm;<ߢz0w='h.G -u~/+ƕzWu|wT4v9h5EFw#BgL" & 7e +F%M[SLX,:~ߞ,m_/+??}g-z +Q; we GjG`<<rF,?=M;ù;S4c#L1"&b+d|F9OW~༧E7OE,Y\Vj^7dG*X59ܴM76Do:"ͥ]3Bx3LU0;]dfA *%[f~4]t$oj Ǻ Bx? gUeޑv{): 9ԶZܰ,gK\b%> +\nޟ5g 4T {z e˘]&ޜL)3CGlE{/w9P] ֐/kƥqW+%Ж,h0N:e⼨z1WJ`& R-i_wގfM7"հvQ)nӗ-葵=GTF |!lCtZfL,E便;N:s6`)ҫZP u`'mzC*+dq`]] H#%n۩16_,쌨/Ͷ_a|yM]Bѻ۲pJ ͕…$oucvKXN`l@p5_@v(Ќ*n+D㚈RC 셵qCPQgWU y):FL-שڰ$1)9Fl d9>ؐ2B#F#@xV+i"R(@c}U=M`/&>Tf.kΦgJAVC$څZa7D@Z_[X fǍHLfV:eke Y,~rҹՄ ߱,9=PKi[c +X_i]d]-:F--u; 9/g~NR4k<x"̈ٻ}y|3 !c?J_JxQh#E6#Ø[!uϰkώđ)rxK{t.3`0QP:it5i΅ Ǧ0]䳤 o75Ͳys@Erz[δg*'ʦo3i6Dm{g5 +B}D|K~DB]Xe,`ȃ/ZX@"Q5i]mEjYNܠ ?.ӊ=}D최b%Tm:BN(3%GTׇ$Nk}^:I -YiK|PTC{jhA"^笈"ezTʳԨDžrgU9#, [DiAд-u*Pe? /uA ֢+W8{6E+gҶY'bԁBo6P$O_u55U^tqk_a!}׋\Zڴe4ܠc2/hJ Sutc `8סt|}UΨW5,)2n$hdl)1w,qg8ܽ#s i'Ђ}r'+;5Rs1Kn4eQ<ŢS=3FaRng<0] (RMeI0s7J2Y(VhM7­<2 +endstream endobj 39 0 obj <>stream +@ iwi8HtutAݸLA]/UX? +[sYYz2s5@jz$g%\Lr"qw7 00Ag暔Z5eP_4(Gf: %a";j&Q%?R=S𰅅{Ȥ6P^o`Mߧ^iS@ptxOlmfl2f ~vw&yoZ^r&HpI߫+|-=2<\W4ί򁞻` +9+b0꟫U7d|` +ֻ Zi6 +>`X >pf^PŹ,5h#ƶڦP#YO -ʟ*-D9>+ 喝X<&73!V{̄q3gavņAD㣑}G!T1-&25%@zI,T݂#yO>4zr0 s׋Ei2;f*bbu{ψH8-Ó&JPpQ\DŽ"v 3J¾d2> `Cj$c$̊PϷ.=3 `Y-$()Ocw4.d !- +ghL[š=bQ N։:q/wWi@Dw'^F+Ou) ^lO{!qF{ZΖLPZhf^DgHe^MaT+zrj2CQgQUNYIz2e@~D +:A #E*:&={)eSB&(䀢\hs^i0![kQ7/qh$ S]Aqru[.NLh” }c¢lEM]"$5%ňcϩLu.~f>oc +)z +]JJ^m1|:鏎[7ݰl.ONDq* +.Y.=^l5bg=`?u$tkf}D) qCks +aߺHW9`>ggĪE)ĩzNioN +-2 Iԙ"B{..Ռ) >)L3ʩu"]~0RR^D'e\Ӽ;໹;W1lf"" +CH[ 0cVpX5~1ӎ'lZɼسP`ϢM0vyQ@t;W7Xr.y^-':\}PRb`5a`ʃK~[%ɉ& +jڻd:6f*z}Hi'qo`&}^=0.%S ".X+ԮXRFQk4G<֍܊8JX!7b&Oq/ká<.=BAYc♮$9|-sL]yb3 >ڮ嬐OU2[Vp{t 6VچZ,O +w*,uz6[ ,/~].D`'wu*(:"z +SߊSjHD0)3_4_Q5^CCq1(K6(BU>GᆴEjnǯ'?毨 +XCPaWt"VR>FCN T +W=‚} +79ń;@j) +u9oO#JC|Pv,1kɃ!gP͒I-Hz SC;( Ki( #ZD +#ܜ='Lt[ +ŬvKV{n2o'r_3{F_ƺpEwja52L(fB9FY\3Q5QBb}zδoeVe^[L6b̻q/o谰C9mq1qP3 WP X ,kZZjg4tH\/=mڱ֛ݧⵯ ޸JC+43fL; t=ϩ58Kөe wLrgJ۽6^F`s\z{,P} z5:@[iuE}8 [Jʍbfi촞A\mIvK/n۝VK&aw~P78,Im6M~,I|?H-7y?% +H$J( ogtWtf*ӑfl_ZOh[[bC LBMqXcňF=(ȟB3])" ›`Q!̯ O 0$$k6WxGbWhU694]<z͖0%qbk`aAB| {(c3pQ-AĚ]^y]=$u EńU&͐: EPv#KrYIu"TBdE#MZZ3ݰܖHͱΔ?g$ǠJSr¿|Ұ^+mxyǥS0t~-Ӽ7J,U%vޥ^#6 znjBWIhݳa>7w +0w15#+*bQ 2kkXZ`zqvbG'o>δO.Z%-X[ϳ|+@Bٵԫ K^lGI_\%0W]IU=]C7NW%6Uy>,b5kAH>=m5jJviW(ʈTHuEX ahGk`m$,)-xgzz a#}f ̢=O籥)(an< C'2{AliXR$4_iPwr +z/VW ,۲ D}Aӣɯuí|o +g v,m#Aqs_c+T$e,;xnBc񖻆 +/heʷe~^?ͤ.^G8Rٹ [ivfeD9-c-ϋ=>&C:%I@HtlK#P(?)aӋaFR]K1 #$jMwyHyQbV{ɟ4RK +YQ峐R,dSz%gpv^%s02ճ9ꪗױ>i'ZZ'7ƇoGn 6gSF˛*bʿE[@IIڦ I5YȯK+QJ`;ƶXh4tAH1@Ej +rv[k\Q˙ +-%+pPbOд.B, +5 -dYY};^qÒZtfUW@K~2\JrYu0|ucfARQ#ZdWuVG~;kA<@Un7=Y/$7X%LW:imٗѧ.Np*b_ [QәQJ }q@֛Np#nbt&$HJKơ()Z#6_ ě)},*E\)y%G.Q&V*#j9ܪ R3-Y*=6g}ڟ⢶ղ +K g=᫜2n(Ueva^R7X(vETwYK\AGv ʶcV +qΥ"bxI7՚שĚSJGrV{d$lqշolHj32;ԅ62 ii?X~d>nK>AeM-h X?2 +xj `a/QX W4,1|̷ l)`.}er(.oYaY,>{R|PcT@S\PAbGd( 1-\U_p:{"̄_YΌ݈tKDH3{\ H2~GX B BUoKֱgR];:垗pO^9o3Z700c!-,A06ZIz.Vv6J|?|"0ѶfgI "_DV!]w _h$q-`D2 +'{2Xz`EpbS{҉IY7cxWҧ/XmqhBe2Z A-S#p 8 +e\zO̶-J uIc$ l%K]ِ;ϮO0~bS@d2 +k/j\1f.UW^{7;Ӣ*蔼HY#f/ šu /=B%l~1Җ.Wy##XV5c%P6.kyc4u*ky"]T2RCگ~{pan?~-5Q~)~s0NJG cҽ!!Xq{&}yX *~r 4SVB-R I**[ s&U̎E# +E]X vӞӰ=jU Ĕ#z=s9^p-买2שT`ƒVւk#_%#)hPeAXGF^*wGaӬ,~&Wh !97)ş8\{Bբ!)blS;4thF, +Az̓HGX}yd1څ~ J`3sӥ|>ed C^u$@i-OzWf 1 +-E\X! g_fX/vew]6iyܻJEQm 6$g\qlbfZe%A3z8@loGA1 +3Uk(65 ϻMuwjў䂘YKg`C;BVttŘX&CA#EVP:ܯ=զ|u0B|5.w _~@x`$WQL/@혈]Tb鏛 gzFu,x7fJ݄[7p,L7}.ҦUyr HVZbAT :uEܛkaFku~+3:ɭ?#b?vmW-B }Ǧ/)n0| +T'Ⱥ=jVҹ޸j}X~7J0$ o}y-v4芷2/y4*i{Aސ @| 1kf"Ah&]նRK^롭;ގ v]NtZ;GFV>>x5n +6M]Uh-IO3wJaC}5].WJ}o?Zn9v +L)J"iΧ#Em4Ies\cdj$T,T%pI;':]bg#R9h7&dٖ$ܖo;Ii knؖW +ٸeS-Ȗ6x&"еiAS]QD ꮁvL"3)(2DnƁ +<bh uҗߖm)?vK*F|Kට| S첕btX^**@y[~{*JYv{ޣ.E.i{1V䞰+"e3Ty6qV, ߾0\ ,7ֹ@&3]LovZ S^Tŭ ̹UidίZZԙMog( T{NWUyv- +AN+p,:OYQRТ}&8]`*Uzs@YLsGvξM6}-\۹zHXͪ}M]&8d46eO9ؤ$(̇61= d .N cZ#ra؝#‘ + r- @d}zO֩WiѤ{1.=(&3S&\hk:9ꡆ#9={L?qG^C{z^>}=Yp?caKP@iM~~QKǫ;=ZR>"u+R8YÎC-AHn=rDGI؎@(7SQa;P(F9 xi;&N_^ۋZ~#y,>@ ؚ2bcLfUN~ a +ML՞v3d;OwP5@nn7A4cVG]ўde4Ӛw(e%vvl^!]?} +|S +g/ƣ8ݗqĜL} ]7JHw{"+'7 GYCxy = ?í82i Y 1赗rcE\ܳl}**[ 3ڼCIXgd(A*ƮVgtGl+(=`z g„v" @ 0 >JdkWLQfh?RedŨ`LDs*`kk<_Cywae UGTqXQ (7_p5yBVpcGo IbgZa>kagX|nGAS pAWM{z BC1"bam6& + (*%y~!,<1 UF1K^jHΕ'Xy3"gUt +julŝAo~/#n1\/}֩vG6(Oӗ?rw6GW!8+$驨v񼪱C9и"K,,]VϹ/+\Sl\\6fq:)-~2hUEuZPq[{YI/}4K<>BMu}Cx*֩qJqJ^ǿ(꠸c$< )xHS&:8L=+ETH:/WEq7(Ƣ>Z}O//+ɋb1t~&ɡ^Ң=<ꥷ;GD~/e2!K[9Q5Q1 0 rQ_xwj +'d9W-,'KJE Gc \FVMhضfZo"zGa衖!A?3ڛXo]| +e߆Wjر) *hzOg?ߋƐ/)+(wπ?cq8)zKQd~F|kZƊ͆Y/{,.XF' @$S +$ +i:д|?ܝS2Re7*F~,0oán̯<O._4b8BiĿ/pݩCh;au_ ZFnw kEʶUKAW)bdxY=m*ڒIO+oK=q:8Mҿ_ #-(,붳,^Ք+w~>j1NU:ϻlCCkdy&1n5,p!-e;~Ub\: }fXד-M3jh(Üiy/Sj2Ct#8gM=387 ٱ@tdk9"#__mggyp,"8)4h䁠Vhy`]wc +~^58C,־f^ݛ0,wk}%Mhs˹=8DUN"ծǠJ!w]=,/ +tZ6u>?P`?"c&x:ahuI.;e(  fY߸o-햌a9?yexV4p~fK d?zGi>vK`8ש_BK-e\{ *^e<:1;yȨf7fDlMn}*v_Xc,-d[E4Ωv࢑0ɴψK* "dE t2SS?G-CH&*)`aӎ!0,X&#^[e+U'̮j"^y3 |5)a$NP2aD*cFOmUG׉P̏Ԣ@u.DIHgz%*eo&N{]hA)^/"m1n,nF%vm*$_G``O>tB^u.}M}QͥAlؾ_X-0.Hl@1h*eӅ5gvńz ^|~34\v|DmoQX:ZXD/I.Z:Y'2DBD"_'(M@ 85cģ5.T!!Ņ.ě3Xhdqb`~Tu:0 J1E&EYqCF#U_]#|'#Hr +** 9M5T|3b]@݁bGEvPlEǑa`ʏfGK{!DQ&[C'o7 lJ&/Nd9pޘ91└3Z2]Flp +( W~ $sgp+B.vniDѨA,q"ܗCnc9'ߗCxRAE{LN8. + +JҗH| J5Vu~#ԈC>U <;JQ$11 +#%]6qe@u_|{%*o/j + SP/b*NG@ew-N>@gљ%(i=mj7 Ts]vł*BcaS71+7FO%3tVƬ>|7,P0o_G![/ݏIˏ/_ӟO?/wwOw_W|w> @6{lT߯OD|~25(x^1Q`@_Ȯ:Z?PS_hb VK}~} +䡇D\]@}$+O)ȽWa[u+?DIDE}:r&WT>JU5E~ {˻TN@y˜QL l3 Lq?sT*Yvr[JJ.f2V~(Ct8\7y(o̴)V႖"Ί?;^0f%OO`&tg09^&p0邬 U;=_= |(%(T8?5\fo:4l:g>K,?OӖV/%fZ`* H'ثDjYS|N>Ǔ1fkϯߗե['7}!>z?%(` Bg8>ĮW^- Ǻ9{)w{O:_wq UJmYŏ^\?>9۱˕'c (۽2ݒr3~ZJKεFg<_{r3 3!;y}(zc:6l?Bt,^W`.MuG[O; +D > qiqqK)[BݏX\3\Q! N!T.qG6q@7 Rzᄨ~L +Z/ ^zK%c_eA05 d4(Dt??J~(\ vp2a4/*/5 lhy"hÍ~ .P +^ Zw)w,p`2+N4+T(j,r=8Ɲ?D&SyGVFP-RjF))R8:tUZÉ>みnJ5^H)%zټ9޵GEJ;- i ߡ>zϹC8iN~Su'Z(^/G +0hoH0,ɸw--LaJ@V RKM@|;|?4ȿn:?vx$BK~OsV)i]۰TW[XM.g< [ 9v*5(߆S-OWU߭.;?KͰ=T~bӖĶmȮטE|+\MPRlj@_ގyUm.?~C`pi("iH׋ux54Eu#cb0 Ϥށ)YZ,[EkF +nJ\NuwDlpZ:Зʅ:`\scZ̽) wQ-EC6Bi_%*pǡ*+EXA>0/ +#垿a&bz{(ˬQ2Bv\+F5g jh`'fZ-#"\1ݙCYyU]s&2\"/-.lҺ߈aN2D0AFDGa}04ڈ>>] g}j:՘bZ`tfNx+mXC\HaS~5S cw8 XR +8}u?g}Ӵ7>2>DHxbaRfCK@*IX:uw("i{_Njft +RcC!²|?LJR .m 4ʰCDLBce' ƥcaj?T ZfS.²qMlhнwBP@ӁOuz骸ɍ xŽxዅWF!JMl ']sxܠ*4Y*r,&.n=+Q;4=# cB",PA ck#"vy9ha#ͥ ,J{ coފ:hvSaY{tvQJe{/U =s0K-X A |˯Abc/z aiޣ?O!M9.)"$uoQ?0Ip+(_;f6Ƽ$J¯ҾI7ה=s1 + +={YJBߊDsũ>bI`H>ޕA[Lf>Q.ESa :j8#tQh r$OtqUFx@ÉHQrdiQHOf=){i]a|=WB69U!!bc8R, 6p +l 1̧D@ KtjHY =5G@mH>d-ax@0Ȏx NKv1l/WL̑쮀{*S\: {@R(Is6}]_lӰ_!%~,"xk,8`iQl7~(ƻ[mk!$族v~KufW_@K+teoÁ>e, nr{lJ1܎ۉ*E;" +`2l">oQ&$ЪҐ@; !zLmQ ~nemǠL:5 +gw*%\Kz> UXL{&y/m4lͲ0A&{ zK6&qKJ<=aKc0W'e `M6:38~וoTeb0ZY,>lrE }|Lug{ppP.׸9/k"Q,]ꉧy:\p"QG*v~=GB+lv 'm9*ͷZQʬ(!Z +[}胤*ɕs=}S̾s?R5jl6si qw)Ga|ݲROre9 ӞdnH2T(@F8U] А@ +l<LH?~ICH4y L%E]^i͋$ x^]Dt( K~= ZmHB3=fD]ڵCESʄZ4ޯP6+iG VT'n-J`>(p}=@IsnAp2<@9ѣSف%ER]b5ԗz}CPDXK6,Dq1t:*%W\i+YŹMR&^L~QmU 3[Mo?|nչhऺ+P䕃޼K~F3,ϊj3;>%7cG_]UgD1BbD8[Dw{8ʰ9a߉9YV֖}J +ϙ 3 נ)$$ғÉ(p̲5I@a^qhQjHiYaG\XEyoˢ 2}a'uv AP0*!88% !̼`1 C*NX%c&# H vʋ%52+ KKǑC6}[C;3l%ݣ/~ny-_GBj8 +ƔF5&go7R>,K F T e?j@w%@9oV ]D1kВ+Δ_#~yE.$ӢX<ɛh!B20bmJE:! R45iy`S4'D` dD(?V ]*\$WH_SrfuPDL5;b +W=K.LSz6ea c5ۆ5t1}t u e/ +6Vy[(c̏-O!C8y9E~;*lPT 0RU7{f~hJr`*I5,~L~ɎǏ gAa BrYx~0QTzCh#7V +3c1S@awv_D^#?E#+Եn}?˞z?J4|? Y@GUqº{Lf_Z }! 081a~/A(}{&yc`CDa[M_-;!Dh>U(` {9ŷFٟZ#;H}kdύ`߳ +7k(n/@BhBy(in|Jzn͌_%$%YQy#Њ_ޖoV/(\6c}sԑ;(f+-|Ǿc̭Ǔ$Y9`RoG\l/13[A1cG_jXthO!YZ+o,%l.vڷڻl" +WƇpZ IP8u57sE9˟aB F=4z5]uJ2)EE@i.S`T5lZ>¯~;l"^PzγŒR|@f7x`-`xlHR9d`ZROwO*3}a Wy8*R{cd^!ELZu&J<|8vb4N4?,nD3q:[T dY6@+,CkCՅ`+͒ۉ"AVU[܋]&e,% +QoQb/DY@?$31!s3pѥ+u>:2 A沕qEJ Η3wMaWƑY9p<.G[ _4ЛTu|xH,G֢L70w9=bB۝]-hXe– L#F=7eۥ_HLazda f7 +vEm>\rɴw'|ύ혣tԐ3iPUvۏhD!FbIQǸ~M+6?uPM".kDS8T, &8d5F:0Mp: 8Jl5Ds)ICus3,$7\DNl0 Hf@Ck)5Յ_@MyѪ\%ℨr'ɉ6Hyr?8Z:i ?<M +\֕0du*GNX˦mH 1v#$Fn"&8H4+=U&M!^UAad"A_։eu>y (hV.{וWh:нIZVP6M?.ec+H,ʟσy̘فG?dc%uKj)b' B<ɦ7D3 +$^be>)!5k|)Ik+R G`0B ݣK1s/CfH-7ŪVBBmG}Efd웗ͥ5W +(y̚ދ9پC .ۣx]P0|WSuCQy&cP2sE?\_ nX,6d?S*-]UŘy4yeYL8Utc!Z`C \ш%.B-{ K',`aE42a%ِMQ.lf}nLzc-Nao xj{⥡w& ZI52l* B*2VLf@؆C29c,ԩD`'%Kf>j]UF.cyOFi>I\2MJ>Z!u]6HÕ>xpdWМ>oOI p^emz9ф8:j3J%AaW/l-Ni>I):A8dU^X5[!BN˥KM9~99d7P"ɰ^CXPH r#K8wx휵=fjx( ߡ/{o/MDuD?`&5ێ}(+6W"J jT| +a<2)JLj +N +0jP3w]=0(.R74JJ<3{ ЦT7c`O'j2 qu\9rWjd6rv Wc_0hOI 59rnԂM_eC֜jnrz(V-6LfYXO{ŴdNb>_& -oX3z "c2,yl^R r==v +z}I2p,=H- +۪ ZG.?t P624C6<zHjFy;8rɳ#yǥ5}hj#V2JW(M/aw6УPbA EvRb^ʑ!$F,P Tj2 ;#ZBr T+e +߆ "`LK-W9iUBX `’?NrWpu5 ="&巛ڡgKMf:nxF6C{J@+ehPio)j:p!YC0)[g+(oi)Io2T]sxP)jpi)K#1 +Ol$Qr_2ByHఱݯO}ju[}y K*2BW`R-1i!]鱎L=Z]K!$Aιj'|DZC Wp2K4 V6n0CĪ 8[Gy9@6v&* )>QXQ:K$9];u> #~}y-#z3U[ѢVH;vT62.1{P] 5Ն ,;_a +_bH}LAtQW +@~a[ +Lyf(|r,G9Y0 y&´6Ήr"BC^Ƿ+a`ǖ~I+8% c(wq t,c wʅwm\xŬgZ9wH͕ J#2rSw Z-ACTUBL&Á"w Ls^BnĿ{HjP N⚕ 7 M֪Pg#] bnt^*'$Lh h B +!(_ax_ɶ4ztnY G_zjq%[]jMU"pV4/O^$*9h'A3@n +@gL8,2 ͮ+CH?CS1A(@gj> 3d~ +OD(4A%I|+JAB"=Y9*c'8Cpz + +j0jYoQO{ Pre[L1RpL1Ocr9s$Ma6y_WmaWop%&*{d\EϜo^ a.pjD񌉙Y=Qt(@"F +L]~Z(oT}r |E6+h Zx@ٻ;]ŮR庽{Z *5B0sNz/ kJB N.lЊÞ- 5º}FBS ':2RW?IӪ?)땏ߔS,gtH)^`X4?te"o!?kW0p?V>^׽7%w8 EެF@,~#0F{""l(tRd{PkߛmA'5UMBGb ehɳp{a>l |aUw |T:3Gа#q.."W`)'\ }pOB"uc1cw򌎕CO#`f lYuP\4$ '&MyiJ'.K`Lg8/Ձe]O=urDApV҆]MjܼՈR*lv*V5Çu)_'>VTl.DvѥXtZlRuYB4zl_G0/5(fbQClqOiTcJm^*KRa)bIArAIt<؂)7o [X؜>"Wֆ}R| 7lFj +Ό,sB޿ a޽v6'[n5dU10i͚ @^*wnţZe[姁+=d (lMDf?u;(%Ғ+^s]z.96C*^p (M] lu +U0ǢAHpVsxƸ2.*ΜL2 . @:JBt[P[`mZFٚ /^4\*w[e09SZGDAf=EbNKċОF2*[(C+ǛXh +i +5 S*bbGW&V#˨٦uT*`,P.#5b.0pE~yuR/;ɔMd}!@N^ +-ܖORyix@Ĩ^aZ>^[" +bߐ$+HOrܑ&U3˷^u6Qw]3llu7RJvF%Qk'rHPӽA4^f`aѲ5^~DdE!S#/Kߴ +Q )4iJ~ڻ e:pӝnv “re JXjiI"gqY#\LH;lQ  ؤHq՟'0||$]8ʰ^֌WLdh6{]{' EaJb!=V@uzDL(ebAFJvB2- ^W[(c Om%r.ͣ1bo; OYa +tFf(Q 4&3ľhnw"G\ x5_QFiQtDvsZt + !(m;<~cx,=:pzQtM^gd=ٟ)#3&{ؿ$|:g&+>Sʏ+w/\3 +tL* -%bRYZnތյkP0CYk 8Ei;2;Š5;l\!LjJ3Ҋ?(#1`C92MW1}٩vMs|"S U H3r&VQQA'jE$q#|Lo-c!3 ,9M=@"Ng1i@#"HYb峒'l&(S3fY8TO>zXGJ@TFY@_P5~;mn(YpP)4a_sm!\?9RfOգ@{P +q_R䂐8MfhVyj5ս#locțNkVsA(z,88c +~#4LZ S"#lω%7ZD <(^IL0]!}8 yQX)t794g q +/31K~B 8SֱVgbz~? KU0i%*ׇ&ux0i$jFV +f*q!)or+te5ك$CP!:8&"Լ ZVi&&-Or!p^Yh,  MempPRAv]WPqJ@j(0]ťQ?Q.`ot :L,ܤeò:ǖC?Յէ3x$ qq{Y ?J9(¨mQ’"`!AF"+ D fg3 +I @UFQ4W{p@Fǒܝ(ڰt˝ |P +cT^*x}ߑ8]dr^*ɦ5u3qh'`ؒ} +oocp͗*WC[}яHQ%e)0EξGG +Is)={.Yh-q>%h yMkw)9nws! ܣmu8֐# ہg&Z穊)xai7 fiߏs +UZZZIr*b]*5W@Uy  OGC: nK4w@!FSψl$" 53DOhcxj .#{?;U:/O +򶤹s N-5PIeʭMgȽR3ْeC;[|CM&ܟ](A!*5u۾ 7!\.#V?h>%k3oq9Sx8@;$e~{CF= ^b0 1c$kN֎{ǹ-bEe;;>侑$-Ɂ`~?g+rSuyXeG2ccn19qX/`*(̌z画Vo=Uﹸ-BV$CEZQOE^0Ptp7׷DZ°zfyaA:'哄3G<׃&HB"0Q>:>01H&MA&n## JPv@af6!Is!)toս=n +ڸ"3:|1@q #j7 #hCUI)3x"V}dW:KU^WXJcO}XT0n>E(J+q۝AS߷AI#а;"w:l ! +"#?tW=ü-mPqoF[aZHX0{~OڰyN\E ЈiJsǤ|(t( 6~$H̕5V}'>p4 aFdEk?U/ h]pz[OaMr En0;a z\|RXEt8~X vDa\[HP2sU=cp.Ճ +z t.2`e'@l-P՜]**4[xi~EDٵsQ`ZU 膁;H@ ].嶚V r`+ePK|rvm%#.|:T)'~"ȎWv Rlng)KYoB4p@R'm3傰8zNΣv"XVlҞߺpib B|t=z3\-~ز=tRk@jW=՟akJ2IK>= +T>0 4Fx:ƋdÉ,/2 +[Nþ5G!?V<.5 ߹¼!8dK*6N*V ͣǒ iqGSq>LP7;BD8JW +.5+XF8ZHuKG`mА鳢 dI/70VhEy#mYEj[GNU5QfҠ #8ea8ܴG y6w5ϬX):zشmjrIY|+ (aPQ L06{I Q˰q*&š bjP$JSu?b7+΃#CD{9d-7֑ | ҳ"3 28.8ôZfvYqX&Q5!)Z|/g‵V {A+& ρA3;,Ċyط[6QZ:(c`XؐcGP :%1I^)sGVj4"{sπU ZD yΓ X@.]巈|3PsY-,*L(4FHC K]񴊾${_WH~'!upML{|^Qf;kA ,]#iAFo^5aƳW7~Yȏ/r띾.%(XЃG1J2OF9z3o3v7rʟ"$ :R}GX? +۽c# х2@Sd⾀CV!xaam!*e_Ղq`c]XAj҆ ڴY4uR=FGQÞB&蝄 ‚ɫ͞i%O^0w_*"rX{&lj T#{x C1`D@?& 3a #:NlsR=2iWz'(9J@?mdntؿ?H\.:ּ-J\q58moX[A\ﲐ#El>Gfo,J?lzi ^9j/}/O;hǨiO%k`ёj2v`;~^j'\!`Dxõ1UV_\j" kRk+I?EOmn`xF[k; tՀy,D,D&~eϕ͌k6A!;PE j?=X4DoxO Cs3StR=21`^){w2 81< X l"`S{I>$16l{hzr<>)E₦'ܶkn֬6yȼq?`(-V!r0UZ*R]3V)sF a{BJ(:U}̈@8vb0@gwXN az0-6qwBf{o$yﷸbeXtJ~s, 2j9hkM[D\j Ékl] ӗK^%G80u$TZ TAɃDm!Bl. m.5"\s;Șp1)+di gG*Gi@ Az3x٨;˩do: X<x?QLꙉl쑶b"+Y66K\ N/0Jqb0fDާ9^Fـ0+ڿ+a˕ `^wIlj+N r4f7ԃ ШK ՝P@ޖszɖ㣪w1\yhǬٹc܏R=Sҝ+q8߃͞Pb5uXgrÃٛ]vL]=F+(n i2RYǹ)~xD8[ʔy% |dž\ž#(@8\ᖕ8=/BMyvڴ ֘0D[GnP,v.Ew2O M T.S DhJM}km#9 adE" VFWdTyTU) >~П̎Ѐ/%= D; +qŽC «GdQv0o'X; u^n)ϰ v7ް$=+9s{)qq~t=jfX +Ł'ӈZU+ ^ӽ hqRP&Yz2;}\{Eu3,ۓ6.$jO)нΪ[<(3ldAQk >H'& 9>Pd cH`NwꙻzIrp֋䗾nlfC' =쬷E73_9BeRY6>'Tltx<FtaM,˹b<izQ) +xd5L2;X$6{`֯d0q؛[*K+4aB 8B]O|2`| +8sz9=y[5\Ȉpx8}Z$(B_SulD, ĻP & @=brWQ= .>}nZR0= +bEH_@\~ !T5Roå +/V=?ނMHJ؀[X=fIⶣp]& GxUh]hMS\0J(DJWظ; 'C<C_p׋.C(0H3S%, IXL1;.]w2n2(zҍz]W"ru؇,L7@Jț-V]2f;2Vc&*=rkɖy_]$&,Fe +Z]=R \" `8Xjİ__x]Ty-)3^uDX Z7aaz"K,*@?` R st$. 9gw& +˛lW$jq^\DW5q/r5RC] ~A_-jԼ3|&^Iܾxl ^iv'`"ߥ3~7ᇼW8ǿzE*-ZIK37qy ?55d'HhH߁O4'Y{ IHDh/]no`lr a~ϐc?Ȑk8|8LAX@ uk/vƝNG:~_:sҢ + C?`7q"_uJ'5Ȏ?2¦'@(0MRץ잎g0Xi9q%>PMP@4ĕUͪKE6`zy*{R@م"de#Rqb岓(cu^{SR\O'm f[ l܇c4{h?ЕJn@bl JBSROgD\ +yPmc d-7ו*09W2Mt[ttBA:uƒ٣QUDGYm$)gw`05SʷT8` .T +.3#Q`mԒMJֶR$W!7$˿Wl7®(*h2(?Y&>?S^7]@] +p6rxe/z*hWjz-avA 1Gĸ#ZDX5žEIcQU)A7_Ta^rn`uܭ5D)vý +gdʌ5[Cs">y_ `ϘnCN;5ÀuN)P +xDʁj:: @l> =2;kќVbTY Z$!wKYfyǵJ|$a;"}khT"܎0?%i6F)T/`,Z x8-Qc g%ܨnWJjh˪Y$K, ǃTԐGxzP,vg_qҌo +55THh6Fqʾx% +TJ\4eR +ZGY 9`%|2OIJS@ov*SܾH xh,ζTK\$Eysf +&;UC;mYc@o-)=茪@*J/r:D)LP@/hg;dbs};XҨ!fa f\Zgbx/W#E+b8ڧΝ+r`٬!0@=_ªə#rLVR4 z r&k[ߞ~"@]~v]#HmB0v"5 ;wz=,q?j){!+K+4<Tc9_u%k~Wz>T-[X ezÉHo`2ZGB~;ųvFƒ"{ c7i>;ӌDWf=OĒv!rgg"A>v"W" Bx.bl̡h5G ҍ<0UeZ?OUP?U?D6Ll[1`2~j+$X^MbeG KvBUǯI.Gs|Dۂܿ]*t/.̽8/1 -W."Fأq!+0Am7fg=02XY )WwaQx$%=Ve }Qa۝Lņb2u!CM5Xޥ v4{@~Z?uު0e葀A8PsZXɞİ} v"T%$)+QN%B,8c鲒y-x 6N d[ Ty `[ڕFt>W2A.;^J1^aʎ|n ,'C9 |Ӯ+D\/(W׋!М𳘬'ݻaW1/u<B^- y1zQif26 DgEX!Cp_fo0埧zA# Xۭ!2ۼWv{&M@]Y$]`vG$k0,yӨ<"{[V`(cGՄW&F̄?qVٌ6,=*,#1מz.@Nﴏw_-a,f@^ i :PfiQsFD~ CSds40 +?# +rb=HM9ۓ-Plo5n:OL?/˹%nf˙G^Sx4ŖH*u~ Pɚ_N0xДa^i{8aWD,=#jC^3HϓC@\kr(ݔ'?^3L}CE(_Y,{Mk;5gb@CtĆS^Ƴz Ö4'$~j ( "4AT?h뤙kØ +e{ۊ}ߟaȲتFج+qˇİs㙣;T:^MԀP9(;MhHxɛBjh!'m8b.nK|v9M"n)XuD^0S3Mx!!@˼_b=m#%-&#iNBFE|:1-Zq$2C(鐽0,;Zߥ;aםXqor;-p ah4)^̚~^&Um\TpG2U- zIEO(R{ﳅ=?a~R_.p SxsLNT^wsff\ :waޢ:kH v؞kВF2&؉1K/Fddhii't~/A4rwu.3`=.=#Zf\!0υP)]+Q%&L$NՊпV4@U ^P`${߄$MM{-B?F3@ڂF*[]7:-_oAG'=;أi"lwn 7Sz[I LY7z.|@l_A@K&IyoRaR7UgoUvkIph.8HtayqR垨ڮT1|BOqÁ +lWx/Qۃ'OvidaygS#fC[U=0K&{y6%yMg7t[* (#GFg-m: Wyd#Yj'Y2([Mfq[[v`8"TqfJ?N,arCf|9C4N~'{}yЂwFalA8)2 j`Qk n`8wsx6"bLH`N_0W +xF'2qe&57xȴ  Szq,TK2TXm>fs)UpIWa*:ܪ-xZm9N-vk$ba^~;}wۑ8(Ein嵪D=6$3HFGA:<"cK+_pǭ(vyzv[tYvo罇O1 +r=rm+Ibb~i#t9zKoecf(QtQVø$0em}?JY¾)*.Z'ȉXqJD)`_@p/֋/Zxt&Jh,Υ/(y(a"(H(Hȼ3-R2µ5 "LzWCx,fxݎfCR|~?MSJQ/a *8``{ƜO.v9T߮=^^WB +<|3r=M`QcXGt{BL;yL!hj%MSX[#=;`^ɢF)_bwLoxcgCr+6׉ (U[okv_%;N]pzZ xo:%#+Ӵz.D,!~:[iF E-*\ &T˧ꥐami[(XT&$jpG\xϝ#[xS/4 XȼLbӆm떿WJ +) JFvη63td%Nt*ʣ@8(BDWɷ>Df AS-JLXd}u=Qo6C%lDpjSњEEfɗB*/pIrJa;jTc"F2aR^TqQƳo~!BbόQJЧ+pasCP<.END |8~wJEr@ETSZ]CGYf*拖s GJ -7]LPRo*~C|C|k!b)륾)nHڱy< n;a-RvҭDyyG<*Rۋމ@Ѯץx'Igچ5%B - s}GD +~SyIUO#Vv}֓D!^+X$Ծaa]ݺhsJ4U/DtU H?qaю0 Hh^ +Tօ;|LZcRvđUN1Ev.@G ZR_pzWڣz^^hc! dOOALGAUJe!'}8j)jp:~pq +h MFC!X; XyJM{G]+?!/#z9) . LK<0q[h.(k > +0Fl~6v3 ,;t_(9ExDd܄*}% +3@) (XGDƒEa܍{nՅ aWa).0L D있lwk~la&:rl*xY02@! Ћ9-jE͂mQ 98V8]31jzا Q#ھlD!N%:QCb[H wk\U4ʥq8JPٕϸ}])cO皙yXjµ +XcZS_'+Od:I[PeClx$a뮀E>|A0b[ (["5ƵB$~HzʜRŗ2r.SF}r҈u$Zl3Z!TB.drz[ȑ.~AW쀊9+|_j48y,jJ)C 2 /oܶesHݦ*d5s(SA/4A "jԣ!~.%JQ:c֓8.B~r\^lh +qw{=[}rBC=c7*J޳Ăj(yk>٪Q<ꇟdUMRQj]H#lZν382IFRs5rqLgF⮧wٵ^Yne5*ցl& A`@Atc[ GrycZ{$YC G5iRL8a]2y+p)g(5z(SZw \sD~eRkJ=b91)Sr'"W;|WrYxm։Ʊ5z@ECIjA2LBw2E|IPZ!e &{S\J'~:Eoz*w"%j(YrgcC׸!}m^}Oj+e +/|k&zԼE"Z4^~m+M8aUꃒU&V"&%uݛk[Nh괨aA8^Oh%O)ڹ]6vŗQqD3 3=5{(+Cs<}XNm֔V -@~i:COJ. .)d;C@OӒ95Lč {jZNZN8@?)6#ܣt/~#"*₏NOalhNHD;&DL(Z"@zv_NEb&!bDy eF~+9}GݜZPH, Z<?#,^9L\+F!L15©ӥK!!S p%0Ȩ +ؐ|R nB;բ"qIq _(|<'qQ阢Eda-D_6y<.ձOs58Q’' +dߑz9nـg0";1%SHwp:eɎG~6m0W-%L}k{ŢuonOwV&L'HOH<BJdkpmk~#kzL0y< Zty%Lz+f>".rfQ;YkB\Mj7@tuUqu?-Jݲ7̒g!unHB0c]9-*nbP/ P|~yȌZxm_I)ajIZ;~R7E iDm#$&z=#^a4meO4O_Gȣ%6١yaMvdUJc9tE@!jg|SW=CU\!qK,vzt%Zm[ "{H,j On UxM@ >TaZJp'?cXƷE c8*r<luIL33iTWi(ʘ>%j!#`_ϙ(|"9lM`Y&_L +\p .|!ęaZucD#Gb4=/y׵| ȿPQXhhk~)_C~3]S7CO?x*y~^<'G4.!j&ZQ'w98XOʛ.[|mn6ډNdib!"B +҉p|QRW@"SOȮ_P*Q&xh34'rb";_BjU4;=MZI/9ѷqdzMF}ZEYvuGUS1F0~Yy4xe(?{c#df_eO޽TYUhYfvfXPb{9u跐[Oמ>w#]u@qR}v{@Hf̜ZP 2ƪƮ*ImU' SUVHQsFNr4_r@Ч#[T;&66Ө" X'ז,ܷNJXo[KDlPQF-)ֽ \ ހ!z#sfV/|]-.n(0S4vNq̬cWuk%90~1E5Y$rGtCj#o0)7_K1"4(<892= fށ !_wd_2&YOFy~'GLD-%b$A S OWQo +b&l!G`zy'5s1pSؾU R5Qe8屦HagqŇkY[" 3.A@^Eɰ`~Nnp35vanEL-K~};Ю^Йz~iZkn##:#$? +[Q?d#[5oAR ]Ƕ#!)&/pi Y a>a"x+fOC>ʁ_ /^ +;WWE@ 'jC%14(O>1գP^9 ++b鷻"}C MQ!bEuJ;ǻ?9#b5wؽPQ 姟,\ex}#h":ύ)T\ a VU74`e 1ۧ[ntW anPKM>w k keJXbڈvLjAi/ ^Dn3L=(#gȗYrOp@5YXAmGwfʕ~!P2KXF+ٛjA!dgDNP.tqlt %hg-RuLh@SΐyGufaZF?>t<쯺9.d L5g!$,ͦtY?hǂwO2R!XxNW: @ĈLוND5+ʌ':2yWL; +E5^*@ lZ;+< ךҪ{֠xx\m|Cw4M#l3`!1s SKɈsG<0 \C^#,Razd?n@JX? نQ|kGl"!Pt:TݵUFDq;Nm L:#ܨ);b''Qcx}w['UkY6Nĭ~,d/0@L>e>yΞڢ yne,vI4}lQ>~CU†L<ψ݂ST#6MP8IR7핞!LFdžwSAA $laicޟ%jryvY>tfj9=Q'KZVWC~N^/ p4)BhW`i:QDsYYp>3:' StS o{xGLwwE "3r $}D"f{6Ю]s9'^VIN#LSzMR+&[ҍnҼUEjb$hG7,| Z%q]PWzMB`А0x͋A,aǙ.2Ɇu?s#gU\d3][>[\>oJpmW|IPӔ-L+ZlWgCZz,^WH[n۟:Zi2jWDEu:XMT`#wΪ u u:kyx~w<m7=cCĺAatzmic 5{.|MsˆEMy]bJ hYv]A>"vug&; SpK~V@L`D(!'M(tdt_l +av *;hPGZ\:ƶ ,`8>j zD?Yߟ#D#DZ>C%0"EsTZzYj*q}G~ Ⱶ'#w6FpJ2]g|#)ˮkw`֢X;iQg0ь:y?"75O>a3dD5A!=H&kBe4?#b"˳ꢠ8>Zr1r!>3"oo# Ӎ v>1zJЗRgYOY5uRѷO{ R@.4E@i?e"ճjb˨jI qu Wݧ&X| PW +) |Aݝr}SnE4˕ﮮ[']{ٝ2@;6:P~n3~eX? +[TgoW|>0 X'  +WTK+3r<+eU陙9`jbQt\|}"طUŠE$*ߟi҅;(}yսbY(Z۪zi:-1.̬Ѓ&ˎZ|BS$!z|  !QpMH Hy~ |X=DALG^(5FtģRX֕G U\wEBsm8)CuҭiRL'ATD&'LZEDYׯ( C>T3bT2T tmK|Ɩ-L>Ċt}v<.Z>_؍h%D)Ў*-}/f 1p3Cmq/iS35܅`cb*;}ݽrs)9bBcI͙wHz&+b/^\ @XL$C +RΣTaP_f>0f`4X-{`6I"w sE()̼ôTP[WL;Z7Ow0Ģw^G0zP6KEl+'}'/"k7n6N\[gľoKs":2Jo}^2rm|q3ӕm"2G{)&P9TNp.#U="8PF߳=b6z"dSeVS~_:wh-[sm Wyz==xha}lk_ *&zOnO֎ac+u+4?MV0m)xߓtY0JW +Su&Ѵ3vE畷&LC6r~ ? f$tOeL@hA{Rϖ@W;F+ =Y0 +zL kCt/|1O r0*8+—EsVxS~F{:0$<*hFWm,$dEf(d"(Cz^_ru+pۡJQR͘`F:* ot 3ٮD/ mmC|d-F+O_W[S1fHQ8e|క[v) Gg< knXu?®< Ǐ,s:;|s4B1gC$I\w럁#ZS0p- D# 2~d~ eBlD7WNN>#53 4C<^/aIH:= |[~B;zʛ$QJ V쌣l 6ƃ3YAN:3Q NLY谻ɦ6 +ȟopg +z]#.jAC]W*59I?/S0+SxN9Cyz%T5iz/|t'ɹ'X6Wd$^aCRIȁBݧ5dlKXacu T]0| QVfXOqo:{ſ2]06xc1Dv@%3PsLKSħ3- LOR䛯~"@Ef9r2\v])㓆ܵ Zwh&MMbXkwx5.Wr206LȌ~І@`=͠ |mtؓ-&{R ՔwK+Ee'NVӡJl::]xXz砺8jws-~!eIҿFds-%F~0PsQI>/Z=VS1gQQ G3B.xKJA nĺ\Q8hEMMUV FINA,՜v q@^gfA僞'h3uf4}=W1:I"UƵ10ul)D@]R?$!{+ +mP*Zmctn*F.#Qt7>|4 M9;8%(p.j`<>=_lD)2}d.ѹ)dΫ)}~3VAD4-u^9Aj:;D*>]Pe֫O)EG%ӎ +3h9$q)z>4<%Hpҕc=|X~oaFTFb( -2SmiCl YxGD 6M -ʾ.dCC=|~0CQame-m%CtjQ3dQ Y@_Fp3q:\⬢H70HdMF:~|zuCmƦaHCJGHSSu!Dh4gUs[ V6%>#@p)ƺ=06փq}0Daq6};3bn\.ޥ[DہM+7Swym)ۑh/ޙ WģGة +[>WtF04g--TxIq;{ak{a ueK=p]vm][Gt~O^"?>kBEo10ƻ")W| 8:H(zfLo[-Ŕw YsњQ~ &=ZF P ۠m ,b~.*wnl?S釖 _3EaC͍@݂#7ffйPJ{`& j*(ptar}xQR@mEJ֠C1ڋwW_X$ e] "$KYz^sު]3_ @tm&օ%GoB3Dz Ŧ_F4yOZ~??߿_Ϳ??Oۿ['~ׅ׈\_n%TAX{gOdGf04P=>êRsHb&v~}#2Xz=˜(U(:U Gԋo P,Hvp 9B|*UJWYZ?h>m4KWO(uV~Ni׿)~.[kT)EήDX#4J9bBjlq"%4WW4o[( d2LAWVhFpJ'uGR +?~{0rBI+Z+]^ c۲/֢ +R T^=ߎ@@fo4OG,=$Fn9е 4_I6Ggt}4 + sjD %ԩ+uH+s+ -^a6{m0+uë)v:eϿ{b3fU#] :/JXd/GY\z^FU/ u|~3p|;+78cO `2Pzl TԜ_2i3肧$8r3h[l3-0v*=߉:j}.!"|xQ;ciDŽBRL8t|]!D: +vS3ek\M;M9ҀkA5AUS5?>QO=AG Álogy;b^{E,)Z=@PjܺjHd܈Me<֟7 ) f2y/GO/{؂ D(1%R iS'- {71Wa]5Q@b("nG(ȒZb}:~:PEx0P#9{+FJ/ODɨjGazTǡAĩj NF9;ʲfYU#Xd4/&]eI U~-uQ%kEi@86{EDD[@tF+ лÖE= + Ej2630H@_]nmtSE1a`y6D*DU`!"E,YC_7;SMCF4ql;gS~d1@ha[x7BsݰzTe6?*, +_z`e@F A%]Zٿ:|!)ݪ,*;<OcˀT#pvezsg\r/y `]7ϞB<\UF'=ƙVu-dʏN9WٓEmP-O~HTRߓ4ڭjbQ(iXhh\B":l0R2қbty:L8|8{Uk?s: (*+O5Zk"ԭ +Wp2OElSPK% +1[y1D&HJwF{?ebv=,} 2g;?X{ڛLE3 = D[Rbnvee ة]ERf`j5,jlq&W FM<AN+ԻˉM/#eڑ .MJo6+"S-6QA[dȋDΆ}<d75R>#JcS uIvIo +dثET풝hjE$z@=t{kRcd(Tc+Bcy݉*Ts׏N~ S)4B!bWQt!Cڌ"6oD4cbOLuF2!#y͝ ]~z\h~8* 58Ʈ`CkTr^G)psaXYNc0cpkJ0^I>uþ z8V(8#a>x{+ !@fhYDchqy+ԡ ,CWU1-m{HtV{&ELX'.G\ ˚"[0 "pVaRo{،Fsyu:{nFNƿ]7=bߵK@r~P}նSS;9u.yq@5w&N[3R;dҤ;<} p`=hKn1Y )np }>G;AֻeX_N$ +FhP%ARoW;t;#)YMD}EuO[hkaZ~:Hѯ\apԙI,$[yFЗ2ӯc 8q4r;"; +?D?}3 rpwC8_ +?5շiuDrY-޴aK '/B]%<'q_FG$>b:{@#[;?{{:SNS`xSÈ׳;xG&1jzȘ4Q1šiiͮ䤛xƇOi#]iǬmXC}GO?-2qM/ /rm8~/T"ˑG9zEዱ^(Mc#{XS.5ց. +*݆mΔ>R`dtL #N$Te=uckUȺ@MV>-5D%cR[FE:y^]aS~i>YYί+\ r4e#VN_&~]jYf4@O6'b<mGr1Τ$ vE{oMΟ{6@F8;sǦT+{]GAhGUz=ug\CO~fhEVz4H שIH|zQ]S1`]D~31?61oER4{J;7`hQ%`M$3 lLSAG:M#B?L\BLL(3⅓I\uP\StR _Jsj Ug"Rgt4'yEl-:@o[g1}u'MOO=wnke#pwHI)u] M'rY$)" o<3SDؐ&%^g.*uejX+%4c=ɶ޷sCF[7dA:I9s:#]X9QêiE ~=yO$Z^OGn e)+=_V ĺGb Ǯ`a`u$ʍjP7A**ډ*hZq {OBOƒ{ 2`XNDBL~فq"L"tCSMi$"i" jkd|ھ_7M^$A"= c jq ,HgʝJ]}ELF_u?uiE5jE,$n"|Sg+ WD3ƽkD远ʝl0}'Ôi2k]TԣP@A&²;w +x@ėmC]o-|s12cv6 Oñi?D]'=  +SQ4bXof$4 PgtYNQ˓C~Z4pFRUyW?7QجQ nr C`΁tQȊ=ka<-ToX { +k;k?};r_ulP~ȭ! +K gG]"{*\YwN2Se#0ְSVf%IKQY 27Y*|@gx8/RElÁt6g'}=nv[Zxkp.Ä<\!ig#0TzE B\!JV0cK0OD;ۃ(brAfA?$c?/iq~2"j'p\pN B3V6IP@{3kN xы?DR_laGuYD#riQ1"F?pE#U= 1V +B]?~#$#{C#d{"ZwP5яoQ٠#߸GN3U9\msQfԈnC1l2O-.~ygQkc+\6fR19m &Xm6|Yey +.E(t]"0f z; %-"tRUYw |,4m k" ڨu1.(y)~E䟫D80`{N0`5wx +75hCꍠ؎eOtUfνU ~rT7d؃%5S vs`FSBin#M_IS&Ry*C R[=欂 (qC 핉 EԀ.AU%@0AGofgر?խK Wvݣ)pj뻹Qۑ1!\ k[}kL1Qx +h<}єP)lpP\ck8;`Jm:"QUl@SP*L~;+ojMe*s*L԰bԫ8Ę[A]2VD"W*c B~]QwD;Y+3KzcUfހ=t&@Gb DwtI4n*"FH^cdCd?Ӿ3U!Kpnu  ܛyn'-zlʍϛ75%@U@Tq#DsWP7$Br(sg<{7Jgs%gd:gI/(yW-Dmu:ݫ"iiurߞ^{i,,jU^Xa㋌U\fnG:J;wEgcp̧uE_|Z?y'nk892U)fD <dpA[-qy5 sQPcLXh ڥ#A;ńCɅPiUz.B0LdxovBPtj}C@psԦG2GO_/'+wWaV(M_0LKqT .j 0wWyh3p lT/5 ]O!#3տi9 axd)y-ʈ$!+b %aFկ1ח{o +%vtb8-v`1 +#H z`#5Ud!蓮q1< +^0W5) +hrW6Cf>N.#x4x!.cW<~E+~56W]i`70y&F({9"29EQ'sfv%SHxܲJܲڙ+Pz%DNY$uWxYQL?:Lζ' įWw50n DІi[fjL~§ xL{Gy>ts-e~rKcuSGztz}j +Ý*3R#աcpRֹ}¢so.vȡrJWJ2wf=$EA;6UOmwBsi +*~@^gzjЋQ1 r` dG%9wm]?{S{Cr53fkF^Oib$pn1lWza1pyM Nr^t㼒Kb^9PGFm+_볘zۮ́I؋nO0.wG@a [+iNa8OLyRU9y5 8 dY f $6;2ƚNtN-[I~}&q f3?f&-]%ةm^ů'jm[2^e;mIq:#_/؋BHNDZ{Z7% _2vT +rN5 -pz_h᪨#֛\ǐsI\J=SratyN٠#Iشf}6gAk1zgexp=JL/$jgP!`/:ti׳ZpoVM Jז42o_;wA@*hJdYIbC*G@b$jV;*U8*ԏrnT &/ϩV6L@cZKjiH61\BGܨ(.jPy32KbXoTյ^5ǥωNmvq@ )O7"Qxp/[zdO)Ž "dj!G +Qpqi+5J8jzed#7*.rOi1@@}n=8axQo|hjt a^&1n"(șDhDBLjTϕ+y-Ek8h[]Rd$3/X#<M*0b`'A ]fWWslt ʣ* +]}I,_ESE˥B#B_z/r"rlϩ*ӌGwQ~nŀa[KZȔLmH<ڲ=MWA,XEmp)N 9Nֻ) +s` l\4J7CXTҷ{֯3ب+DG^5*,SF79*s&id{ +endstream endobj 40 0 obj <>stream +4쐪/-`}&B0R`|f%KIMJV~y7}tj-j4|z\Aڙ6)_qht 6ƫKx\drV tZyb,AgHM2\K^9Ҡ^|L6} ŕx5&K[ߤS_A|=ZN!N#ԕ=ZdŸu>hx7_K+H:s=\PݴV Bͭɬ St$2Ƚ^17bGj( +'9,hhK[9C`u*+ٳ^,q,$0W8%D*\aN4;jƜך +'20`َ~1f]Ubu(5J4fRZ{hDnr8]\A*-+._M̎=ENV/4hC'Ns@>'>2dUvK@X}r""Q"l۹O-{LQG܉B5"ͶB Wo6aUCcQ +w{T:j!vr&圩m )|ͧW|8T,u|1"32ܒDAس0tNs|PQh+HY_'|la%cFĸDSTG;^/#t AxmgH[RI,W{`敡"NC]9 .Ưuq ;dp%#>$SKSٵZ,2jn-=ݐQ&=iwLük8^MB kP;}U^Z@ X@ۙ>z0Ə} 0KWS/TfR]oŔ5F|3Q`*`!ta7e#o6,EH5dux;(T֍ES]`Q{c?HCwnm#]4d,v~Sz g:7؈L9㏞kĔ֕&bj1zOx6RzpjǕ) ZW1hddKxfY+j}zPFB  x8b.;0Ŋ,%boaҀ#*kcԁ;F-AjtA ިp;>5嫘Y`c]*멊D j'rO"̤`q"uԺeQs:x1|6>Aᗅ:ypoYس<#R;Vc멨).`Z*+θi+tZ]mPi'`LHZI1?[ih&ȧ +98qt|Q;.p]ܡ["tJ}9a*QB|girkQ4ъ4GduouNtTK8>-O 3[|b?~?Cc?pՓ*0UU-H<*I:]}98жh~Q”̡7妞9]X)129"_&0祈x`bn ~ `nNAU<(':_>n. ߐC णiU&Եkz?\/*/1,#6-TƤw<T +XTeva}gQl}Q6A6*HF NjOns͙87Q}A[Q*5aqe)kGBbps[uΰ,2C%.%"^#PT^RTYS"667i mkt2ِBfoyLZKttd_:F;~" .wzA[z/ VV.[+4}D=Vo3 c{ݏ'VzNgιi#4ue1{$? +u-BZSE+rԻ:o>q Z0n +%e|NSHҊ.^5'8u_QF+ +IaeOX4uFb.&cpn湔egGA;Av>#&DA+Dg4~!7k[*KlC6:Zȁ] N +X7@60QbWjx98 |CAuEQ!@[#c +? UT_p"PTE<$=, +Hľ*%UyO'M2渆 rq1.2]lҙrP$gsg w:2K ɮOk'j/^y mdXwX+a #1rí` ibUfI?cD3o U8+}kOYƠB=D`FS/2'g'C3RYy` ꡂ:%'mr6חm 8*;9hci}&~raTiM,9lMYe\34j ӋfrvEfIk@=K2:d4,ܛ X*'e(`]_6lfdTo_wz@c:D;JG>:RL`ixx}GDA;LK~_h[A>i2xϭNp!T}ߔUzd.߁Lt- +~ +@<=NKeP+})G`3KZ5u'y G&5Sͱ5N +FĭeԸ"\fk& &X'3߿yGZ*o˸ +dHġVq%202^9c'- OB`96i_k>^S0%UP,Mzm /]DTMw/%jDeAS СdcYKk/&LjΚ<:YKE\oƈNvDn G'#Q jJh=6k0В~Sq!-:rI`Ey=$`>ЎuJtz6HуS, -5eWqmzD3F.h!!-Ǯ̼^|It[\@80S6VR?r&41.5jNRIZ5%/&`eKAqqY۩=:TyHL #L͎éTAqzَA=Ȋ)@ .< +0&| I w􃺥J9UWbwƱeUؿ#a-@->SU#wB't$[f,a7$_\jz 1A<`V1C4uCB mAxnidۊ2}5L=[%fy1_h>0<=A>QjRZhFHzʚOVvF>};\Zz )]"YT ҷL4"={Hе) +FUnªp),c&gru!ݔd(z19^E)mtX7~%>P[E-%Q>6<< HPB-L('*\ha;>* `"&nj 3|N0~#o縘Q p\t*#|>gΓಫdا3+W~@k (#Lpyˠ hvc֭s-;\D,›n|=I`&fT%eK]%ψd%)<~DZe + rFf'3`r}CO*}a Z{pzȧBjvKWTWjH1e m5zcZe zws#Cz +۰@V]ێUF w>p,ŧ GU**jVݱmcTL}znr^M# )-IS>>ޛs;v{쮫XJ؃mB٣.s;HkbrgoJйbb䆭etk@LkaNθm1g% /D +c'{?p(#$KG"D2CtdFϰZ5¤?lp^Ӛs"#@L@|oۢuI@9no vF^JqJ7?{m~^T]o}")ȩWU3$XFXF4DЧ3'4EyI"3J׳-ם}W(f#$p| k_a5H31.{6<_2>"v+o8wy){fwS=xE G|FŞF`4 +Sf]6-O $XC/HkеW#oDiI9v9P1ThV$ֻmQk8ķ˸-GEThx-^6qvl`:rZO6\Zڡ=ǽsuf'cIOST|!yDWNU@$3(S?# [T9{W@jMX? E. 2b*ka/F8l?*ϵCҩ[moegR"Tˋ| J3)<ϰ~&Udi8dK5"7vn7"!ȿNtV8{7l*u"DkOvlZݶ;I8mZ=RO= H?daK$TҔsBXE)kQόz;wFSpn耴uW_O70q;"4 -p߁D3>-rg, !'Tpm]\V˝zɨ|63UI^@f[19 +q:Aɷ3Z t^c7КIl5@V/"[%eHLJN/ׯGU-#9/`}휹rD2_$h+j uåe_Yi7`=$3:ƍC^#7=*hQ=HT7d(I3xD};WիDQ|1/D8@ﱍ97LAѶ6\rm}{"VGݫu!x5#H:(']ѭI:_`\$dڍS@+3{s2WVⵅ`>{&̥$tPN~[6Ҟ e+ 6[)(< tmQ tfs%{dˉ^8W(JȰ1vb~6wЦexO*\ӿiu+k,${\ٶh@Y>!YA\ubŗ>H'aͩC w\'_Β0؝un8N4fԬ-7eFq?rbO';dKK[إ˾GNk9=3k!S7}$"f1N%(:Pr 9C*s{(ӷ)5Ӵ:[ R0{pMUUⅥoi<]U 6X87ߟP>e{,Stヾfn]f|a(3 +>3ZDP$NOôٕS*SUDZ.$"4;{M?JbD§QÌz&+aBA%3FX|k3 v=%$}g/@륪ʭrS끳 >9tFҸ+"}^1"F[ }5WNnGfdegVof#Á1]:j`қC:!%j=Aޥ5omY|5Zw^gľ}-(<|2 \[߉X;au2kX+(+|#ս ?O 0ۑo::e+ց֊://~;GVo@P\5w`U9D9SlZt1zFiꢬLj" 8]gPDc~.FBS"Y'p衂Rg+q 0,dUP PY\gtm+Čb1E<C{PASA߀LAG>7|!`$7~pż s*UYGn!6PyOH#>-z0\Q;Y eBl)i+t3 +m DuB-'O# +O16U(%w׿R4ck"~|lM}>@pS|HASϒ\\[l!wnytk鬩wz(y8ߏ +^P=N3-#*"#U2ba\B2X)6CcGi%Aw֍eY,2um̏ 1,ej~AoC֚ȷFg cwG??'_]UEg0:k4o& Q/x`pQG.Qbюφ#f"D_1wD}"` Q.Z Su;BҴMӾB:BޞP>fU` i}uwbCH+%Ct[ +sZjtǝ6|=bڈR)4A.+AIj+7oxءj~NWĦj3.IP[IWu?N+[noI$[ZԈ}F* үa bn]p>7M/B=*F[U>$O1sշ# JO*Ep&dr+O bd*ظA̓ "ÖL%ǴaG|Dh ~HJ+ZH? d`{@d$ps{ +'|"X1^thU."5 Btrٺ,lfr)='Ŀti)~QŘ",uGuNU0e%褠 7!z 9e<[tH=c_q*\@1saEBNZ:ŒMCu)!3]Ddj6?b&VJ.ssZ5Tpy9R?T@!$#"lH5z>"x%HoM4zKG(Pc(lj5|C_Wyf'EljwL &!&mtdSJd`*ʭ,t;~ݎgV3I JNiD [Cfe5PQAYKY. ,;846ə~X7!Ų) }ZwtXwP㊦(BL4>#){~RiUY(+rQbFJ4g/U7b0[ngd2ۣ͚27!h}m\9 bQǔ 10JRa!sAoA悪L&9 PTg|9Zl+i B#+7br3f,.Y<G}|0]19*Y kBvaQAf]iޞ @DG23,H2#~yޮג8׻/)U~g^J}cڭs`3:C=C# 獈{ BʝU>2##U&3|-Yb:Z{2pC!ܔ#\?G\`i.:)#R52T=|MLf@`DԲI2K'3»NA \sC>0;I_oZRezqO%dQrz?^ aȀsn2G7wHfnwJCepYRC3- +ӭOM +HK<TVr.½d3W3H2V1jSU >03g}l7ߔ%_) )ePP9\fUr9BX󹇿X.] 2bGdOo8iJ +[jݜ1 f p0]Zc\#68ptɽFWNUQJ KADSwq*wzь|ȹm-SIJܫ+gDp>М4Tw*/.BYqPw\U`|)]/8A]2]3p,hc8%dhɲ"Hhzv#\.}/c?YAEQFzLe=5ђ y|H>.:ÌEi,j*nP0m + +9 [m㊖-,&tQPўJw IS,Jup4%|bUXŤeዌwN~Ll:$hP.,5!@Zd@仵PQKyJٍYikB[+}6Э5^JEdROG^Kj; +~E^r;֋/t +GdCq6 `u)IJS/;0 E׫{tż_Bm ǁH )b R"tSV`r@]](g; +Ij+za29 l,pi6P%$%!y.R13ɧT?ZadP?=|Є~B-??A3o_'Mo?o_?Wo_|ݏ?77_u_D\?G;8 +luvGa(s@:L3i*]%3MJ:fzW2.SeMr-YQ⶛a;AG|@]vmEU|{>!&$dZhsP@ 5ބJ+ j ,rwykd'bPTiR7#D&a\ &PLDpFDk?ao}OTIA"\p)\8ac6r4?efP t3Qr#t'?J .k` N |t*X}1$e&*~ &OZ>Rix3|v`S`Jo9b+3lI,K= H?K/UWAZ)L&7,Ɓ [] ?CL|dIRE> 9udvc8؜66* %fA3;Ala`Ep1+oZ]v^5eIE)E+7ިRlR6Eo ' jG/x~9.'t4BxVRv,ItynES8)K¤Ϧ¸<$W% 6LT\Ξ#lWZ +, +4- kTJo,.n- ;ep|N4U^wM] X_俆Y = +^.f;)@% q=g"(Ey>N!Ol%vFvYf@Ȁz/.Ύ&v\{:d'P k9b2U1.TM 0jSⱂH->An.{PK=>Rz Val(3G@s7E2$qvK@N||Y3(ѧ&LEp 4Rr;}L L!ޯqM!Z|whX;HO ۜ?~ jDr1׋pT#}>vi'#H) 響!_8N81뤇ơWhF ] #6ݠZ$$)T̎]C3ۏ8esU[T U,Î3Y=YŐ;^S~Sp:w+t"%ŹY6~n˘$4t4sG"O+p-,Nt Y=Q%)ة#O +Fb- $Ͷ:gQ=i[: 9 +DDT.5r|^gp9!e ٲZ= Zp=`O=liюUr+m^NL$t%fɴ +8ރyk3Kuj=> fFW} +{Cs>RDKcXhM*VB%cM kAV`+tdzQ+E >(ΪKuuA^fjRxH`䡜!{51oJ2Fk!Q,{n S1R J0Et¼ z4fqC k?uUSYS,3*{:oF)Rp6~/m%hgf=~ { *$88yE Lil92M}jE8^vT n ;K aGqnK>xUQC18{ِ9 +х܊MM"xY{Ё-e!)nE?CO4q~[Ǵz0{&l+P|iXjyO >ؖ/H;"g2\I\4ZC +>p U`_{ܗ .,EDjbbƖ݈kBVwPI% RUC~>˒/_} Gp\hD-'+:ɾ@UZ + `٪lZs6W@ܥ'y'u 춤})Ou.$Sk`DJhBi +謪we)MJudM=.x-8;l5M;ݠk"Nzq.'l3=@Y)"Վ%2dw;U\m"k-xM*S~A~yd*P-&r5K($Ub7U!v)Ɖ@3 ߵy}9)yvxt}|*i +kvfgݶekG@g* rS%Gnr I$2 +AyJM,.ͮ< C}zQH<]k?y\)*RQEk ŕH<01q?ϏB7G:/x2OLHv>&=lΐ"E HIP̀M6'!el/,=N 8FVv/e6Đ2빇g(T\p0w,Swx=zM[\@{|I=g4^ =00ٽxnL١|o0xK~Ӑ U4t^tK+\MPKۥ +U +Frsp ws[RT.^߀ِ2 @מ;.:_3%Ǐ1L.]lUNen뛡K &@qsf.Ηc m >¹z^ٴ$:=佷0%g"AnpG5 Mt@:~l;%F&I93W2.ugDS>ڇ㹃}@2yhA F8NW(2B@C.%L8/-ha7O&I} sk`xPWY@T;``'ۥV0ғ}걪-A]7>V0k^ nEj@7[Ud=.Oʲ `5;݆H%YNl0k6$BnК.f|[EnX]~EHZe=KPQ[ ]J; c Ofd[j6ܱT2d}Dzdm" 1cTaIZ}fjMXLi^]jc1+s4:΋}BϻIpD<;:Bb4s%ט<$| N1"G\_&*r5ƻ#\y j=ƲTt%Q3rxowk!P-5c8 +n%Cə$x0܏$Hr$-GEߴ{i; oDf^?0-%EyR ?(ԭL%U O*6FId/qoƓ͏KC,ّ/]IkD_(BMmYS3,h,ٰ^2Q8K;E[[0: 7{wϢH4 o!d e}7 ]nߨ0T׹T=D2\v/ʢtB{%pQx̨6"2qz{WԾlV uD| dopt|aK*LLR:lwr6rCw6BclY͘GΈsN-Q2xN+ߧ|w*,ԃ%<-H$g*0@qPȺKt΁^aRd!ר!NR,1)2c|n8jY$E21=xS`x[nl5I{G#o:]5(o 4B3"Td_uQ8x kTlYIVH]\FÐ G0,>{}klF\ +?> +Olxh.@RdSNyw9]2m-X<?[A7H/PMqq_6؍W6L1$z2I7|?O0D SF`WzE̞̅8(ypФ{DLLS!{uo`+?I'sX췡Wd;"4P`r N6D)^E=xZW<BnM_, +X- +2-pZǺ^7栞6kL)Rb`o2,Ȗ҇ dž@ zaZ%pG(M Nq4vu>(p}{J,I7H#I/L.eԆ + +_l}d2y^Fhɥ.˞b|p$Z.]%Y44HUz%qLH &cPf<-JtTPWL =̪a,ZT Rzi.KLYW|)#˦PI+C=z͉϶eg*KU(j5TZc'%ؿ&NjaJPnj{Z +?tɉ~dK4i. *K˖ 3Z /D"˔;$qۥ%&iS7:^_u& +Z:Jn$B a^ Z5!meR1^юI1PjFԨ8Q ɣFTI&\XHV8fofEhdqQ:e;=d/j3`9 prd\X5PR(? :[& p2l< hlMF*UjYXZcUx`3b #8$**nž5/Ur4 q%{$ nX <1"5!4En;nǀ +y߄zǴcM!rBJ5E0m5D*EՃD3#z,:z ๱x6+MtK.$.D:=d rJx$['dجT^4BfhF4~[Ju4厂 HP%+yUk *֨{a%֤.bh%Ys+M^-TZ"F/FFT~Yd%t&9B785 j>vheӏH +Cgy㊴<^\GtoFMs%`cw?0UTЫLߤ8bR]v.MKY Ӣn@/hWzYsS3O t(2L{q3HR 08J94%h(dFPur +sũx^PgOAӴ xsCC0ܤ+ bޏe9aNRԵs~ ӱhaɕQ< ]ӟ3 :\ dzvO>[>Ӛ.RBf-1lXvo9>zJ9\q0;PaX 죉6Lq .iGWA/bdrn?V)FEӤ5Bԣf\y]2N#6cDoeg2]6^XuC-$e%9*B{|t0^U (4( ҭ%SZ6u`A; T3LBNl{j[y$Mϳw,w +)m$ {ʇ?GP*=_/~@1M^0s2iw,CD=.CTKL>Y9G` +yѡAJ+HA3DgrK&HЫaaz*Ùh +}dyD1*PB""G %lR$Q֖BizXl')_Ek fVXQړa F#X9 C/-;VeF*oqqPBiUM p`CAFBY\"P*NΓAW >fgQCcvT +M,VQ]#}j +&vGPsL!H%X%~MUff#& l<.j pA5(44Xw`aC{Tj4]ed|#;Gu,)x@B ߸r d + % +>nè樥=&S{LhcQm{XqaSo:~ ]x-mr*Kr-03djUd湐V h-KB*ȥ>jb;C$NNd"Qi,\lNz6(8JTgE *XfuhiZྦྷuq-\U"t`5._x>sJtԬjׄUݒXS["6g#I lF%= B Fs;Uk&!`JoOoW+⾸*Zy1󥟭g+&ilpv -=ekt'dCh_R +T@P!%6*ҳ݀P$Ie >T^Չ$Z+~Uf.m~dBwrj$dd㟉r%>T6aZL;д1;(kxE(Z0 + -n_@ /)}:0  `t׵UUh(ц/{%*n=C$#郣z^A"vxfMp<_rw " 8b{$ƤJ -aP[%K[$uMr|5wBPQ<*͑@<@| +,sL@ "}]̳}&J‚`5d 4*B 7M3ɡk. b* T`lw_@=SJUOhB .Ss"OO@ ŽhC WS@[h92u2K"%M柨s}9%a@0Xڬm&LGir +eʟʌlh +CJSOFADpwÕگtlgEyI&af<Yk; Î Ӆx1MGpnY-[:ه+7(O^MV9O}_}l~`w +;>y\P>*J%1kpFKdKC~afKtOYP2)Hpv8hWpp̱%\z3i< #߰_Cr{>Rz.d9>%;<%4g8 ',d~aR-ڛr +!(F4 1нG'rOpF.d<2;| Sj}HR+$ɷ$4e ՠC֯/$4(v8Z j/jɀHق*35]7\~}$S)9}5c.`]RͦE/gȷt2ERra`95Bc1l&4f"Di-]gb/*$]lB{nI5/ڡ~a'x58j,`V*YIHp B rmIIT ʿdy5#=_1 /XF0_1WH P$џe864 hn s +fⷑp'js]Jf7왮dh4*&YLSY9C'|ͮ" ңLnEd6.sڱPSSFH@[":-;$`hS\b-i,$aԾ +!r5Do68I~Ep s5pKX4DZ91NPZ +%(†JTKafPF]LlǮ(`*MS77_# 4`&Y]Ҕ$v@ˀ +R6d2{ݘf&18g q d>pJ[w$mbrO&C£^.xLI,H>QGalE5m8D(U4(ʀ]Ĕ@^K—TDCڞ*3Je!BE1`AKhK!i]eYK#9{01esӲ; ztQRcکpU7]ls@Yj֌ɔa)רY2F_lNCa\waќDG*ILee6X"_y~}MnC8U'8#ڄuBS +:mq9 UPgX)7jg:u/[oSPfbޯ41(\U`p3"VM(H2")(W(KD{ęIwuJvl&+ʊIF 0(>9GDsDzdJ8>[4wHdXEH4~]VеIG.˜T!$$\ԶD"8"\ˈ&}H*P/ZLN1 +%^o8SKK=.\Daug0rMƏFDCԖwI d+ϲYZ(I Dց.x/C׊gc3i=j*ݔ(a>ɿ~a`=U؍(=rz85h!!2JYC)L&rMi4%:O:\{KA] E mɾg ~ũD.IRJ~ۈ S7}5@ )iipuU[0o[\4KPj&8-Fxd}3S+O1KpZQb0;ݯ};9?_smF&)깇 I0K* ;jHfvôT6R^%ZúyΣs`\* S +K &L%ӍCaOTT)(jP} Gߞn5;_`(FͿTh=BНhKT 1Eݭ%W,LƑZzj۔"=,]e?ՀB7%$Zav ëZMRxEj}'Hp 's7k`TxZ4b+xf3@6;h + #C2ɔoArL>[s 94}]2iS~\ʝ %i7v6o %[4lqAr={r`eh80lB + 8|lhI%p0}4ۺ8'Hn %̪;WT13ԕ#-4O `[sPmn(Wfnt6B܁Q G BV؇øHу@ⵚꐲQT-*og +K07 PӅՎі& +BA2|@<ÄEIl*5W=bߑb$1/}ſCvGT11,[f]B;xCmk" nue&T;(zk oV3Ҍ{U;0]oJܑuC2kEu)U1:5*]#@n:[Sv^Qz(C0Zi˖0H|%z +MYp10(*ǖ&_q-M.g&3+ڙ/XUT;b3J^؊K**U=ViLǶ^vL3^}92}^RRT7ᐘMD4H~&=;sT0gMe4({P2I౒t Ek81"r "Wzū%NN}E~CF("+*uMW Fqڗ0A4 "`봜檮.K}DCm޲̗@"A_j&U~bcʀ; 6r(|#Iٽ"/%bn8+~ $O~'v.@PTaaOnl~ҎI%ԋԽT0ua~!ĸp,*)0Qԡ",՝윤{RdCbLUIj"jQ0G)#8UqTCt'NT"%K+2#rdJD ivYG۞PzǛ^+(?= hR @  5`LOK%( {#t-d%g'GKIsJE0'L#Aͅ>zLK&K*]5P/x~%P E *U=NCt7RN\C ˮf$BMMVS%>m$9''?:eȮ|)"CGuJVdoi,Ɉ8+yY(eҨOmW{ONа1%ȾWjcɚ|BRlYJ: 4riTe}Q$`KfSr J8}zII"]GC.D9(w9fvzH ^l' ^ƴn8yS t˅MS@( 0Ճ־`DB`egGW@6n!Fv-Vs># 1 lc/@$YZr ;;pln`ļagc8nYySh{=/z4';NܭiE,I.^n%GT \i] ENyß%ӓX҆q%¢fE(s*>ZMQ~@C5Xl顝^= ,ZLJme@.YMhF);*@-AWg ,) +f< ?+,J?CD>>0h+33ⴊņ7!].A(`kwE8EXFs |{mQ +[0^wTww"9%KYJ4}kxT_w){ApD+lPEÞ܄+Cr An-Oң2,銸2sR8ϗdDms6V\앦{, x1RB~HƉ+6yP +Sj^KoUUVYPo0Mʰ{x +ɩ̋@lBܝ| JSoxrdx,cPಬ$ ̀w]CIeKuSP&JD?\Dʻ\J˯= ]ۮD0Ѻm)lkR\á#I2)U0^ҐWV9$'sFsw@`]Tr*_EGy'jNr*) +Yf!9!w(CnA%CiQ@&+ 0b==FUpcORh ,MI8KAy9,1)kT3^} ;N Wʖg/KUQS ++eȶzIpT( {)uTI!C% +%u| fj@A""RC+NPUQqoӻw/H[܉w4Pgs#O/yk)/,M(O139zOiBJ` +)Y =&wc%&3pMndZ@|(ljP1\S1\{JKYe(#Fi x%x'A`YHҝc\H2PQQxHp.)ۖ4[D *#Id'-9"}A5/,n쇪XJ>Dpqvͨb&#JȌ fv3=P)} VLDڃ\f3:KD_6saY@0#vAY=RR_4eǖ5*jVBoqh*KJ=LWV&Q-*uaK\>ÁUб]:ױdH~<J>X G$'NE%$'XEZ&?QJ]J]M/IAeQmhG O&s(41:S鰩j7H{ +x ݉h*gӑã dv,dAHTRCT&Z2SUnWխ y}4cGf7=8U {^ѯr7:8VYZv/%uߑo澓|$Az%VKDk -=_*9h-F*utȄPKpWuhEOB>f0#,VթХ%l[v +&P!<`6ju$5|{[ LE0P3JyjRRcpK%ɉ-0E1ЅIi R ȣ+ndADaxO5 W[* $(T<4|aozTc0BI#hnɨaT@' aLBO68gc,+&UV`h\&qX9SOAw._C U &R0q4p*(%[>O-zڴ DSf[a(Qq͂X"Ϛ3Xr qywUFi_k [eɖiTLe%%8=6P+@,SVSAWV1t6E e 1mdiu CKTQKQ 蚎r}󊮒@w_{ˑ1-KR:lwAhNVu6R8L0꣡(EgHHpx(fTtqߵC] hwrt'?Uf`U3V*sD;YX]X4c'XjhCԀTR {sO ]X> g#Z@Kbd0=kRh! _,Ð +>J37&2ް jmI)Ā~&6şa[ +y3o=lkA^=q  ]Auou1#%Ϊ^=1I"`];aA=!#;U7͜tآ*ug^{<28jozzB!F@ذ٨΂jɑ%J{x$04iğj(|3gMsHiŖvqi7ߓ +fGʛ%,R+Glڦyv ہ~OGI*L*}`r$}VT')i_;r$hA U )j\w 0xՄm\ TΎY\Mwh8gK= a%B~w*p,)=ј8IA~-Ss."h_1K٥6_ɜ ]3ffLvXFha*t,+*bY$AnĦ朙=ٱ^n$Rdk3+W Ɩ|2+dmtJ ^($DᬼaTD1GHJыȇzgރO=H#E`6%sOS^BѡTr{ JDu6t`ޔSSf"z2(\ `i&1mHLDsQ.r.&^fNLX =b$s& ':!W).#3 \5$L( `*1ឲxzC/(PX8C84t[AL\݀x[dcw,?[Jgw|%SlCX,>r(\Ɋ8)W=RM߹?Ŧx99DkSfU;T`]GL S^+WIuъRp#TF`/#En,> Mh];b٬#m-mh!AzRxwp" +TD)g>CSPˡGqpB"EehwaѲ̑@֠hFܬf%abNBQ`~? lA4Z'+9ͺ}d..Q#[&;d1(ԣ$鸸 c +1wa%(*tPPjn0U64fE(G,zWBROb Q K֔ +E]|W鱓fe]( pUǖl֎㮋J\L\4T5vLetCJԶK$Q :TQm B }ϼ{5W#;{EN3ĩG|Ĝoa"…u}cQI%ЌC=^ K7v~ծWq֐}7f7 5Wa-AMdF}¯ضlت,!lf:TC攫-yv9#73o~cՍ݈.K5g)H>,$$x {,Iz_~d2Dpv=FrK&>JV&w6LXY:qZW΢mG.և,>:ReŴA*ӣJ Q!U=Ywm EaiGX4Ɓd7"Xљt6'yɣސ9'bm#M˝@pA+i ,@S`3yؿ8@NA$;!$]2ۑUGwS{1H<|0 ki8 +|}X̻nH#XȎJgPs%y ҃}1=n#z0q^$d]%'K*#&6Bv-clu*ȧz+w $p?{Kf3*Bp*eUR^.Keҟ{wOX^,IЦYl +9ҹU\i︫k/w2€'*1.BْR3:0f82¦gXz"|R0E/Ð3{aӿO|1Nrˤ 8H\^0[KY!ME <0å,7-YYfq*(0L0 $*Ec\bj $D0tLx tuZklwQ|xnτusM(q^gmF>ם_@W/T<%Y9Ɣfq~xz&Qpz̈́@HLCP2@bm"9zҝ\19?!nyz+Zf:خ 3<'!0/'Ԏkq?8Zr1`xiDfeȉpU#@˱NJ$OFE1GcYVE}bhHΖ Zm+ 7/~/___o?߿o}W#rogr׿?w対Ew?k?{?ÿ1~|w{?pR޷x_J'~4?{w>Uo\ z? x8~cӎt7o/z? c_>x_o_no}}GePv#Ϳ?o^/Ͽ;%H64%x@5 B/ )Z{:V#,'A5y:Dvs\ _,v~@ n+Ԭ_9`q5]5NAjJ֮5koA줳\>P_bޝܓWUc:dmC B5wL$4hA^x;%7kgßO޸GXFJf?|Gh81J\*ּ7~|O2 _>mj1Naڲ5S(>7$Box[핶9ۇ8!E{U{Z\5rv?AD| +/O#?>9z*?{R +1?CTc\tt9j˧(35-zwm=AOC4W|r3+.ɈL;BƏ/ao/V MJ{uFҰ}5Ir <d*GOўSufl~gƞE#nr(Iǚ/`'ARҾVH/ߝ\v5S0Oq;5ė~ xh6߯Dl_g6_n~7̙rf +^OnPگ33Qj6Ca3gg _7k"yOsC0<4Hfa |1Z+ϸ>l)&}_(&}"u]{+eyT~{(:0ⷐ߷k4)HǸri +X_u^|R xܸ4q_#9=e]^^}urEA@S|:W|:}LV6sF;ȹuu{[wS@熚+(/ޙϏqњkD*xnq8[m$_5tKYCP@7?r,GϮ@]_x//ýia='6}i 8os#í W>Otk\^:tb\F/ey3UcyJ)~_t~km,CܡsU>UIÿ_ $X}[cE%Gϑ\Y 3@5r -N{OrvFhgQ˴cjկ {^z:zKV8>L*z)g?w(#9Z9 L}D4I]F2FlAF2s 4ֽWqWu~`0IjKev1V}i,xG[4wtg|U޹͘v{{yWJ-{W|T ɀv_XgKTpæ Tw}eQdpIYxݣG0}vAPݣHp2bng $g>F{*/wi`}e]gJVϛ%a}܋=si ܫNTh/Q@[(B>ns +WĊݶ"E>iּ]b腋 .:0O ?vOsHz1,390 +#VfVj'˃})"`f'E[?Dv#mjV}99gzwj $jHoدHoJ0:ޛkx"UOzؠWu6⨵ӹf>O{Bc#Ko^{^%[R D*)Ns4C9Hy4/R xWE^>W& Z>:lj`x4Ne-!ļ/O>MOvF%K)zE<^=|$z4"WRg)pRݪ/N#%{V +DZίAEyJNe+`A5oFd(Q~Ț{ ͈tdr-)pNm[鴏Tڣ1ޥuopF(gO{qE5|xxX!;k6~uҙ#kR|ӻg8 xt1vUa4ޤ1I%VK~ƌtxŬxyV=pƙJTFZSgύ +h æ3_8}auΞcw?⼹00*>E{qn1~-Q):h@z +hn=oKGbܨjn/ =ۊܣ/:k8|hs$ m|ig֜m*F5MǶ覭9Eb|FHb~D| +D)c;QN=̶lMrJE=X $EshxXpaڴhWy[DQ^no'8*;esmF]crFx3bG dzoq{uPZ]*ß#5X4iݘo"/x+Ң7vcw ̓xmg[{U|vd۰IHǤx y?Fs)X,)xJ +yb=b M}MDѽ=RNVjikP4k:5sYш?7FlZ猠 T=[2?ᇴ"tVLK Zc/xD;IQ:clO$azqBdmFq0B$odWXٖu` + U: xO6͌Hk#/˧z0Fǟ'XcP"ChD :xGo`ܰawr皝j1=n5jL=^uE L5b=Ztp<@A&r)_Nw4JB)k'o"^%cF%q e7 ~"d./rM1:6HTg?*Qdcà#݅ 'ol}P߸GdU*5 9XYfoa4y^aۮ#r+7HÇ=qq"zȝ5c;sQO1] }KycE*K&AŭN_I;I9#[SI웯 4a4%~8e"k>QԆLazk=nL'Jj >"9hqi͓ hW8p4R[~'YN{iU(Qv )ŨI4.[Oa\1673Rk0iLvYIF3~-i +"Qi6j|$w6ou8aR{:zd|xhF-gz~b)qRS{7ͣ:p.tʺ +j@,uZ&.vjPНHі}΃m:׳Je_ZeCKuD2z|È4?F{(FQc\}๬r=5"*:3F3ae ޷Zհa|wjXg6WQˡiDj7M0erJq;РUJ[j2if;,DS+}kU1z߈7I)[<(Ya|YA&q aSF` y {2'tCxM%ok9Co'}[+q֑Zmr*48˾W>r@%h)9±{\1J9ߓ5"#ӆsUj1-Sa~Z1%PZϤ%t7YhǷDz[K'ǵ9j7F6uLl>#~ 86G`5hZg cY,oEqޠLH0 p96t j{UaAma/tbU +Q|z=PSH:XR~aÜA+7^'6E;92O0446bL:ؙ@x.Щ/k1#|Ia.'2R<;>S:lo?ER`_5ޙ&(k}Ƃ%z6=@P&QuK칪b&SO^Qn?r'v>B9fI|^g߻B~}PmDӧ9"8cuLCu~ucҙJ"{rWevA=Je}Tz>=lcϦ Rn.g +ra}<ï+(|߀7$vR%ln3#4)ᾘㅺUjqDQuxuƽ"$eh0t +uO |9ޘyȦQGc@~~.^Ƨ2.mC=Qg7i{i"D֞WPǩP"+BvvCYWppP i ,0f5`>ՠMrۜ7n5._]ɘr%Ӧd`[ ց<:Coa8ApX|ۼeynw8ވr3H D^h|e>: +Yzxsn:ȇ|i 5#zPo,-^ js_Ѭq j ђMeQH|U-vl> +g^xk$~cy@Z@zAܱzXxSHn%@)-\o`y/G?x{?Ƃϲ( _s|~qѹF>SvCh^)sf4J@'⓸cc5=/ٚ)m }}φg._r)<r@ #;IdFiE@"x\BKNgDhcx"4@)gi@(P`RG୳ŊVAG!78%D9Whg;VٴX5ܱ0#) ޹OF^!(ބrOTTM+V d"a#Gw3}|8J.Ee>?j(~P;Jd5cT9sշOvdqγЅ@%DCqN#NB =>IE[cYkYQ9nʝwn9~I2 n19x['qe`aH(J=.ADL7~wKGEtb^A~UW*2rKl`ըVv8zv= bI.>}?{xׇxrGTk-pʑWZ>qUǹ3ʉ"Dxq2zn,{M6~+&tF(R׈%/\%Tl(jVj +o̳|eyUkl/ +#P|N'B6wc[G2#XH-ýx7*_jɺ_19)w耍YW!n iGl7ɶ~殘6.}!k3!F[G6H~L%A.ֹm\(sdٯ:PHm:|<#wNloqϽc#tEQ>Yb&zcݭ{sW~D9#RB8@ I)tp3m|` Yi%g@^VߗAyo\0T6¸Uz`E22=H J1 +g't`&EÎGHrpFyPdȞBha?Cd gWO>gpa]վ~u‘?ul{\v$>KiZ羏CRs.`Nr_TfQ?*Ǔ8 I-g_Vߥ>6¼>5()76A43 JLg~@{C6Ed8(LtY7ZoRw!Xjj-|ye6;BV&1c? [f;x؀5 y⻂ h2s>>0մN{猾vHNO822َ=ƊHiu5E"?3@D.Ok [g 8M=pXp`)_?X/_c *zJ{+FFOtQR&cNeyZzB:g\[kRd' nD=2'K?g<u'jhM^] B\" g0Bl0fH6G(gai^3fznA# q;,RU#t–%gM_yϋbZ +osj28Nx|xz#ɔGtݳ1oJс@.y.0dт[9n.i) xh{P7N&loיjuuTHP=Mz}$J"EDzpIx}L;FfH9gNBDx7t@gBe/j˧ƛ+ LيDY}\$;*~N&Ji ^JtFGSˢ='FSNQ|KԸJQЖ":v&g֋G *ʳ$[.3:\+sI@r+푭ʝwfR-k˨SGsH4ۈ ^d_tBF)r/SyetǦ5 # +*M:g'w i̽#k iFܗ)o #[GoSJ8מt.5ee JIҬV*3eQѕMetTև/҇b*M >X0r.ײ\o)[Q=췝ZB~DbTBYɾ`ز\awd֋nj{f-M9WT #, +`YSx)v_VLgdr=p*BPNr y*37eSR*4W\!,sM-C~S+uPHt.NY}MX"IT.e.(f̎ GS +%RYeiV뗎*G,B͍!~PFRe}F*:.ˣ7#=\LE|2m|,u-R /( +T*CY6 ¼ljEi#q#:O5l/j +D!LG]J~=RBݬ1PBWB; ,.K +}˧s2 l{"9uGQQK:v/2*8)K@@@i'*x@Q@EJSKzeٓ7󲷓Q/A:Hh& +;0tgi +-(hi;VNߟe :i"'R|x#F-ro%<F)}gW'4adkgLdudeT:(f'eW9w=$JE515x}Z? = k; +tc@+(y c| (=M +dzثK`e.-`ЏAt1-$"z QPC> ؜AMT2Ui ,KsrYH)cTEt. c"T!2A*(M{NaGwBNZTKhi6=|NU=@+ӵ𲅘29}f8*Flc-0)ӍQ'#>E% *f!VGfn^)#qܗXr1I5[Lo} Y +}9ÓG=à< fǒ2XBb'W>-L]@PZF=rKFʙ-tK-K\ˑ$Rfi)bɠb*;fОMbcЅJQ1V r5JbdK[~9/(΅S2>pȧ I&u>ɠ`d,1uhQBU2,ЙG2[J+tDBSWZe7Uڥ"rr櫅22l#eL1 zΨ:|.QF5̱La|t%T}FaU~e?Ѣ]O() +0JJrJ3>+w]Qw^?1O);V{U";̠ NJ| +]@G4SRhPD?d2W&TN9 +sTK ȕ5ґm#< +JET8FOeWpm⠀nCUL#uܕY\c18@Q{uDGURB!ԭ4t&L +=0 +v:>7~,q{ fTBh;,C^:*^aAP?8\t-5(#f*?/g-HMf +|Hb#KL#]=1_.SH4S?Fk,j|f"GPàk w|k 0zNXPy3:G xEtfZR?=n*H{uN2tE)>ρdXPJ +S$v@@ +tߍdT?ިhhq5]OS Ԛ\yC%Kݕ ²7.Nrurt"χ U3kkz*MP-q6Y:'SP/ذNdI+ ?/.ء3Y4tW~17{:({ChEr^PBVe`oxEtUxY>ꐽ*S1WqASFW(k2*oJd91JAc/T]쎡"*| w^y}?|Nz9rtU UWJ\e) +|$zTxfTb`~a_}ӣ(l;S@'2 ]K r"j/SJ⯽ޞޤ0hDzըp3Pg5~F!]90w9jh)CwUQ.](ݕ 3s _GO,(U+̀ܗ3'0ul{R =9D sWd/6RJU,,+cRO!+DOa!G7 +*UɁzЅj<>П>dPVFe g&  je{+JsjQJsCv(d"dRl_^6y24|hR<<ӭlcI!U 4@Y?*.)ؗ(K2飯\yПı}+ύ "j^(ƽhLf U`(W(wHۿrZ/3y,lޭ/{'krYTDJvZ@~D(k܋ܞ ((Ȫ":X>^Tܱ.wA,jϥ1+t;}eBZ=:̈́N٢߃`P^=Jh:}%@]UV7{B],$کcײl^Y`d^( +)z7{r~>j+{WW?rzBuT+n0zvAw{~TT`!ug'.{{yu]WdYLagU^iY3 S^z>hDd "@gC7!}F}yJaEB-,@=Ҕ|(OєP?d'*RW·CC"Fx(!7ʃ']22 iݣ%Hl13Ο,XIq{EYyT#5\cT?rIVw1o7$tl!3b4LIk7Vu7bx}$'5xuN?}O~脮_]fs3K=_E. zv"tc?NWȔsfbdr˲U'6 [ 1 H+`JrYT*q'h=QIoxK$S?/-^ADzOY˽o6=-PIii>EiFDϳ]#Xe +"4rgV|2jtk&"mFN3!E寍?ӥ!ʶ(.󔒲!و QJX!ϲot{&GXptAُc_߉;"_ZEhyw"elIUz{ &$} sfB.`.`*>@_;n%sU-q̆:.+ASuOE_Avy@Z 35b6?:lOk6%7EQk0k$T"uH1F2KVFYG6'xgEYea0NQGga>lVƔo /,~`̜3Oq^ mnN7^ +Ϭ:,2u(6&ʘUּ9yxu͓?E&7rty,>+.¥-i~yxRlJ\5Ew,k[5„W+9kcOZ73FDE+N:u 6իyYw^b|Zr0k ik❚k̓Ui?p11凧Dii-fU1IAM=fds]S6mHgO#n%] +| 3fUEyi֔|tVr Vy]Aλr8;^21X/XGW_rmz94(6gx6&'0G O5ʘS^U4 #V9s̯$Mjt-S 嬄GOf'qf^jwb.qK%i Yx#VE+;.%71pE֞p+ٸSߘ]b٤GڱSK͙sSMV5w,oߴH]6I>©.%'11:5ni{ү7i%̦W+MԜJ;cŮYBkc슩K_v CҲ5}/'q'̥ߠ|.%Ygq ʯnE޼d|39q)3?\gAFzI.oON7gM Oۻ5o$:)a>[,ʭm%4i"fU3M=i+YHVa/vmʎì3 kS:!c":pVI]|{tI.iKҫStܥ fY*Z1Ѯ/7|ƦLՇM\odOs΄/_cvҥn}s.߯7߲N +̥bADjͿ^AM:6VUt\ߤ}qmᖯOw0wT̝۰w5 +} ?ļ_\*nظa-콙)0Lk%&=֊S~i_^)IKU܄]TMu[Qsj$-9g%c ~:VQhs&@#;u1ޙW#!Z%un%N)FL䝫yGVG'- qt?y=I Ҵ7lֿ~\ە'5ț@?q+#\)vqC)-Smˏ?Zu7# ߵ}w(BYpN'XVu^^?f~Uunŗ^6?/Mw#*`' Wq R>N6AMŮYݤ +ͪ;7Gج^4nRs=kkLٰd1c$ڳڏ|_ުMU-_{uSN:!iԙi^Ԩp&i$+156ykکRv=\yjk6/6&'}3smR^p7e0qwe1 1q!rGrܯ保d{d8`RԂjwp3m0{𦹾pr5]J搶>a#f%lѭ7j~5W%}Q;4+iXq5nS'zW +q酁{7Rpp/΄_Šg͜3+<^ШJʋ;E1%︙gpП! օEļz-ؼYNE!h9y!h}D{sb|agp)O:IpHH^ppAkQ7+[➞zW#Z8kV.VA]e$=:E.mZ9sfkTr!6%*p&:˫̣W,>fn͛CxVD&D[cYFǣ3Kq.%g_q-7]0"^}ݔpj8f3)v`ΉbYWnX$Sڍ1q%Hǧk?|YMIk>iYRxSY{5\=*%6-%m:)+LNgT;ޫ)X^=g9qpsw&bԃYd IVFzh-'ZnUlYdю;/t@Vb Q)6  0;_-I.sЪwE AtgAgYhsjywIp)3ܳiRTJA8=>I[+SEB76ҡ\jbcKܜ{=+ {ǐh sؿ[s;vL7 (w'9/lXa1*0m^Oot;s*iҧӒNEP1o%sңzo*r =lpky/irtmkl=+CmH;"Z.iܘXn -:^,to)zƸRتfcߩ%vwL:T*2 6>WvϗȏM)w= #bo'Cs/8 >i+R}<}W|Jnj Ά'>_q5=&zM璀cOSD? O';?e-+Ӓ##;wnXɮOSݟzRqܬcbF--KmKZ_מ2.=:?sOM茤{8iֱ-tFG,A%uɠʺqT}s$v˯~$ɀamKQ;_ߵwj~fk, +M5oOeKD +H>s ށ[ + +r/삋q *d@-ApFDyݢwg^-O/ Y/"e7Hzok/Rk&90 5Rqv_K.YEU>>s&Tm9'1^ZW-Lozo};K伌RN̫U1p"W[aÂ=Ar4jt5+q62<aڸc.aCt:0+-Jǽ?Lי6E_mW_>='lfů +4߽~^cv=g߷E?VЄщGM-ޫO <]|a8 +PPqtuo>=uhmxDǁ/m$;~If.4=1>M,?o-tW笓8 sbhu|YU w; +9qq%;d^%˂H}Ԝixtu'wrϓ^*7$Ҏs.fIG)i0u$<▴[tVsEmW Ҡ'I5p-NI"`6fm+[)c΄~E`wAgLr^$`ԜVqbv<8|KYh~x]>qK<2O%`}Ssk1($p(q L p$abүA > :97 ZH#|=!,F\Wv^'Gօ߼}\㔶6I=\}7.aoA1?YIMe+[ n+BS#ݟn1ŭK 1fN8n6}֒u>n_UOiYO#dQv&lziaғ.0dᕇmjμ&{"A.p'؞҈y|%&}ponZ;8 WA,A)Q5Dy@}{]{Z׆fpҋƏBKA'TˀO0s u4IJYaӉ1[ݘ0[NnN X%ܰ*^=gY5|203']^ە\rQ&tzM^"A: TF-Q[ˁu6_NM"99?J%հ¤ԋ|7|yɀF9-8SsJV0p-ke# [_:׀Dz >J'H& |Qb&k!FLבC{Vﺕ,$5£%᝵a3 G I女 KIN. ŝ>54;U2U.0Tq]K32w#5r-?U^ژQ&|GpN_} ޏ[# C"`YlnRc>0}=>:::6?E\ңlEd@hۘд*Ai?t)șGR>uxfVu'׷'9gV6|86ůfE11Xf}{r xaʦBE|9n6&`DHo焯&'GڕG5n +qQΐ +Ą_L9q{rN%]xdx'W n( xiw4 `T1a# נKs^ġFu3p ui ? $產pۨ +LFx5T@%EXo4:dcUl7%Ysm2f%5Yn3{; 1Ъp q.ze0+kB}|mZ]i{V :(fե`SoҁG@ ecBT{BheL ^ Yxar,4ժ,ʷc3zpQGx4Rt*dV`tsJH *ҭS]nɈ0$dx\|)bѿ}"-\$U + +dNNO^~tcGcq`S +{Jңnw3ǝ+ϻV^ +N/}YTE&YN仓w%lտ]߱Yqsx˿Y}TIdx>^MygKhV~[W^yH$C@ dKWEm +̢ag Bv0[alu`+ W#{ϓց~tBÍ:46Zd&i{ 4d6Yae̥Ղ}2}cUl7oO¾}C=o{wi+uv[ur3fSuMhO85M+Ӯ :sc{d&6kGgL5:hLw $>#z5hӬ.<|9!=![~?p0NaAYit, ze[P v}7yw«b($w>|!}zӈW!xuΜq`6g㳪̮&|_%!A86>z}hsLQvz­$FC 8ܪERf׼䗿z>mQv=ٍ*"0x717 mkZxaAWL{%&:#0 $z}*-TVz! swy b'ZU\u,TI3 m?g ?Bƿp˻q,`0=Wpз@m+c8Okz5 ^6y(2bWǜrdP]!{GgXq6 kmFPO[هc3z#f SjTĀDFcV k{JT>?b{Bt>ͳO_UE,IȌqkR^w$4&W#a^D8 D&WM8IMAn60' EP2&|*q#gmY~&YqΥ켾2F."| )OO?دRAS_2`DF.v%T +y>1oT]C/9 +7!AÜՉcӂ{j8# +5$؛6f_yyKߗwJkH SN 7v|GҶ87`pQ6&[]BgoqYCq|Zܴ7͂B ߬쌎_p_o?o<: TcVYG|j'># [yX+ WN!i6D*796bήq{I!nHztdO'5Dͪ헲qMgdBLxդ_KcN +.nxZFɶO GBq5j^$bؒ[c.aΦ}Bsv +87RtNIKtVTC<=F[O2μΰ1I7wMabVl[#V@":;T .' +^![" +vӨلGDxe {o 2h苻dmSHnD)I,`\|_:'>l5DH ~YEDPo}̃֠owg,hJ9ȭt)Ec(xq0 x*95$畲xyt'酁R[0>p- I4=†OY0oI#vq-Cߥ|#@EՉOvIYT3(2eLDq[g[o7_|&\J6jmǰe\^ښ>`@qZݚYX5&bNjssz%

    ,nnMk.$āӠ4-gbA=6qZ; g:LЕ`L&ށOگ7%0ni౎JZOn#l:R[-ȸQkۡnL5_ڜeWJV9|':6³YUv}Hԥl851=J?@}n%00x_%E՘KNv4r^ksn|28ߚŃl8~rZw_CZDv?h(^8hޯpےApfx14x='s>"a~IGǮx酾MbNY+"wA>K썡ԜFUN]  ~£!9LB?yqwt#jC6橍`~51hA.E-KEJ{݃_xM}_?(ޚ֠tqp}AsԀMɚGyc׾ +MIuKf^6<:pkwAb6uWlᄻw7f֙TΩO~BZt_!\ +V%Zl:ҠO$y,9C]r%w՟{b3*nE |ӦڴF>ݴ*!ȖYڐ%m}W"ޅҕgnM]Zڄqc\QC؝뽽v'<=6-k!q)y[fqpO!G·΢Z +1wFu +k1 s+T? +/>|Rպ =;-׿گ?;ӿ +g-SJ?((0/齆z$H_K̜qJ^"~nUͽƃxŇ]M/ +"%qZD`YVGv6eW1fl +\(<,H{ACnPJ;|2UavHe*v:w;^I{NAQbvAm=cl!Q5Ty¯`{ kv]C_.%ws{zc`k%[ ŨXvf|!^A}tD{{mSin4&} eЦ_A'=_n2ڄ5GDGvA}u^ 9Z^?n/Wqc©V=H+X1NeU.%k&p֜IQQҭ`&)AM>v Q~DeP}Àk4gHz,][d {e'x%25R+,镱>hgx-x=t3`2һskwt;5bdcFxq?aWo_"8R1AILI]qO@7p+1]{A{nāâ^2l[/m!o0@j t.TPE+r~m|w0 `{!sCn%ӵwA-̅Z{ =i1ďjert-䖶|=**G1Im*ڝ((Pv s6Cщ͢]޾1#gWTn +g& GcO;۠CfQ[q r/W^V42cM?/[P^v9C&( +/ 39a +/Ij0+tlZޘ\^Lv¿.%ͮ;dmǠ#3ѸEPsWo2bmipN9z8S#Luj`ٰI:p]c4,l +;nvIzdIY, lM;wFzwgYC2?{.0$\0]'EA;$ݼI& :;.L;K}>ߤ@' ?_Ҟ6Ҧ͑ҋ}:%tnI{*kz|f`Qh'|r&5]qNu~1\{ +8\7EܧA $_a1=w3ոW%gu%\v 6ph: aU"GC;KW'Ro_ޙS+wKTs + s @gm̪V +t +`85Wp>Q8y+O<^,L*"ao4}|{tp)!:?^m:!oLj3Zf̮NHfѭɶ_=#l:;)ϸMRa~g~N؅ջAS"(VE<>+lk_RªVا:fT':fg }<}ޱϋ2q:(JEYqɄG8q&I8cܣdnA͖`tw>a6rjʈwps+w} +V)k!zRҩ6Jo"]Lnᵰ GJd4[݇vtPK2g;LH{%qq6&׀y!zR Z=5'<˄[Xg%!|zrNMy?,aaYѲ<"< \=듒OK>%xWxWwyaKYe +pbp Tt&'1 a]HڤM(49{뽁)SjVxVxqE[򓎢W;ƙṛ%zu&:P(V_*z84>U<^ͪnCc(g}ˢ/* sO6QM:2-Y;^J>~V_۝5?| ovizێVuĦxg1ˎO;CYΙ_9?LxU6_$,6¿<2Z=V:HXx1j}{lnW^tRQoԾ`',xls::?w4>nO[ +2lL*֞ y̲[>Y}Zf [9bvׁ>J8U('ftRt6:nWt.dˣӂNfWuf ͤڜVŬ*o OrOm"߃_iWg샛K3Є\bVG܅fYg3֧iI]oI$*^lFTħg7<7.<_ -E3+_=g~JzݱqfU?1G# &T̸C+ؚ9P;9wYwڭd8, sg9g_v}JX0^+KXOl/E7`n>]poiek$dL&11&11{o"K"b{H͙s}Cem콿u0>D/B_ \yFD}_XfH)Ib:\A"GD]6Wl\杩̃j©1J+MRr*-2xE +=ڔߛEZ~*ŀH/m†½L(hEy!Gmm5AP{0AN=7jjEpSe7z=N@)™n +{}p~˥,r$Ԧ$MsUb"t#h:_2ջ2b򩜔 +9DR4v50J?؄ƴ'!s6If ̗3/T Hr.TUE$nm9jZpg*bʯ**ywJlp?i%aoKTMɶGf1ӤIB0*,&g\̲Z4>48A>x_/o mJbU0O]Gfـ=-:_ucBJ0O8"XsE8)>b殷Wa>]j(Wg_*dŸճU<l73\H˃l1 +b(:S +˾_߻ +my/[d+t +nt2(O`öKn4_ySۭy/Ijl; ZA'h +ܙB],*J1Wv`SΖpeDNN#7U?P}0F6!\$ ".4_oW܎=G|1N`mRTĥ?TAj?*B'˿˼6ۊ.  V0w_z_{_:˽Cr/(&2IB苴""$&qGå 촇PO0<$qGLnOPͶ` L?E9e<6ꇊ=ϧRdnW+"P76Olӄb$&)I[egRJ&^p~ iXV6)1DO:_(9J[z0L#t#?mAKܭ"l 98D!zX\}?sVirmAV{֢Nm'a? e +{:c56 ذ\ʩYbzeJTA()vYit"n79Fq}.Oky99YvMзGC蠭\ y6γ[<t*ǛE.8QLౘ2גdɢy +(B4N ,v[f?[o*p,nh*q'PvK˭ظ bm?,DB-[dLflV;,'4)&~l(4?ۘ|ITu,n&h751_@?[DZ nU~4P[q^jޘ0Ze  XQ?VWZYeBLǵdRB)r eF^.5$ڏ~@%>¥v!tؐfͦbf;9+$>n;&ȲM3Mz nҐl&as~[Ut"DLM?}7^t8K|͖׶i.bV5i04Gui{}2NJ#<䠿"J}zFGCP6 /FsId`y7%x*C,ra +@&%WR|O2II50׍v!5Pצ9JO,Rb'/;CSҜXl'd*z>ˀŘ諾ڶwGe W9z< +?,@mGQ2omg aeQrʍ3EJU<נ?9[p_,yJ[kA}4$&b649(&:=v/6pZDFHNcdW_>bhӞ6濒NξT0IrUBN>,\m{?eg>TW=G!ed9Ne?s8W9*0ͧBt"Jڹ \n{=M}w4.%h XYs=b}LgZ`aN parOmGbRS6s-8pYUxwڶ޼wNlS$:7 SbTsU kr^4o|0-z/g%W$ݖPSrsIH1wQwyIN / P] 5jA 34)󢟐 $HY帽^`zXvlb &xw",-P?sk;_}(vж%W01Sо.gmW ޚ<*U$nu < cX>0z4Ru8uv]KrI{I"Cz^6Ȼ +^S72()*R?AYk; gK7Z2^Jxq(9 `0s ע0~191ƨa8ٟ+)Aa?{^;ֺ ƽ {t4UkP V91hZ+RqK/0-|^_g(!jmA~ +};:в|֜ pzeԓnGŕf*9onan/گ6D~$\i|6JH~,"F9JzZbb\c2fS`o.6D5>rRPvlb'<:.:/p7Mb/`ss)!U{L6kmK%, n*wM%L5þ`^}%n[]// CeKy.m^批ߙtSaU~7r,Rfa9Å <: +߬XI_My|">my7k.kM7[:0>cBפdStE ] ]ΊSBm}&Ytۃ]A~9B\ n^U lQD8EO`CS&&ofLv<>tZv'h߇_\%'NS4Ȼ5qٯĤKUO IG}_MR\0*%eYlɵ R\3r &FRPEr+WQXYo,F|l/z +~[iYL'%atF{+mgDE}a );ٱp*i_-弘'>h, +}mȥrZDS9-& o3n:pk3&%(l#6 bCu hn VgG3LU-js8(=E #VZo6G:Ͱmpj]곍!י +T%Pj)i)AJ oOg4DAfx؃1LQrϲ ug%5pI ;c\m )'ﵼ +DXI;A@?嶫 jKvlIyd 99')^a$R5K5q**NW|4q ا\_le&<]tXowSb^. { +UVі)t1 V1.$*]h~݁ +ۍߟ$TC|s7M3NL4b}%i%u]FZy raafA?l_x,lF'g9r@GRSDL{}m` .lgUќL}"&采ж|tTn7%栯"wc~w6Rc!]i6r<X)zV;e&DJϴP_aゲvBRRuK ?rM!g"k^xDcBp)RI(ӎx9yv:ޚgKœ1jZ[^7xy< 1`BJΪbK\FM9ȼ'2|M:Ş+Hi 5k ~I9dM}1B-5?p+/4T +An띣~t%.<|6K2K`ioVot_Xp ~b>1#-4:m8o RIz t*H86"AD gz廮6=.t4jD?VYg"dJKnonom!jO=b]ۇ :E&g@wBW1;aMA+0hy6%l>aw]ş滊"&"VқG}Яcea6 (腧J`eeo F|5H\م@t|mpw8@?J͔;$%>OSҞhj{@M+?V9Etrj쫪:{E½͒*6b%0>P/_c@^NHx2ĝ(廣nf 3[3c#DA'2PăNJ>-wubvVg^;y5,R@,zޔTō/f>,p;.?&gc3 QYZbsъ2^۵o,tR䫈&9 q +E•Rincqeܣw{`?py;J^l/~`ÏEqriV^lؠ_Ky̝YBICX")&~՗\,ˎ)nekr_LQJ/+c}naK]e]V!Lq5rU~Ac2Dͧ(iG|m.`orl‰?Pw4׏c,,anH~VQ"Rܩ ?WeShb}um̪ }Zn}~^iU:q2 +8E=]mth/?N;瘧 ZF{l2 zua>'L{-{d`NX _hUHbχŞE^rtm;Lnf`O娘/J~5임W*6boVVS-3uLV& 9y\:=JEڨ%Oq\YO3qi)64|㴊IZN_j+<bCtr.^WipΛ˭ij/Wuk@>Kjզd,az8\9@ +'?c"?[G!^v;n'!sO/=W3W22R f 3M/tx>nDZa ؁G=PͶE0~N6Xi6Ng8p Y&_K qϬbBֹJXG<׽S%kvABL4 !ɺ(5*n"cwt4РW=u׃!tԙG[n,u[o,rֶB y*UUY1Uf2i9Ղc 6p e 1[n+7l +QZQ~mQphvO-}, 4NbOTl|:SY T΄@gy qxǥ W,UMVdY , W1-]U6E=(t1L㮞ŷHX%Z =q;\葉liL5.Դ^w_aD%`fh%6&_xj'ܒЧFHVuAED<^?5IE׸lVSx$$(:`XE'YQ&h$71/|-?= +Q>fF'*hsY/湎kאD0#iN͌ҶWFג>ïr-Il}+u"htpye}*a#|r=3`]noƅX]?3ESU.tahOޕ1 dcdx9#첃Q\~wmp bnI{y~dZJDgT@>,Oa++K5 4C-8_O4, <]v0Zvy aLG ,>5vb"DTeGǓx9c(Ĭ~DlCFށ$p 7Lvg9Џ,臕&br+{q%zw:N̯6Xr本I.6z.6="z)rWmV|, Q^l9c4]f? +VI…m +endstream endobj 41 0 obj <>stream +f*Wu  4Z7(kЫߺrw'k$?eBa\MC>Zɟ<䫱JS7Q5$6gE|5Nbܵ9q2 sy{L!@x1R J%pV{ɲjG?Wvq;./=)덜ϟE +z +{p֍~]b FYs\',X^˝ +``_c!qO&xa, i 44'W"޾H3F/znBQqFۖi( +Y.V|<Ujjyc,1왌x5Gih?Vx~t3ut一[ȯ5bNte.V1 &@ |SN̆Wi~R>gUOvD>߄^wv +}:-)ܾV Nfp{ +HD<l"lxE/>󵄯5)w~bn$&IM02o':$[fyd\5@)Bv'ы(.ius2n7~~-Zz99z>+oK`eoxk.ԓIJqxCMJ6P,%/+3bO _uWP0H~"fai (@xI=Fܢ(Ex9<9=1۷~(}@]t(,acQVL'/z֌Ϳ˃fb߀ń^ w} Ԫ( +zb@-~&,Ԋn*{7͆>|Xb4+(NEԴk!!T/ gg|>v}v=7y8:yM„8mthGI!f0UKjǡw-h}zvڍ`k^@ޟA>ޠDw73`<߿@u5 nAﳍH}:ʠF'm@?-)y!fڌ)GL "AQ./AAO@o=] z>:lG/M#v&K|tb|V7FT5m_E]=|p4" wpbУw@n\y:o +AǼ\(<?ʍ2J01RLԊpj"@|w+,2!5^xAV1̓޶xBM\f H\onמc/A_os4gIA(_, lVs1ک*Dm!o*bvuTRk ϗA-#˻cP'k.SBucE7+{zll +BD=c + + ޾p' G@'P;0 -A;JY~} cMY⻵Y7#~HdF0yEV)Lo~ t}qv%zxܞ?Oнw#(zק5']H?A!w.wRGX}Lj8>82Z/$ooP97An)賳 _ty-';â`-*Ox%ѢMgʛP%!72>R<,;,q s`ܟz +gN\_ +B~'5:qnWĮ"{!ԒgȔaoA@>Ay[s'PQD:Hoظ%&8ϧj(G fyÇB%v- oKН 83ЇA\|z@zSiqeMWxL +>~ +ۥ W~/D2^aA`RaÕMnTK塬d2y^hkݞ)S]̠:g%D-x =X_}t؃A{6VfjGHe^Hy2S +H#YB L6DqPoIeIyB"oz?}SӣgdhYK=sjn:W \X\( g$%8;7k; +t=#":$^z\U544bՂۼPJFZ$SqvP"\AA\A<}ʅ;.l)3xsa77Oq!W^Z0Hw'+MxU7?qDǼCK bRn\0];Ѓ?>8yæ+* r{o4O]l +;k[UlzcWi#^WA܇جei}\O +$#OQTBKDz^)ٟ̝oG{.)zgϭȰ?PYI2"z +| I7i|]3ی"]Z8Ֆ-nj56LWsGhgU[c)uFIvfhcaŌ^gFt[5ܖcfvXkMxGG=sb7 vfYaÍ%1~nR3J5ņVBsqut;0"M+&]8lN]Oؒ쨪rkzMCD܀tiж,lbhEܮЮ{dAvD"骎oZZOEǪj \eaT +>uw +&A dNwVKepPSrj%㔘RE#ddM<(;.Nؔn 8]m\1%M%!GJfbVhTbeAb~⃙F92NmiO +Ɉ^+Eخؑ6PiYZ6G0/pɧ+u9]Amsl 1pooϝ3MېU3lUvXm}R"]'"ǯzIY %_J]&( ibuVdS5~2K˿bCq&䤤# 1x"Oٖ*yX>[jF 9Y7Q_nRqPk xU.|Dkmi7)؍{QTࡘ%&q9FY P2~10 Z}f%Bێ^TQƜkNg56%t(AD\+9ˌrFY,=c&Td㌜3MK} <]jHEDe:Zu eV(&lMMFM}㑂3TdTlBbAmU۳&|+XS5 rV2+'WUgUaSUn!,"%,b]mp^d4i~j}ީNhۢe|Yr"l>[~۪j{JUI{8gv'h>tqyX;٪_nն4t5esW~з/MRdW=+V9&io07p8LC+1ȘХ|'M]VO;,>P]L}s$F}}3βOW:NVUlcCEظݡ_V k q窈fj'Et?U'*.(cCZ$\Ùf-2Y[yAc+8nRbbJl^RAgHz1 3/VF9HtEA/x4Yi#ewB?m #;<ɬbRTTB46Ҭ \ON\`;bǿ\i^.ߗߖheR/4b9PF(0 + J+<֕ 6)6 )\C+T~9,.uoZ쪟$&|[bւ\(X\K(Y+U_j W2.-*O,s4^Z~L^nP߶:97EͺXi5kXZ*QY-7η[mU75\0>̪^z4EO5"rRC[?sdX%JС=wؠmK,iN%tCUέ0p'p)K#H65^1 S 3dt`=}MþI(z(*l.o*9*+}αʨg>ГmT}_Rx$g#dԶ y_I[ @nKG]OܙeFᡫè(s?2jc56v{l:0~7vLItdnߘ&O2}Z;м.a5CC͐ 8>q=YIpGqhL}=*sW9qL}]"#}l_itWBm.dyKIQi[3,p㯈Q1Zdcxn`ARӘQd0"j{Rl +2v|OSsQTЁSres¤˫}Kx꽭g኏Y|@C翌S*>ޣdW9Zm>9[:!Рysҗ:!Z@Obvb/-A)<0>1 Y2Ud_d넇C,sM!ꘄGr2jr/䍵,i2\b'[j-|♂'XM)Ocl0$!=NLQȏNNLAB=3W|Mram 9W?w +5܌xQ'oK96EcӞ:bn.>tKԕ`s0lĎ^ul׊6e<\SG]ܚdվ݅ϼ2>Vv6=X۵ydU}*tc|%6Ĉ@t~ݛgH>%&5HX~؟簏, ֚}KMȥz)E%ܐ#JBo^xG/.Z|*#'9QMEg]ĜĬ?Kb|>RG$H&L2#HԘwd924e _9pSRb1-*Ӥ.}s>Pa⥥F8pe9ͽEK{ÞZ 06\*y_(.~Ġ kX䜪Z)x083C+W 9n -`=qɧT~%,T36oY%fY]^K;a%k!B Zr4xdScK}}k(V@LɿЉ8SҮ*ቶɊPy0˨<(@~t.Xd"1?2qJr~[)`O3ԬK䴂Z~-kK>D IiۆCn}(/(\r1QtEݛrXZ/Ms'BbźJYJkNBS7< wutZ+0B{ūkTԮU(%o />Z-FlM9@03bTT ӯkXI>4RF5$$?6~ 861 '@\c#:RYN9KG}=\ +z41ūal?fB1tO+u#{V~{\fk [X9VKI/ݙլY#w xǙe[ㄤŶ'k<Ɯ*A.S}Msk( +tYn֡o'ԜoZ:Up+R{z?E*6Po/2s>/}A|{F] +?*-7[?P|7~kwPp +x9ߚXϡZu>ZJɑ1 ~}e-ڎTUd 6{]҃ gaW ZkBM}㘔>TYK_f{r>oGNkq 9O6 _ԑCV1fJ~HIts +UcFQٳ)RUnǰ1OȪٝ%m ?zZd634㨸TߋSGsLs(p} cm$f|sSr^9*WAUF&ڙXM.fN,#JaJ:qW-\ߟn|oIo?|81gn:XnJa]Y7W:nZ;U{ hwP~ z5Fʫz?L y.X#d,km(p B0mCxi;rl +o'=agEkRZMh9m !oWL ǡ*@[qbb^Țs +KtcMuM벯9t!Yd).Z|$)]me 3)5f_E@6R6Ms+X9$)+S #蘯溶SPQ,6X=IIYST2uĄD~X}2FTKn*+j^\qJՕ'0몌ydꙨy< Ҷdj .]FJ_K_\qˉ9;(ے"Kij:QʅKC:XuhӟzaZ'z2D[mm&sg?)hI v< ~Îlyf6eWC*R4dW%&0>9ڮ b;AGEBܣ 3~XM񷑎qNbvļ Xj޹ `X豑jiU*maz: +xRo*gȠAxGJd,\v8;U웆nM@z?(vdυ7J^n VEA:@Y_ߜf, k)V0=k0>I5A/bHĮP4WdW]r̩?\m[EMjv4# 7yb}]\iqNG:9_ȹe) \jɾvuĕA>):|Voٵr܎/ q4\NS nf[X 2dn˄y,}IR=8?RmZ5A.ݛĬQv@/|R.xy1/)3]>;>{y*ț9\k9~BD8&R挰bSG5C cb~k+ ,zrS ?^z?>'c()X#3`~no*k5{a} f-r~Qb3f1ު>X~myKVs6& sӘ?ɇJ51#/`{{}$觿vg;2KHc + 0O{r@lIIZ!1Y-}li/y ed]>U~9 _ا ZzPE螵7rmzQu l{Yt@L5+ -i\]aDUi5vw:6yU5DT0.뫉MbY掤+_kZwmSnHP O͵]'z6勑;Pw啱C/Seaߖ-]mSEjꎬlꞚXs*tsE׭9ccP+*Nl6{P{8ڶGc-9i&ڧ ITC1);&rOw`l>05XO-V*yR˷5 ߟXZϭ-uk˝%bD2p9\ YA68MWGBF@/R/ץtЖYjv]Oٞ$zӓJTcﵺ3Ԯ9߫LrJ1殲G+P rCʅ.tb-8s 敒Ztu~AUDRxN-[+2FwϏȌ}=`]ڜ'CY]/fi'eURfT]#] VG oV ZR _X/`:Q1tss\ #ҭo55w{pr[|пo!ܿK@2"r5jw uRd”äY6vuf]w`R=DZ8%п=} osZU 8n)`ZQ WaCUq̴#ȌQa髎SүwfEf"BZy jj_>x_,|֡|BKG* ?2n9MAϾRW?(1e/\S 9{{m} <➎Xthܳ/Gg.E`a4FLvv@\iпҕPI(cdU霩- +wb/1Ną٘8lduo 9>5AYr7L]E,Q^Ei@s]c}nO8F@A5oI6 2w峿Zy֘̿Wpuqv@~HU,0ݙwCvPcFAX+CO$@[Y;] W/j80Ԉv4<ʶX 0eO)Nqespv_kټ j{S`~LT~J,"|+|@*B.3v;}17Su/3b>Q!DOK,p)puҟJfAhmXV #eXj#$]$-'uuQYVGqt[F^#jZ/m4Ȟ^ԸTf983OƋ;sz!k 8c?N o >jJ=>٥1wѩ -/˞Jɷ'れ[{nBT!~,lN^=Or.z#]@WjJ p}IW"恿wۇ wmVaaN)|WpOYWtG  jܳU+E$lN1skZuQ*RBbÁIꑱe[3sWUC[šMka|kd߫ܚ)LlKDXO,Tܑ*ˣ{-fEL|HY%^͚bǹ<K?Iߖe;ZDb/83E+J;bRıI%[[y,[)|`jx؆^ro̰u?<C;(.6IJ:rOK86 +DGye}  6e\cJXNrJו~>#M*}Х{k#PkohU "{$|V<2DsO3+Jd+bS] +tKߒWFxy"jտ(U_= ,VgF@ۛo(Ds'"tvQ%(}ԍNX{ҌM#.vUYA'^fޞ!g>fj1㢲5C |`ĕĦmyr\v ;x2oj7յl}m,{n\"ïI'Xtؼ0,;漹9,ttQ*}ux> 6S_|BK>o>hT9T,7w$& +ﱽody|k!n.uuk.8 wɩG^MYQI>@tT`z5K+ssh5 [s6اkN5^zy(/2mS,S5o ;*ʫd%UQCBD^3+k궔-%:ݡnJ!)KC&wT÷ [Z>fپg&辑= 5GU0j|*=Bm)``^iJ 9=3fܳvQb[ qIgkL23`.NS.YZ8& s-$FHu*{\ڡqgmph6@㗇7'Iʺ̻"x?ӌe`n,𽌓sZ+9sSIWܖc4,r'\me-J{ R.|m޽nh0suY"Uq^&ߖFm fމ7{ +x941N5?i k(^. 8+*F7c׃uq UCM3nց`USj^RAlW까L"pVsZbނ٫0WIkc>&mۆLiGF8E֪@1&BgE 1w>:0 n?#,聶7!y~GCAG+Lڶ\$$3TG<]n=k{41V6B_wcb/.A)r[Bu b#7]`/NR*Y>m gG'`YؽawmZjKAl:7y51%;ǣ:^dAL⭥ +AVP*g}9>41w/0mW-w(1\۶gq8}LPRA6%cTu dmRp`i5wՆ06XK_\aԁځ}% ;Ak}Oۋx畂=ܲ[O/}'*$l"Le/mgSd]k^9uN8Uݣ٦{l506ny olGN9б+2VIj]`# ~:g$lM`s,']D;;F *^-7TƝ0sō]%tCh6SeA"X&zļC ?fKگ&HG/=]/|6M?_>^#,wV15/CjSYVm R(p"fy\{V쒠3\2x#oѻjQ,!$!k 4$7pE(25_]_DȡU"}ߙ>Tmrx~&mBQWGh)g<(lf 2,C#&2H[|!(;r݀Kɧ8gxGߖb0,#w{IVFsY^|R?:bm oPWzlNPXv_ѣk66:YV@^ W/bc5<Mҋ4Zzo2~O*'^zv.(`gY6@Ғ`dYԮ:0ADqԍ̉DVQnI@5j%ychH )<`tdR]ѠVv_'l~¦mG-bAħvȓ;MOz਀GH/8eM 'RWy`ًm $H˪ +ADbU_Z*2vs=sб_shܱ6Mx-ZlCk^~CŮpP-76qe@w1: `pDuHEh`mCaGz"̧Ay:&oQIihоP#fmd,j;5&?9'S'qMbF^|RGų3KTd~b77[,ߓ= 2>ZnpIAo퓥oeŁ4c##}l&"d評Z}b d*ꋙ;񲯣#5,ґXK:l 2T/쓘QjQr?%)9/'1&L%Շ&bTmf]} LN0"bi}ٶaJk{ +rZ&q!ZGʹ5<\*aV- XR n%#Z&p H2@N8o)}f##ye9gqOYojE~hE7+%;jfU*oSjE{*BwnA-t^(O0G'8U[;k=YAFp7߻GQ Euwd,lGA:ҒAwmEOtME϶0:3̥]]l_5_۫>E{KN/Xj˻˸Qm%aDEױ0KpkK動vK`Mg,UqQbXTw)%~]b=#jMo:};~H~qf @*C#526Zbh|0 e u@OFIC= [Irʈ.51(y9tE{+bd _*zm̼k +<\SSaJ|}3[ d_qL +<䘼-"z`v8 ,~i>^mV,vzDN ]R?G?w픿_*{?-]ػ<3ʭ)AcރߘƝQ1ιJmc--/k +mHe,]O,6>x'dm os-kl+;2V١[}W{Q+- {9O8d?Ԉ)y9Ԣ[=Y!alRL}-d żgԼ#kӸKN C6ň=5PG,>b$C+ 6H xۙ%nb>l%kļoE^3"tV} 8)`n-4C>h ́ +e\!obH:X~o ߟoԴ""WG_YggFF~Ysg6'(yHɄJ y1RN30`v.)l/p`j .+ey%%]XDx K2dGi=- q`P6&Zȧ@OGm#?Dwϗև*# ;C伏_uG(LeĎ-x.)jwUXu,=դCzրI{ѯ#?(qFXq}tFXw7%d8:jc'WlHedU.g6lBC߁qdIeCsHafгATkg+"7KBֺ A"Yz(b>*T~ym+{-t*s잪e>0gř/ׇF,x;IGz>_@'Jgol<5U_uζWDYS2 XuwoGmN",WSCQ.IelM)'B{=lXb=RG8뱥aD䩎K:()jឆGӈX Z_͊ʟV"Mm}'4#BG3 hj +^-vWu!kZ!SJKW9Qcc#Z4xkvc"gTD]U2Q3e=6crM#2bm38{>՞R׻ Șs^~w:*a"nX{%[rZ- +>ror2ק¯>s{jBS\nj}&}󮏐̫!3eŨފWԂk-er&nFHm_j0rDm ampW:ɽ9uI |m%@Qzp 1iv Yv*IyҰw>QY7iecW^x?&>]tk>l_dpI/- *b_m:4&ႌy[! v:E5Y _0=÷%@O{K{5?񒚟|cK U wd!8>O~ +Ԓ`_C*ϰ A~GSd嚘B;:/%b-E {P p0EꚂnI)ȧ] ['J`Ά,,'>?Y֍elkN"|̯?YdRu/ TiN% 9A\Q 2(ya1 +AE3)B-, &>)r~RkU`m_h聽e+*wذ?-4<9^;|ŽGw8>D{wuU 3aL7$>b6~YBʸb3Yn+詈u ݨ`}mH+&d.6ې!]  %Fg `^?+O;iWmZH7=^,`|ZRɮM + 42ߛ5_WMZ6bc2טww{:2t̢An[\50GwA^o?3w2cSƇcS5 º==n$*EOg:!o}jtaȣ{մ2^jnIIFẄS>-4w{Gcؤ?z*>4NHln.zx.YgdC?ڂ}`X ŕCQqFLM鋅^x¿mSG 4GM?`з{qПio\n)0숳Jne}#衡⽥h2BLq.X 5p0I9![u~?3u^,ځ# ;S y8Xyo疏o.S4yEg$pu\Tׂr/kʧKN_e]k&#K;}=M3BKձ%1K؜ƧK B3*zMx RO+oω8izbw1Bܟ/G*]쥕_ +zz;T›mPϿ Stg /ʸ /~Y( >VJZAϵԛZA_?tq + ^⫋O}3ѐs4ĮcvSԕ#dG$%d\&_!e]59tr8WzʻtugRnSf֫16:'%^\xG:&ʓbP?'d( @w }jtc*le8l[a|#i*I ($|}9Q=Kr>l(%r/.R]gK'K%4Sn_&Mг=*oc,skMl{xߵM +lS|$%{m6QR+D5^%f&Zh#uK}ą. Za|܅~dc]1`Mnk8Vf] +".o(e>xobv 斚S!*~\_2VΥ'^)6P![F1qƾʠ-ps +gUѪ"RR5P_7 wZ\Z)gC7& 5d~r[e/FK]}dR}!gMLKN1u,mCm|_)<_{ujugW,u/v/vcև涢{ƜlOK)["j55;Bwq)ȹy.iW/|BsQf Z^D7G)yv#3IYyw!? Cj,}']"$61N(2yGbR)5Zwem1@uNa6y 焠)#r׆Xƭalc﬐:@H兀6 927GQuF7Oئ·4=P2b 041F+kvJpIS,IRJ:l|Y4!wǸ%#Hp)sTuaI\gZ +b徑=6qd={`Xu8y +ا9'I. ] ,1_N6!߯2#W7c+]oY}=witS4tW3 + &J6̣"}Z.es4lakIPqn%t}d3-+=fa|幺3O{ЋVa.T;gL/}hlk ;-P=HU}kC3"tG!/* oO0T*#Lo5衹;J+*==9_wǼ{Mn^LMLzYAQaj Nޫ?&&c"~"_W 8&Ht`Q >1PڒvE6͵Pεi}@;-϶_8}e(Qqr)\}2hg=Yyz/ +F-7Qsä9uRf{3ۣU{))/0ї gY/6zQ;rB}W%SaJ>p7G%[=Ḛ"p17(5yn郥/T|V`r)H0hF"7!*^̵>s x-pcࡨ 1ぁsJ QDgXN6]tGA?4i'&6鉶#̯/XG\銰5a綊j{Ip>,wtVWtl@ QFl-9+-̭y7,my770CVǥ.|G.Z`D W5ITWŨi8c#<`^LfPĹU-96a=83oeЉ +uV'/TkEև.Yೂ-iu#B[m%=ˆ-!>:hbp/K#H٪]-٘W%b$LS{V!f2]x"&Wݟ ^D6|bSh 4'*?D/pfp,ص)FM*DH.BD "鼜S诹ϼ<yfwn haUe{wwm#L +3j}>+AϷcoSߖlBcU7k +՝g?}3p's4OT}Äg=6ٿM/ǹyo!a~}ܝc!2>jmI _˽WfߖD]++E:eD][1re1.v5`y> 46P>zޗQ΃_:R >3wl뿴呧z ёjL" KH.2#B>ʎZqIku[*lbc>JB@*q]rnM{I2AGxi +aoMM$[+%JMKsk'8p_1* Lؙ̉cV ulmXlZ㕒޻/4}63LIޫ%%;*vDzޕ +{WQ{O~wvpuG:3&Gr"vؼgv]]sҺUIډɧ';syZK6vf~MQΊZhb庍۴sPby@zuP8U; +h +?C-Eۣؒ^7:z3"A8UkVI܌E ){FOd҅ަKw\{ [Eh\2=HLKNf㌉]63*wG9/n @giʮ_w 􌁐3g/{\"5VT-jiŋJQhüso[ZVl򾁔~(3*GO\o_[ΔuN f\hh)ZV슇@ouÛD蔴Dw͐)JKʽDd+n,`NИq+[saKu↧}of-i2|ꪝR6 Vʯlљ72gVUxnH*r +<+5[1#*:UrJ?agnxZJfT7*直!:Q@ͩ!6\ _!v²/? q`W񮲇Gǫ?9Bj)HnY1%;3.۰Q.}ZObbm#l^1RYu' 7Ʀ.{81 7lM*>|#iL95,!M4GΪwykN>cwg{DKf.tNk9Uv_7da5-Z:٫^pesTKsJ vz?|o+Q-{z._ceL j|Ԑ^ub=[X_/6&0ВJhmZk^nZג3%/&~]t]ڪt3{L0aD}anLiP#c  ֆȿ|W1̄ottm'Z 4\ZUz 31 Zx||ݔmzZ匚RdBΞ'ZyVl\^睒+31胊YMy#6}Nk0ѫ)~I9uy]M;# 0>*Vۨ,RALӑKv@~y]K*ˇ67F<(?i.:9E@yyh` +*=<>eٹ s6h耇|XP%OQ4@E#.@myu_+acvnM6y[Bά`pw]ѐs6 +(!5&֘uv.wcE [5t0vQ ?g\;Re&.tXּ4زH3fXu7־?15!06BR:!ۉEKwGۧ` Э [8MA%5sJïw +e'{<ֶ[_͆ St+Y h?옩 YpgLb\r(zz񘂕듰*~UkcڌZ}/i];^JI؈XA&f݊KTj&1m',.Z!3kḓM#2d⌚^?k :_uwg/F sY[zt΂8qo>}kLZsr޼V7(u[R풝PflȢUHTDŽ565=Lؒ0!|-81*rRa=Žh⤚zCyXVXPW=X˶|lRXGsoM +uՆY1떬Ģ9#"i(shx쬥uT=@jCG\{G=9sŪ,06&E wCPz}Cjl&)@ x)LXaWv+OfYO`GO _vW&Xz' ߙu/ &~{R43%=[ Z،Ȝ3a +9 OOuQu٘7 >ebickӴʗyP2d=[@_oOId7@N6˔;g4l:|꬛\=M#γV 3.wLvtK"=Znjlg(m~JNfXi/ݼIZ# ii{v]HZzSٚc !/I#plLj}#rs&nmPUet-/[eÕCMmxoUw~\+;pSOc@0S$Y]jŒ y|jYfe)۴r2@K*G>1^ ?CU^px3a=*8}*GVx{:g̊i=ゅTqVӊ; xL(2)S4Ȝ;kb\|~P4=pںh12,5OD L\;cfv/g$ĴAzuӀMmi1LXU N.jN~v( u6CXP^h.{!V*2-Rcev>mO|=9kmӆ(]p :%}LĂjn~Towila.䀘 +|2A=7I.!kUigZ**QG͌]rp,tEޙ≏D 2J\ETg+`nÜʄnW2)Z&h"ش9ګt۶'I,0Lgk#{7e?;ZsIH4uh%`L8 .\PumZG;Dn.z%xM&Q6ڪ#ŭ|!MV= U c~/C܆(75mdUޛc +Æ;W&Y0劉Vxʥ ӴI&sz=2 La,g9$;P!Je2 vQ>uy[!Up +r)D#5 27_ +6u;Ñ.L4 ,JUU!"|C +G ]vTIZc_]–-Qt_01ξ)S(czXo2m'ǧBfd4$6+[;)§fHJ/J4ݛ3)Sn"ܤ=6+`=90`wl6.G(X|Q#BwRx"/}N;w"݌;^Zr̸ཷ+č }!abB|@SI:UْnsKd-][Ըe'+9eZ UWAnH# y4ꞵX + n94mgFlޜ챭{gb9()xXQ&tiz4Z\ NU I,K?3cNJ"żo^v|̓d߫&%$TqV/=?c{/yY'?ߏGq+(O%Y_9;άV*.]ҌHD$C. ɭh!Y&" 7MjdK.JK$?{y+7"9=f&LG,4up^rq3hl 2z)㗢R +ɂ"cWoFx=)#p/'FDETV딤y9Ol)Jx<6K1xY |*7ϡ1 CoYdc$VO_Ȉ)?YM0w0Sz[[>вO-Z̏]=&q +Iҳ@92 c,b!/J?Vb*_LA~o E{)l)qGI]V~G~ŀ !@9>\?pQuMל{ӧK's˥?=zp95ҵDj#^؞|fupwﴣ*_GW,ƟlDo'|x{_3 +d߿@=vquDTK5C4o'(O&fKΤ#}sg?}q^~ěDjĽЈKs">3ʧ. ȫnrTN+I sU%p9R8XwټMF\Qq;EO^##F=B}_Q?'4:rE̙c^Q JL~J1tT~dbo"n=Mb{qqSĭTޱwҏG%N^%3{YE&P"xrcxv7-Gl)w "G%a]??Cv!8ӑ0N-ph)?vI]ĝwEwc/cwG~X `.G~Ȗ.܃kwIʟ3W%|e>oFMb?!-oMch/2tZN?G7k4riLnwPݓedl*1KlxdJˈ[jzS[ 睼ʉs6>"dYn5+ohpo |ч0?*ao25~wiNDN!Ǭr/9 ?d^SE:9ԐdV.j`Sn/Ѭ3 FuU ǥ~HV>SX/RPn(wݭ3F1;CEĮ;_2<*QZs~ |siUwHW c3b9Y㭌KQqT_Neߐ)sAq~^VEǭRM8´!6*^7$rꤰa1L)&B~Pے1a);EIO-PǴ0 ˫ܚk³wP~aͱq$^^N}PV?hfZ +zգtUEյ6\-htFXd#5d{8sM nfq!Rӝg ~=5]0uŤABHQW!rak;ޭ׊:HdN+g2XQU?)n|,;YQ >0v25)ܲ~cF^ۅ}[AJTV y-,hx"$3N `-`(ul6۪W5^PF kGL`hp169ؠW34*ԀLQ,і-%V] Ea{(_(u J4n6Q6=B谀c[MC|nݑa<Ő-k1RY?v>}b?&wiP!x^]kǨ `K\cS&\>~X,NPNp9AO·o/>vVAXıwѿW06UT&% ,gTW9(EjR'[b-XjD2-]H5.o60PdwP]44Wa7HA[v/jsN"[eQ~k7h;]ә'͐ eJ=D:K$A7h!`m^g%{ Z)9IZ{UW: I љ %HDB=M<ХЖ[XOi5M+ў!Eޭ ՝Ba! )PW+E$VSji*|z8BywkR$\ظ =iq+*=jqS]UY"CThP!ޡzo45(,komy萐=&Uy8M/9ezUm%f1-IJBOқ'%nm7̣m}Eoe- yy1.Su׃. Awi51mFϩ;0C)eIʇDMu=ߡm3 +4-as'g%yC`dl!އ̌_iӧ0 #V[ȷKU=}E4KҢG["[H $-:[֡u}Ir&,ʎMºsO#_CG36,S\qދ)0JԪ?'s"k1jASD 庠j_)դ:eo ."=_՚r+JST?8UBbsru9Iꝰ5zf1 &&~]8ȣݙ60Ou4- +}%o9bqpsCS}V ɥ!9BvڪMX2HsfrъR-hcgɨo"w^r͜VT#?NRՄ]8qTص<%έk+uv]謝ݰ > +|-k.le{pV۝vLS)u* jPu]BT :iˇĬI`/յo~qYuK 調cԊAΒہ:VE( K϶,'1qP?^PV\֬! g'A6wہ?!6yIsvfWx4Qd/Y4p {^R>lgڙ utJf 7S*\6#qs:LʦXy!jX6'kR4#N=' \s8Bvb暖QFÂR3'!T⸬ո 6lI0 ~ugӨ5oZC^sHv`El\WȽcA ѿLK +_r̅;N&fg9y{Kٴ]D +$Lu u+y^4=/|Kד NעGҾ0U{#T{z}8Ik &oPisƄ=7t4cPb->o!*eM̽9waDkݙ'L€Zu7f%V=xZ ozٱFl]Ê6\YUݳ{!Yѝ[ ar5?M2hu?Ou.ikMut V4:!?OSY~f 7,ͥ󪒘"fX zu k gfr٪ڴ?)u9g[lƊT3C%ME+ 9`A*8y8taʿ*\:+np狐)7;RqۄY&ϫӊ( -=mcVtqӛ#&A UGp ^8͏)_?XȾxmCi^N˪T>]WKx=,{Ѽä~%C]'rMWֿmx۲&F;A7tu*eM9%ʻ0]G, 6J?8TAg:t@ums gE~ 9Zs*P)'L-[YTغڱasM=/9wrDfj?nhj{v5c^̟h_r4zZi;6ֆ k)mW]85…=jY.(*=.̿c۵6;ta կiVlֶR`@f I-pOڷ7_tE6¾_ aN,hIXSV);.T jW׽/4oQqx4 Ҝc5sk,i{n[옠 KX†_$; +>9s@|"W`s־a^5\e +Z5nQG+ߦX- *{MPVB~ 0 ƚFp +~@~v+,P}p, F}#4z 5;2|NLy]K,캗 +e{s-3ۆ }sB@ZL\̨1P_Ó ʚ*h>c٤徉 ӊ`.nrvtr"kFk\3@cAe Gs'CU1 +`_|j?ȍښpEE Cu ak+pU;`%B$k<3OpqД6j͎p]Ӕ(~ϏrNOJnn%=! ;VDꪪ!j}g^BcNs_tOC{⥩KNp2Sge8lXM +Hj!/ׁJC)H;&R'''-z6T8PbK[ŋXRULK~fP|"ېfDŽWvDd߇+_Pռ2x%5-5NJmkF,)tpmFdŒoe8z~ `]4a}wa]8ЁWr48%lj^/ʮ @^oHC9i=xEIZ몪y2}3SV~q泓[V@cl}:oC [Ӕ>&:p+*O|gP/pޟttPTs~lǃcCx?hOr.L -ao'6%ucҊ)nEnMtUXWdNKzkWԧpQ ; +n-Zaikx[tw|ќw5A-*L&RoFo7,U<۱N]j~FM[&d0;tHǞe#dȬEYc'įA2| IZ1|}2Wt}ښ'z/ʋo V<椝2ZQx쥏!fAXP|=/y/{.}(D-S(-% iiԠ[CO qaҸg" Os2n7꺦QX ͮv{NkiN^8#붪5Sc"lK? +^wzM]%&7!bNziI'@5 uc{& y-K;/ |wGE-"e@yۄ_7M=o^WrSw`Ma'B> >*|Ȋ} uY0?PtJ\tuiHPo=7*gۉٲ S6 COg͸,+0Y=M+o ʯxZ/j1 +Tܢcyncһi+@w-?]]h~eվԕ=ڳV;ʶ +<׽XVս25]?~㕄Ҧ6llFX9kA(=%RJ6$?8f!l%o|m6?G9M#6s˄5RAC6bɚ}|}e$8m:sM]xzqFdE]ݞx+Z׉BD3-} >t4_ւť%_XYY[.^Ok>:Jޖh xNbRM)0)`m8ϓ9gE]`+a^U6@+T?m}v*w poE-6Zi5좺.~֜>ڞ0(j_S6Ue\/{Esw9WV/VeUc̎)uMN #z9k\/ݟyPmmnnp2OHr_y"$G9⛾֜N>I_gQ s-U%(r\tO%A7s9ھmC,( o uU=̏r"LI΢ + +}g"?|kJ"Ҷ-k:XƦ (hY2A˽6|<tx|_5UMus=s@BוuQ9)v 1p܄ʐ nCbDʖæu@w#(,8P 8>iE. u$zG[zoY^twVZ|{ذnf4ot&.:o/y(1"*eWmRݙdz=1!qÙʣ~A^C7j-np%㯗UM7JAM/k&L +q];i&lISR=y\x_GZvlQ╕tIKM9롥=p )Ol8[*W-zy|mI'ue %"-}7:[$$.~2'.{_`@Gޝ鞜KoD!'{no _?.cVvVaUͩa݅ ?+q?yf$ /*|%' Bs_q~{=lPXxat{^ =h|}2%,8-(WlS0ڝCyZ6}܂cқ:rӊKs{%5粖{eq:,x"{>{2G=^J>-16uP{4DDקs//+Kl[ GFxq,iasKoL uAߞԢ/ C;k;:Ыpoڋ/'L+92Z75C'f׎*3C64w^etp +5e^Wn%ڷ&ǓFT֊V?sK;@S?zh׆]ց_2= -fIOn|S;I|": +ة&%OEب+zl!:H8flx5~?6Ğjê7rY5[w)Kj/U;_jS[3D Lceu'#ͬ:Rɞ;GQi#][=/͈Uy)a A!qɵUmĺhNe%Mߚ̼iBlf55"Ԃ 8e 4@#AiՓUQ*)cB%5Ov̑@v=,hWe}]}3ţ?aM +:nócUM6G0b>zԵ/.|ɦT~8t1C Q卙s +aAҹv-%4ALu< +ԽڐiZXȿ0TbVkNtR^~%@5崅Uy&}wmQaU:7#9W~nV=Hsb|.5_m_^)D+s e=uӄ= CB[Y&n^PuԾ:iܷ + }M$j]WsjL8!G$^42>{> е&M:TWqO9aj^1q3U4-)w6,MY_q"{f>IENlj(_a2nTՖ|~3ޚݧhFP^S*(_m2xE +in oiDA7 q=7foVuJW쐴ٲPEؑ[KY'f%o#m-"e]nSLnQY;\ɶPt_]R!?Xes5O;Ko\?v%56} ?RBlY INh:-ֵ-bb_) .T_cxab횮)nQ z6TlˀgIg2B׍աAnJ +G޲,MLLuuWWwA +Cv혙-[,KE$Y̲ IUw{9[w{f/YxXyGzo05ݸ9M\\RRԵ32}׼ȝOƅf pwN(&!0M\C]<}/(tߣ;Ao0*#ⳝt"K1( sלܶkxt9PO䖱CemLXP]?H\+${g8IQ1Jw!5%^iE' TxV{d CJ>* QS|RaI~r?n3%=m: dMDivOQKݕ+d֕>Օ- wʿg_AO[JSΣ;AdOj~؜zXʛ@/\{=*0/?k,s6_Ʃ_|0zq%4c\̦RQ6%gb&. ƥ>T!sMJkޚ3WZ2KY,uz|ʢJkͰX ^s̩AEo6+s?y]٨V 8/,Üu1|p7se{bPISĕ7&cU*;Bf`QUIj)o]>r/O'@|sDqrx\DΪLszY>kZD\Ыe’?DTx8G1Vgm/i9еQj0ffHìwSԲ5'[\1˯m(t]E8#W:a^B:'skIA~շxy Rsws}s5Eѷ(妇GKZn&4y +4n&C뾛{֚Uw7eBOͨOX(sk})#4.MV?'+c,v?İU<̜]jRxPͩ.'HD܈G$۸>M/Zfν^ kyQhz;:,hrM1~kHA.YF_vn=|LTˆ"!~R i|Ȭ=3RT(B +-eGךoB +* "ffsv>Qa~BpV[񵺫h!_o(n5hCA*)ݞ7'@=JMkm!Gvz^&u<(>kuze]{s]ꑶaV524aUFLE(HHrMsvE okw53j=$n/#vfS'mW+Sq7@ZdB~Rw% 6–#xg75BK ǣI#&$.N\P.cZTOF*yȘ5  ˣ/92jwQGm/YiziPA+iPdz3yGS+qwucw9=W 9BO p†G}![e@GIEQ9&j f|rv{y9rr] /YQB߅dyְ_`ӉP2 +8IiZ޼D4sp1 3ٜ9g탢oO-H +ZW^LS؛) uV(멹TaѰ–U٘ߢ3ҽw2^nu\˄2Qa~ +v2b&=zS^9(6#ǭW,ohxxTDl<>F*p(<ʫ6}yD4[b'N!o̽lޫ{elPѕ I;qa]{R{C<(w(㸬ȓ[j+Tw}.PFeg#M|˽SQ-uG2NBOl91Aę~µw|k̥?Lx|V^|Ӛx\nWD_QQJC*NSʮYgDuN /-mIVLWK}9 <2-/%5_U#c# M"Zj]̣U\DH%D B?;+(Qq8i#_3U=Uw&4C)F^Nom {iE~˺WJ-0z' o,-5zi.Xoؔ"o9+ ^)&?eIV$JR~ZyPF1'iJEzIڔCa2ɴ>kȱqـ +3'`?B툁P2kZzfY_?nڇ*p ӏ{WfK;+Ì[sϑM_LrU!縂X4uIV{>QW[b +yhx_qW#Jz̧A-SC~埵]W~gb.eJP27'-͏ Ww<9H^\_e SJGa voJ0N׋O~a!֌Ӝd bu15?{@^2r?#*:8 oA/F9a y[_ pe~vii<n o62Y h_IaӖ} :eRsϥI v[@CE5ꈚ\9Ur'Sv|u a=% 7rصr!"zѯ渦ȗIy!T_sdzcB԰1̫Z#\]5z3ޚ'ÖFV&7ƅ CArIS/7 ϺP7&PRLr-Zϼ7GL !, +tQiXGFVKząQz.MA~E h츅WRJ_OͿjHiukcJ]IvUDCy$nWֻEߺ[. T{KtaL-&jIcu~.9FS[gVu7g$ oi~T#O14~gzC)AɐJDEYH uK -jvOwwsY>"nP"7Al}z&8CAkUީ9toĪ䬈Jx{j5tA!p˅ȝa%%1{םiq\Y5NdԦқӤG[輛^K7c! +}jtmQWoU^uԆԜֲqe\ &QF5BXhzжKн%`wEk)k)b1GG?JGBPOXȀo-H:.%eVFYa#uxEtsRF{aY[߽%ѥOV5JtU:B1N#/F[l7v+M)=I>)3 b^D鰊Ztޭ4ug)5A)>'7&t:mϡ13zaeOݩuPJvkDl^]?'ĤO%)BQ._n357@Q6+aݘngIm -#ҟn W`s,:BDDŽ&m},4 ѝ1QʜðknK͇̈`aN>k.+e`"$FT⤁Ґԟ[\C!pB [z0fO8НN^xBvSո9231ү>+} kg_NqK 6'Z.&Ҕdt\"f 7!g(E1-%1'B4)-eEt]k#؋-'%.|pISmW f--a~#K_zƱ@vc"l*> N~U\qħ=]Qc{'f`AbZbkۢ|rr!>tմ3G9~e# Toݙ{6*4|iE ?8ޡnm:tI9ʞw%pw[2HQekoH/a =?B*rw釵ޑ^GH.\택xȐFfers^y@R㖶vOo]B{ ;:H9ͫ[ܜ{NpaIjT,9?ūq v6c}rcA !/פ bRq`ZWXEL s3{##Ԕ,h ##bz.9;!.&ׁ6.Ox^1l<0Ƅ[!klXAڛ kx1Gt-8uA)˦);(n3"Afs}71V-?np|v"WUW,,ޜճH [afk^d+nj%Lio8!ōbPFx\86Uj*b5'1iLNn5|[1mT^>)EF@ޙ~n~yZB%̨fj>c|>u=xqQxw>z nl=JDԺ@:V=]՝ۋ+ҘYĉ99!'9gGfYh^ Ƥg0bs?ףK@Xzƞq^淵gmv;cRun}:E/v|PEL|%4gup-)˯R~#wsN5#n*0!Cݯ=*|mtces~`e_&L,P^l5=.uO b8%=>5R:zjRRB|_V4uKҾ[142ʀsceqftL0BRCDf%I 2 E款-;.g9H ;M kِՒ]0k X{O0˒f%$feؽ96:T{[FJMq!<³\~>L:y˕y5B܆R[$]q".l @ς8Ɍɩ5[vL+"Z螦nYx57U?̿?1)'Gt%] 1-7T3t!ianϷM"r$dl;>>s `܍kC#!eu?jp|r$}=x,f戒RT +͗Nsuvlmbk2j0ܮGM9u5ڒI -ǃIZL_|=<\hUC:zmHGhUQ'53|w4Q>uGߊD,4 2>C_wJr hf޲okaHU.[VcX>tO~x?~HEvj 7.À [$E~gFм1έ\寏S6>)(r\^P'!5_!l :AM'_|[3QduxGc10%ͷ휰H8Y94#f /b|3r0oCߺF*d9&|8j~=i\N\/nR%pvs׫`lNK>i!! 2~[s9_:r9Muv%lZDǨjX-a,7 =bDž7S.)~F7a%(%Ԅdղg1F>!o;dT|,t_y~Mr9/ 8t/ %˓ ˓^ӓF~ŏ6=xst?]cZBUH tZ,,J#O!-5Q2[BQg8QzWIFϻ_׺^Fg9I@Y +oHzwE-;Noz,L~qt0"Woȕa raox6]{ү!a]c3j +_4[&.*g6mٹkL% {Pg6(9>Rvs龆x,vmPSRJFEx?to'aaV=u|J *6䂠^yGJ/ 0_i(Au;#o襛2bR?spկ`Tʖ`և +J11r;Zvvm,V$yg+";久A\&i*v~%3C/(>%|q3(OEԔ]xUsg +~qeq.&xZT[3{CJ&te TPڄ6OxXŠFg!7π9qnoGݝ:8LU7`{GZ/~qO@N6[DbnDD$M"JAWvz~_^CN{ZD}G.] tPjHaf(cK,W;1/5.]; +=u'ևlwYU*y *ΎO04_~S{X]!\Qz o@N&b^.恚"#e!Ժ ']tlLh@KKϣGmr)t_ܘB]{ԼS?Q+%CdvX8Iޤ,"( >&殍onJc67AWcZ\;F.6eѣi_QJ\=}>699Ւc&.¯@Ӭ +UwMǒ;aAnN ym;4&6C +z[J.)xMN=~h(~'*N⫦ӞItnj舱ӫxt%p>e/V^xp88ê:N톌Wgz=|/1%"0C_'(;@BW9Ub°<"Bh^Ŗz,_'lN9~qpe}Vx0'Z{ZB +f5ȵNBuO=`T .[Q9E/_!\ëoe  &Mdł:RII+4MJ0Q@}r_WFg7eҰN@HZ:S~%^[rH}-!4?1N*HNR 'f7 UWշ/^_5q[F!q#_/$ +fR?%7_ kа۵ xh<ݲwK9TՠsZ3S̆mk[jߚ0|{]aC;5j~\oZ7))XJ'Qx~5*^39Nh UVK>)2fgk ø̰JHQrKȱ35 T z5EkacwKYxSc@F. +X5q߬B)` #:&9uʠ\0CvnJĜԼ}kN" +=Ւkzun-K99:zD]1qR˗ttHrNtN7_ +k1%!=3)fnG,&s#6.=l&lu) /~\CW N,]|9+"wWE,|{R2lyQ47"VZ4sS, 4ؙQgpim[W/§wY@ D@.hU=pBn\ZX]sKq)rNPάܶ .hؤTRK3꤁LEĈݐ4p`Q )n`!f5*:#NmL'Ć +hx͋c+'?Բm399og%MB=Q#&'LkPOcF6̧a,ȠjE'Ul~z2fDF脴f33˼rxhلٵ>>T aw/`n Ԝ(ԓB3Nw)VSpVJ@'sc %fW}7;Zzm 䂘6CBZMĴӼ9Ȳ?-۲>MB:$̤VfEu?Q@^C6\xPA- ΀2!F^P.8K}9GYBfFy !Q茈V>eF 5&ne!C $]ջc@]R-_m> x{ nRy_TÀ:|r=PћS텎۩y>k{I&-3A^ݜ6t9<0Yac"涎mQ(#س%E@tJ -k;z!Ɩq$і ed謰2j\dsiImҶm"ZȂ4[A-1`C.oQ 9;bFuƄ2(ae?Lq֟b +,XDWΩN + c|X}cfU,e6ưQ3 cc1 VkSb_qv=e=Yj-0†QZWHk>^SRG1sP1- @,1!*;A9Aț.rRqj^耸ԭ1qq7BCUԪk?Y:i"f i(eE97Ĭ^f,~sƟ~WRrC EMEwfݘQ@;h1c0njoۙ~&riXny$m|@\,Voʎӑ׆ǽ؛4Ł#K~g?۷f{FݽK9w-WrF3 [FZs$g騍 %Y1aI5Njsߚ%$8;f>a.ڱuu&vg³oH%K񖕃J?P 5<(w@l ܨYaA(n[Ę1|@9R|*᦬ζ'ж R`C +rqLC.iu~ne 3(6/ea@]S~q{}-~e=ŪZz"HoN+X}[ud'nuĬm4:x>0-KQW}R ukcs˫vN.v9{A361^:2Qr[S0*S&6 t]%&Tvuq>K:{CbGtO^ȐZioE᙮iEYK=/k̤f;I5!5bw[vV|LC\ͬ M^j- jm*rFIu,]L#+bֶR2 H8mm f{?Eq0[f٘]7wtm^\~xՏ]3mqCn7vM#c֞{~3 )9ɪk^9z+C^ + bZfCX (츆Z>2=Fxf85n)~cWڇ;oY*)rʂ4>_KT+a[;Qb3; cww@+Įo,2[%ĢRec"}/ZT́G5;?@wY];֠ JbEĄEfS22^Hus ̶A[7@N]D^ID\ ݘ@]LD-aY4iN <#n}'u!1 M:`ekztly[@1kCkoOTHWnܵsn8lҒV6֧_@JJM9*{wA8bgA۶KI1*98a5$mlIR*O夂Y>~kVD1=`Ŝ\}ï +N_%W9GOy&!>N~-YdUq;|< +x;hd tyߤ;Lv2Ԛm+)']IfyNku:5rh7@Q* ~cUVP2ȫͰ!96WڗC6'a1bhoN,H\# |kԕq^2IkWC룚*bqLoڱ3{s,W +1?q 63ns"vƄEb0W +|zGP'S aith%+gTl?6kttFmrM .#D̺BLSJ3<my_j<s{8n#>& "ZV㶙V4 +O+"a=J+據[y 9J?P"ر1@m׆Ͽ)9ndžU°^LkVPq[n E4!75_ro~ +Q̂&,VZ }c## +J{:Eq{|qp BDl ,b`/ -W{xK/al 3]cFH′2ɮX&d)EIb;%!G|G՝K2@Tu6};-p)Soޖ)2B[hF,}h Ցk']㈫)s2nsJji:ְsۅz:UY!MbOI) jUY!}oR7mNnō"R|uy~[d|IcQ$z"us +>I̙C|A-K86J/2'g A@]@_i/jyg6qo¶, ^S`Χp6+|G8φk_G[D>%dapw=z5Œ߫y_8/1#7#eT Dq+L7V +?& 7w <䮑5[̦YB,&'G_OZkcQ^}Pu}gN&l-Kg_FH;vB?.{JuR+ʨSԾjL(˰WrF]qcbRr~^1f`AM_whEˣ+Ô_ml"l5~u¯A(oۅ !m!p푄Qgwlm&t7W.eg +ƣ|5_ɨhu㜌25p!>~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7;~ 7CX&oGб(.O ş~r$lEq8TMln> %1(sg_uM"`cE?t*k>g?>Q\mÁc t:~K?=w(O>~q?Q_l8w!,R'3d]:u.!]|&l(/.\H.߻?6.r }DG9ǎV'gq|w >#Z2ϝqP2i;?wY.#x} pG>mMʺt|O":4aP )6gX >59jd4o۩ $E,5a1CZV[F+(%Fe\GoEABj MwJ^]|{ !GĂTQ%MBRpaxY%+>);; `oRHX˃R(:fMslD/۲RJV4s[13g4DZd&TL{GGji9q9Y.#'篏3|42Ӣj2<#!zƄ[x1Ð_6 ^JY [Z>e7V^7qN;`QWNL 5)a+`dM`[.p<踉ٱH201-.G4ƀQձ&Zx $|Dٝs"mJɥqc7an52Xc "~, n( C-gz.bZjH05ǧnٹKH8X FRY@g2\L„,,xHOh5[v6>KoHY[$9A|b̧4R +_3[66b{w;ArQtWqK{[ZvݾmkWϖYٲI;>:na$lԎCDԌڄQ@Jn?Jypw;igcbĪX5BjbGNT=li{ړ̸OJ-LX:slBHz6lwI}vvS;e˒,[9K(A9E(rsD. +o !ADa]8ca c&^8$|%M *2fZ 9Q_M; za0Dm?cr!c!G,[3[N4ڷFiW45L:n?c&YG"lўV7Ћi#:vte[+ZEYٕLԩd7ׄ9TCHQŸKМu [s^Ri2.j0c3U҆BE&W^T+tV[^䕊x ,/}Y,lzQ7}S:#n]*adK᥿ o>Yh8Ɵgq;u2DRg|jnP!8i̫Z hCԒ +NWaWWrKщ)An2Č+iѼW \|r~!j8]NßhHm}xHۅ_^__ȅ-,oúk9u=V'tFLG20:ۄiŸh9^cv RJ,hLZui-mj r3˅p\yڣ7$]2V(=̊|@Й hix(1Zg@+< +?Wĭy7aM-ʀ~ƭ22 Ej%x^[(EFɪXW7&ێz~v!9_pdv:7ꜛߌ9[-˺S#JەIԐ1I[oOԶ6Ǽ<Ȅ.c2ZVVY||r .t;$i6oǬf@D'eȜ:mfìr!@=j+QߝO^0N->e|:G'v^ӎG'SF^eƯh=>xq`W`Nn usTr2PM'م^ Hr!nl?4#VVi=iu;S]8HO蜰v}t`%Ht{i}s.>"*Y~ZڙwJ?@uM%]jqQֱ5\|̶X~Qh9VQJ#!#QcΥjŘGЎ%]Ѐ9$y,Xeyrv<2U)xG1:z06q|͂JjZNYi)(x(bV;_.-c! (eX@cxj!(UݰB Y݅9FG02 n)g7(~!$>)nbTI+8wȹy9xu|w[7136nK2N&Qә܂%]-(8pQaAnۖPW.VU>(lc9_&dœOANVjm<98$ +!-̯ 2xn ncW +a.N hf`n34aϒvvxyj41.lNM̢bMjH9n=rtgZXʩS cԤCFKf R.ع5K: s=%N2Kb&Ÿ}esqrky&8L:$%}KC&'f'7@ߗ_5~q_l[OYИXbnrabl .jݘ4nQG;wB +[}t|<>y,5G9aj>XOgq*3Q;hv 8dYC%gʼn94huf8WqR[ +1t>1#`\, }=)ui#fV bg\I* +L T$xҎy9feƠW%FޢJ}Vi AKfˇZxi2n_xT,pyA]R(~]U- ߚQ/ ea~uP`A +qG)*"ND4$l_.ƒ*Tsa=BjV¿i1i!mo^8䫬8טr 7mOwGRr̫֭M[Pۄ:!irv$䛤MڅZ)ng(d\]ĉfO7R҉WW?^xT׭c;&aꬴnfg_"&IsbSnq/'r~58P7ؗŐX+?nO3Su|@d2OԐ ׺h%>ή U|Sώʿ<p*\>]j9pm~e;՟!jt<:'6^6RWϮ!d ōx{rlgz*mTf&54.*~*l\m}_6_l|4Lig֦:5RpVIri+.hwle)#pW߇C]d=s-GVj1̧`[[6Cg c,,vie +Yb/'5B¼7+7yFq_4|뺄Stj3ƚ"F>(A.N-c~&q )r~}U +dli ܂r i7 q,bºR-첝Yڙ}!4٥$Awf).agޏl@|BR_0$%J, >@d$>(xz%Ҁ?2^A[̫$Q%ر0@蹋{t1"+iVwS&asc ܜ're!' fVGE1$RjN؜ c)(Bbv>̨*jCnί3k$ (qЏj -xߖ%(OԹe"NU+0I 2A֯'!7 +&jŜZ|&2q +8{nͻn) x8)y\p2>%|}r'ĩLj_4ǥ +6 +JR +Օ{\@)͸\mFz/Ń]7R^^$ʈrgF/O䀳2._:kS6IVp0F hEIG:9je'q#$f3qKMPs_O|FڙQnLʷ^T<`Y h[#'N EǚXn=f_YՍ@EemYAVb])9K>2ThI(k_iieˡ]LԔvacW%AT3m)sS_kJ6DDޒRs WF[~\n?syZyToA!hہdzlQ5jP),=ż\){_D?N|OX5Y$R4w?7^cx@?LK;q GyƮflZY2C>(c~^ 6C\H& o] Kk{!>qnz=(=F9eT$|A ը9/PV\OrPc7AɅ<֪80v *G_x4Vn%kWr[ 36d[B- w +RߙAO0^I`V_躻G-(UIY{0yB`e.%8 iJ^>n6$- Koz{trw*1*Q |}qaۨoٻ{^ !jUG!̀O&I+juȜ wid[%_gboOi 2/&I^>,,ILC]dY +jN'b<2qq𛖟Qs (E[ʸ4Qڹk/K;&'yy8UY圓X6aP狏?C8䘥'5nߟ"RZp큕MfyUKO[lrG y_6v06KYT1.ɹ7P_>O^zU|_p*$7K]Fi}s#"*XyI99+',uPyuW[ {WO*ak0"C(1i1D$OBX"x""^VM zuTRgY! 8QIoºA'f&ŬZaڤfVn-C/&fqk&{19R JSp*A1=0ZzGS!e))l)=RzƪDfdSz }[UhvBXrpL{v }/J)0_%]|ckÈf=)|i3y' l/żb: +-aUŌ_VzIo=ݗw[,+5_H%IQuk|zs|*c1#ve꣍'rjvouA0ZM +J?v_(zyeU4Gz tD?Z _IZ'vfYhFt^ޙ0+Y׍2pJi9AebNI !`I.kzO6a<ٳ5nWǘ% +x``8{n^NRԢ.ffSkzQ`K#ŀP3s'%ܓ9kO7qL96J +$bVK6(btFr%eB*D52awYoC x +z r  Kii7!n)+>&?T)#sQ/si}-쾏fQ@Ϻ Z?Ɲ׆LY=7bӫR7񛷦2^}bUv8V_G +Fr֌ <+(w%njJrʪbaD#BH-aUV !UN9+]f4ȣ&)i{[j!4&͢"%%MJQǑ3_y^DD^i6':d]9CΌ +]ȴ%<#jleaVAsrSbU5]y)5T +^f~͑~|YQxfٴU%ơQОu*+tm|"=Q࠙;"ZĬ:AbJږ&e$ӓ_!6cbØ^ pXHAbN ԭ',*N¦lrZw\~9gu3S^)%w!>xs?YU}YJ{$v j}nH۳ |9- #"muv32( u{oV'"SڂKoH;x [ tŽZ n"۲kg%c1eFZ} )tHynYx|d{BDh&7#,b]O֯Vr!ki\Z<&,x0]P|A׋{s~,났6[(3އ->]u) \?h~} +endstream endobj 42 0 obj <>stream +(较zET>S,*58dh̡{'e+vI;:rAdoIE׊tq)h[FNyxki(j:"RV<F +ZlW=S]MwR>#L̉a]lVP4ڥa֙WB\Jp4 9Jt*.52++ShrNX*y{ow =hط0PfXۥ{yAw5y^ujXwf-7|kL?g]]36*mU;nX iAXM~)j5:$-XK۾>apݡ¼C_Ko?G7_bnC7wwHYh3wK鈋ߘٻ{Iܛ*#g?nW}V%^ړS3O]ĭLnO^!'mZI3{oA>F ^1uv +A\U}rs>@{o%r3 +!/m#6,.) < ϥjE),]!m*>PC*8@^184sc 뮧|26&_n`~;`nNuމص #꺕'^ 9xaoޱ1'%yi& ug5}LiR/g +E6oW +Sxp ).=n{+ͅB )\|A.hS6uňC-z{Pmsr.2)E}OVcsBP^CMz9u97x7KJ_^U.EM# rJ)>%v# b,cՊ[JbH7=V6qfNYov+{}r<ubn-jTVQGzO VVExT&FڄI0~n$O)11Qsm75 T2HqQMjZљ`.V54ĩL+)٠`L Y1 xHh״Mܖ2q+0E^v1𷌛]qo70wv&}wSd*f%NAk̫Š^yoXm=]Piv"*fUg\+AN^ۜqQKcVդU%}Rbw(a^3jd䅺y҉WIn%,Jej-wj=koVHb[axv +j"uG=V'@]CJMnA*J5 rU{Wb(yoeۜؑɶ)#\أUvF+EFfڜKBܚD+۸z12/x.E,0 '2Sԙ2{]By{!~Ǜ3BNiJ)EI [(.$obN|3|ƿ2bMRZ9 뻟skT 9fun gϤ\jŎC+݂lxfeu#] ~F-eqDN˸);uIkoIǮ͆j/eo2~jXaIݗ ŨOߖ 2v=rAdVXU8ibBs|%taA :>z],e0R(ga;!Kug@1F }Aԥz/RHs> r:@<2Q99voIw#T*6꥝ɎGLYXED +ۻ^]Yn-?)%moJl +'jA6QoO.#{y81+o+SSJ +5`kz*1C>0JaȺ 8/; 5ъAvK_/-*YR`. -AגF~怱iĚeǡ?|C7Q!o!*Pw ;vv O1{_U:sq&%G$X*=P[&acf]I lkhљ5#mڙ{!Ýݠ{[DB^NtIYQ@ާV[zUYWNZ15(d +ʔ[zDd-BYH);^`cf5v$,ׇgcN%w,m]cQ*֠~UWc @Y&jb\Ndb#4$b%̪YӋAFE&@6YX/nwWr +m$Da>!5e<{x'2~+(%_RY+BrQIls㗌SJC!|*뺅/t?z%3~ȘX@Z1V'?jQGBC+x= +Y/agBQ)C\Dq#v|^t潆Гu:()䵡vN],Z~a̬mZqt^ANdu 9咴o qQrIi;6~SLr7fy0GqO4 +䑲IXy`UJɺ Ľ5u߅ܨo7+s_׃$̽% sR>4U pd?h(όG^N}®NrsNPIn";DN4\P3~, i_PРB +"Fq y kڤ! +}Eݒv6 f k0ܱdpcâ`ԬOԹ4y۳{~5(|vlNPNLQB@!g!VԯKzxAX'Zb=E*A,mCĥfr6d3J)lOm"Ș5Iڞ0 mtb?2q"KCO.iz! +lvJ:25v]j UoƣC`oy7 :F;4Dacs3P)}k_0zq&|)$E LLy9F^4ڞ5:eΫQGB2q!$ ,bqaid^B:D-QڀMꌙ_WB.l$3>sw[7$&l"b&p/ĭQ'my^04aT!v%>dwewدkQ`gyqSrЋ.N5|D֥]})|.eEy &OȞxH +ߚv@-)5J.KD9IyܲzaYw3݀1.oZ4 eݘfң&\lo`<09Hqΐܕ{R[D787Ґa~̆xI#fyN^puy=%\|P$_A} >@Az,[b&r97Y|TX[y[ Hׄ8(B[_ABoA[pF'9jؿq*U]e1`Xizr5eԥ:ނBJ%rz® s65˙CI/ +?7aU#vq_:/^^ޙf\Xq#˼fٙڙUq.mQL R:imO:{\8XL"lLg:fG&%yh9cJ5d=mHge3)J{\υ$|HuQ&N1`-ue[{{{VP:L)듲pg"w%Ȥ0IIkf^x)估v{ux^1:.(Y<Voί<Nk1?4[xKOJ;tϳfFbaF&7G|e-RR.KϓO&g~M%1 *jUmO0-=!4ln}n_jvRsq95Ǿu6='oK06&g[9y__m;KXW;vů='g*♘]YJHB–[%%yL5Q]\Tk@22bNV!VIj&f"ڃ]:B>>J?\03+YW 䚭ۣcȼ !qM4=.yM? +o{%9˯HL˚cSV"@LqcyƥWLA**iÁ?QG!Hm2jڡD\zոq=;<7 ( +!N~}(^$HZ_]qq ;v^+։Kǖٺ?Y{S>Y&(gQ+dgW^xE?B=wpۂRYEu%,j6b5h&lr|N؜lMB.ڙyg`l`t$㲦>$=*-_{?Z'/'6G^/{k5k}:yo~v@F3:ꄹ"EYVWӏlLM!^OB?uM_lX}ͼl(է?jrYFe#=թ9YK*&T4،5% '}#W_l&HysܒSԖuII|K)*) +!ؖU9_Gl s./?_WP@C.D&X# [Ô7^t =lw?eCֆ^LOoϑOM(,n~y8֯}r,x'--}G=xv +O ^戽UZ5UJ hX : 1yhhb=(K(q _7k?ܨ01ILh<v@GrĪŌҖW̓AgkUr~QQ#]W%e80(N{oL0veJA.N*P)~{DO: ŴEl4t(ɺuibKOˏ(6_1ӈI _T@퟊P&Z}ֱw3GOg+OK/-cZO?~(|)=!Ʊa9 aka֩g]Ô;߭><?2^}Ae%(8:ι1pۏ1DӚ=m?PrZZzڋvw)" $xUԬoOۗΆrOQld|r_9L?Yet2$;,)NsEY&mf= ?i};i#mQԌ2_ ;$P &ix,>K;>-_:U?&>OX|{cU|Z?[yJ?:6l봲O֮Ə3)( \/+C_6^sJ׫OBlB,-&'|mZO]_yovʘ0W_[ \o?XQ|yZD$׷3)k^|Jmfjt^U8nx{CwFZoBm~n*ʓ/&nu|&߬^XO6~|F9踚QoW,3کxjssӎw?Y?1Kg}yvgVEޜ7Elr:-feyM=3kl>»P?$Ӷu7ĸ{%)sFgI8lb/41JxBq]*Ĥ`l #c+Oڿs mNUμ&C_u i'i'ܢm_/C|||$D:ꈠ4i^V8k>=~X'?՟=0<[sN)Ɍ]*)AYʬJn.H{?\zز?5+3_oX֮Uw\՗6^|Y҃ϑy~汿DYQ-_;]}1Pg,] w݁_F=дoJQߍ缺cI;?P|ww?:>vkҗiZvy}V]#Nɇ[OK EU9Eџ?K})[=ؤ 헵'm߄o5~peo6}{[myI>YzLq(~0x@~)(+ESa6 ا_RZy޾i75Y{߳twsO|7?4.vf{C we{㍑F{)8^lJ =Xᆟe)9P7Ѓǥ;mBqtVPu#g-Cr,]'2J5 #Sܒ- /~]'}pGȗ=CÖC[_[{?phݷ}vcL\IpQȇ=7۾\fr]omW)3U}fmz YUͻwu +5̳c+Wo_nuXW?8/k-_tg }Wwj]h+Ph)-Ŋw n%$ N$D ޞsϽ`ɞefKxmw{4t 1p[h*t2֬g;yDY +(9cb C~jK + +ͥ^Ku2:ƒ-H!^޳& >2VJ~EJ|PT_~Rm&cܖ/QWg&%[[݅HuXKsxToBM5f/<$73%/)v "\-x>Sn"i: +0];Jޟ[J}NyKqAG1x\i~fD|PqczKW_4_ot|^n|?Pq6);Џ_n~o~5B%8Jpmȧ5"()!,Np |RMދg+u).[v 6f_ZiyN%jUPC@ ~z2Ɋ(ٞ>MV$<^lB[h[SO'/\ȫBۍ6d։Z^!F֧>OtYjxo*zӅ*x#* +  +<[|2jwcIdv?45h?:}wFgV' g%*f]U>jß]΍c߯c1^CB_n)s 4Z,fӷ}\uu. 9ϧ+3)hqwXO =%au5׀nlKߖvZx+%eaf <#eyURen8󲈩>͎OP!x*є-eAQb+NW)m7 +vIIJ[c5䷥nd2NIZf?Sק}$_^kGNJ=%jNV[;>TSMؓctW}lRNV/%z[eA塝7\LJI%'&=&ex.tACc>_ |yZ4|'/W\V_lvCNH'f]K{y~d$ \E4;JFqҷ[>]$G=VdcaH:j*e>畜|{IȣƲ`0aCęNpHБVqyLmxvtdBXG!u_JMm~\hH}6ߐ¡-,u|S-#`ȶK%f1'o3^tDĕN >9.tZȲyރZi}\Xz(!]. oGIZQˍvbvg76le擹cVDL2sw!rbSvynB@[IG*ڡK*z! d^ 7Wdk*y&mu!le ?N /47t~Ѝ=!Lxݙz^pu=/-xܙ;z 5+鶌fۭ.gMV"ȏ{O +6~_JJ86.FN:sLľk#,uQ{gxLpKM/& .S–k-yoϾs1.6_*'j +=1ח=̓H68/jv.)~=%32Z'.CkTlCӭ~dzT@| bfv9FG;UB@نQa@ .:n{ଯ<`VԜv*KJ~ښHE-zEɏ{r!$D%n]Kzݒlz8CㄴL,&/boR8[~HƋ{؜n%]9V]̉{jS ~]3u}),j#vGp!jDqJJk N(1Y + sx̙lnq۝ŞZJM5"G?ր,-cG<$?(xԏO0 !2,8&eBMK;lL1|m硒+8C8_:Ⴐg +c )0,5Ig.쭾bo@׵֜;{rb ;A%lr h-x`JG\{#&N?.5YyM%9$s5)o7 19\n_*3MÄ[̘?Q7:5(߆kϑ5Gh dZ7m+X騥q\}S7ULWd=#.=gD=@\Cϵz"HO }X>k$|_I)ڑOv(=G߅rN;DuJ[=徆hiigBvLj񚚄GM&11$GXe|M(CV:fJFvOg]CsmUB/ dZ\Ǝ!1Jvog͹;#L۸;B\n.b@%8KuȱIgֻD/'wwiQP +*ޮdLbtek?Yڛ@̀3CȄ}9NgSØ!0LoAoa-}8-{w]&̊ o7:Qq!ze;UURUYڎ1῵u ݝҚrkOG!kӞbO_yˍp 30#uQ,T֞/y }FXؔijɡДz+ғ5g?D#>`_l* mFܫ׌'\^hC|׺  kXY`g`_.}erc\Mc~1VQGWfJqqrLk:Mf 7R7AdCA):lEl }CDy]mRm+}c}RjFr] =ϼ;{.f|)PAZc=-1DU2߂ j6`#wU|jVto-1J\qkuz,3 rhi2mgd~QrڞGtL9T"]N/6zx)mF"{Y^k%5v: X}G 0Cn뻡5gR z1~x;C~|s]aY~kDz/?5d|Xj9ZT^WV=`b3{5BsIzRqLm?'^ڲ_[ȘE.x<)#&=[{lMu4j%rw;sCV{XɊ*ҷnRɽVL A^ %~ҼA_^<u"o"c^b~i&59{EHE>j(ʸ]{CЋ;@ao%_A_btII.k/VV~!zUXƄ=| Twzx)yDBe\?kNm\.n[ (a7d >gY֍x&ȍ r(4+WлAoćo$s=l$1Fx6i;bBc<lŎbO5?Lg"7:99Bt%DKWȉq_ y\ˈU9 I6%ny}`ߍVR)l mBXyj6?'2%%j .=/^ݻ cWku1X6y/N¤doQ`jUły냤y54LXy2'**V(1ZC9'SƻV2{}1?2[^>zӛ7@!n@9RokYώ<,B 2CDzTu^QezvEHdP@>%yr|}@oE}| yt%P{>E^kD|ԕg=@_jcF*jY$3F>*,)\֏$o W@/o}}}.@;^}^lM?@ӏmرJʧf4I Ҁ(EHw.gPP7( 50nqgsF1Y %CDj+E PQ *f0*¹#o/B8 @nA^O><ݹ|tϋRmYqDhQ\m~^|tJU#b/|@1߁<]ݽrwmݫ@/y Pã +yY1u~nD<@FE:|]Poyz~6#k@_=>/'yWS1$(0HZE6uТGz~ĵvIȹn GA_^=w%s3^{~O Ϡ?Wښ}esT?H/e䮎tE5 Qɾy!׹g<j".ky/wPixElR荅β]Yi8RzϿJ+N˽Xx[w@}qݓ砟_ĦdޮG@_*vW$lp1.daXU=T{u<&\Z?gu%ns㹟nwQɮc+N\5YC6 d=kUqW>y<@oށ^޻\޽ ۠@r֢BX}3Q+WQ}hp׊Ҭ+e+70?y޸<P\Ao= .^ jf%9Ƥ`T42h㮜z5UhKa|^no<@Os|t?@oݸ z{>mp51 YjC׵]tsaY-pxع@ޠ(O '/_ +#ЗWn7n|q \f[#,OVr1y,g06q7:*x_=?Xv|t #`?=6)T-of4 jr7)j>IJ.~ K  ϋW W`~]6 [W5|(? ݰ[+*e7eDdMT!_!9qQb<=E̍7sеK\|cO@L93nՂn"˭ MzA`h/ɥ7"r! w9ewu1vXcGV6S-ڝ\ST6z@ūI$]' 5ZS| ::.-61-9ٵeqkݥ1&u}G)Ǫ# $@-TO]}*Ŷj"p|N+a|=AjǠKVG[/H[FH&m"8`!< _L*"01(׷w'_ϧ/HȻ.+~9Ć{-m +i]`WA\e޴H`hjxŒ>>ܽA_BA/AwE *sbܺYoc=+7AyRB.!.$nPcW(ߕS|rъ`qK͂k ?U.u[Ptu~49vW?}{~by)߹+4hD)D?t`FY/]75vm(o&Tc!.+q~>3CL  З7r'&ʈDÑo8z7/Rgihae7i+וU4Y+=gsស@9Q~>~ J 9IR){5- ԰PY`GԴ :X-n9݌w VZ`FyjU&mr )6תLS䎜]jT p +.lMɅ-WaavfL'~eC6[. 4Wʶ1 +][(i`Uf˻FMM1dnf[[WԀ) u{jsݟf̽ +=es}=V [,TW(;mMhYmm丐7@R7զKzx^QeD}麢!w,Ȏ +Ƨ\'ޟVGPkR\f_j9Ғ%nmΪ#]Xu}],ffrm~kRZCWQiiԷ[f{kO x0C'Hz .R\QF h`D2S3ʞtZx\ja@ }obX7 vzTu{:Qr`vpOWYwvo:̈́h+#-fOU#'thQU53P]2E`&7G);^D1#`kQ PCAl"CNQ&"kJY0t]8ARЮg扮i0fWT"n 59RW*z៮7hPk +.tQZUT +c&kd-Pe_]jk. Kȅ|R]+e|-in~ +=V@V)`RȰ=k8&HQjQ0 +LRN'9UAwj*V5TW*jU gb\qj#$*8e󽜔JU3\P@[U=v+Q"[u=ͩ u{8H\jԎUpSۜ>IrM9+k?N8Gt%H$(eḢ;Cv1lUqP6 f"~k*b]MܛlQ}%-jm +>>N4q1ѧS\M.@aXYש -2VPR\MEiE <{Wc|UO6*$-1  +|چ؃ ݕG ug}r62NJC׻s?FslS֢éږ3JFUM3mJllKW9c|#QJ%r&b݀ugٴ"eRU1@}֟g:jQb'8۳۪"iYZJ\u40It@׹Bw(.d; x4C)>gӏԿB eRS jOS1DI聎XtL&qF9`s~PhӲPΞ8נSiӄAZe;{.@Oٕw4l&QJ+5=-v:ϡSJk.O +#_OSxabMq w8J5J~xb/&ds -y6%:g+Ň+U]k†U碰ƦW1!59lOT>hZ +l|e/} ~4ء-9vYIGOWO!WOߺyu&VwGXGX8{쒽I&bWN9\/rHJ3$J xM#<>@NuIY + #wFf@׎aU +i3յN-qԏ +E?'խ:4)ϦFi8uZJaT: lg{Haqxc %D f\fQ'Iſ8x\]FoO03-G= br{۵v)h|u##FoY<P"&:]#/d$xk؛cSTw}kahI%Đ.XF3~xc|2/ZEsCrkQVذ} HK+@-*~J3*Hp`KIi{b\V{ En#D&z޵zkoW]g^ߔg҄'<.[`W 3@&t0ۇbJ>,ͷ~pΰ9ʓyxAiqU&*",te -g[8VykspO>RSv5,!%^O1H(lx{!ޫ`0枚uc*(1uj R8vQAϒUA$zhjբ<"#ՕiMOvׇNl={2Ifܝ4,Mifqs=>$'MrtK 1JJVS_ͷĹZBC7{ +\߀"lƁ w}Gc(z[·}hw]E=Kh mA "Z+,q'{_$|ksc3uӟq7xP?@Z$ +ra s/>^bkَbo *p?QNY@ǰQ#𠭾 06CFse`^(j=kTz*cf(,~2lU]I뻊MCakK]ylʳ}ODGg}*jOfB&16@Ge߬@8|{*\ ,pЍ ꠦzmN]mUn7YaOÄW/voac7)Me}~SkKK[G1ݐ/f14}86hF8ǂfQ)NYϱDŽVH6_m :>B&s\jEEIpL-xsè=]iўU,25: Dn "7a6k#':4㎩Z$TQIRMwzhֻJ66 ]Я;b_ +SOstݮJ6"lPٷ#-c8a`~-#ņܗ uyܕNZ~b2~?>؇ *5PV=3o‚w w"J,X{7Pr@3dԜ:=Lߟ~JuC͎ژ:=dR`6GYń1~gًF׵bנIyvx00qe}>V2ZgnD2^NgBN +"?̷Dv1K ۣ}0~-阮n9j}uy0mW|ݎrEĤYQY~Y~)5z?ٚg@#v V/>0LM{eQSC/YhYizڜKnqr0!nz T0K = +A:HU~dCS?9i_(XאX%9Hru/]Bd2Ә~2ˡZV9ԀGP9 [O&ʦc!8859oo]o{w Gy.X(-wwTt#.TdE{Cߟ*ANۓ S;s5zJ/PPdzM̈́Gq<]MʳQT"*Uq>]F| +©"fn}4 B|tm&p4#ȵTڿVǎLi0fOSkg(thTPr`v9yE{<qk5d\22Hs^)H':5؜~׃lEk-o?|TC~Xh;\k{sn[S][R_94Дbh}Pt~gt ]C-<@s^,x'3\4萱 bL~H(3 cwP1|x +o*<1Ngi8!9LtLGsC  b؏Ǔӂ=fW{c\Bc .誘xcZ@ycqs8N b +*!}eyi m>aBUӯyЪX/,$:Trlq>aÄwֺ>d5EwU0)i'y[vye3픦608 ffLb -Y$$[$mg^<慖%-K::g:g{cgYCA9+,!zwOld (FU賕\qOx 4$#Un,2cMu6njcw!sԍ᪗*Vrhiwᭉ[㍉g}^B ]UJ&dX޳yݑ4\0 %DTP͸ꈦ)n=\o[ؓU@iM+Jn ++UƖ;nB ~[$Dl&uWXᄅ'+~1*#Bm-HQ<5:y +߮M4D]# k:Zƀ<'9&Z +j#7{Kyt{f}o%׬#yb-YluG{R=G["FĬ/X Ȅ H@ޅ'ahOs^`m)|_'{I[RzyaihE$81eVŞ,u`-@5po!Eϫ\ξQqKhezɪO@;KײίvU,b3~ċGք>Xyw#ʉX?2U7DՏ6 |6(V0'&xw왹7㲥/g{ҳP> y"Zab(~X}44ԁYrdcQ4f&PIE[ܰVӣtⷀXr52ݻɏZsp;62[DF{uXȍ> <2P{sm1[RxdbXG*JŅERusMW!| +NK6l->_vҙw;]z؂76y\Ƒ<4p'lڑI~ٿʱT S" rdwAOM0ue]81OaRI?=9ZҮ Wsmc ϴ²Y=8֨>6i#<:uc4MT}-z g5F`46K.e >U + +.elQV6fgr] H5zJ]P\{>N4,2rnlN!֧qJVU$,y_= Y0ލgcS| |gRWe{O%3]W7G`v$|u)E)ʽ9 ]"T|@D𡍎_sLѡY|9ZhȐc— 2(1R"(i绪~QK%)gg e/nwU\ܞK3*M FQɯlN33LP„NiN̢f`e>|dnn=Xav4b>'mxrdkKlL4 I>tg}3+yji7:8<$]T8fG~<[FG OAG[wLT讑X1 +w {+[rDkȒ JH9)Ds'U1Hٚ:FOI{HÍٺ9*G`;eG]yZO" Zo"<ɻؔWcD No9C3T +%g^`)RSbVdc@ t803y样(E^ nY~g=h]S--_C?׽Dg]Myo恊$dXY1Vp^|Sk~oS1uq;5; ny6. 5,dS\6V6՘ udrv2phPOll47Ȩimzk`+s޻k"FQہI(r!) +H=8,gX,-jڣj-7pܫ#oF WOVpS +`ufX/>#pFD~f(YR.|ZzqaV}r*L6ebh%Ol퀖^*y+@.3΍bR* -]TFY6%جYTR@N(\gk݋TОQԾo 7, {lI؄cD?K*pS&[oo|y&W7FQ٫] w_;_IỸ z֧[bXrGݔIDv|tښFZ0tlS+ XߘNocG ڻ]M,WҪ<+_!o}RZ욪кz1_JK"u7ŞU0zt5RDi<j&#gG`ov,sWaH 4ǷMrI9'ٷ> )U)ԯ9D whU5 fkzk1 +,:ikTSGƆHZ䲸KmY}e 킩'}=3b߱n y{'f܈T#7EGֱ2{b0R|퓕M yD.냬L)zKͻoŘ_vP"Sv؞y[v}c +gn|n?3BnPB-Q3!%*-('!+KsOVů%m4!e.RJnlbԊ~{O09ag5笝R}tm)f~~[HFxF!שׁO +iC`[1 tHhط[|d7[\FW8ư݈N9fo vYyvtYt9'f/[j[Gkﻢ-tzgM-ڕ6ſĂ~@.1 X`+,CC cק!qkqD_A;r[Jluce6&ldcKZ?;Qǎ{[VҫvͼCcsakXjrQbvs^ @)zsaWMD5sG/ܐQ1Зe[/VrJHK +rLT*{f!6*1mx::jT3W#c$l?2 +yZ: 14 ҴOY$d<2e઱mNaqz<#:")뜵w> b2vOh[P;+m>5 `s)5P_ݑLms0~ !g/.Pb#e?QC +tfHACŏ ?+zoT-.7\sck^=YV?6oO''窟)aFL ]GLIh}'>窷O*hi5E 5vmBFAYy,T޶!bM/W(eV_M 0ya5sh?G"~π! 86gVEa#6ؘyVB9D5m,=Ůu7>7Ğ>\6pS.z&c T`+4 ݫ J0u(-={!^ Y(RV1@sDmlJhkR2`طWܾk`69.cTZzی#] çz'1#uwW*~oC_9t4cXCt>(jbX|iRGA9Wl$&9IST+} ~ 9<*0Ww"e +J۞Cg8FKi[3.! &'{chu1CvbDE<4 rwMi.qsBm̾:6A%PK7p0HsLZBQV,)wXb&ᔽ|א<QT_Z[TD#j]#Zb[RfC\Y%hcvLdWHI@oț]7f!oW?),6¦Fu"hLSpn1wOEޞx&Hi>KS{}95w\b?5C8:{Ũ @#ǻۧ(7WE>{H쨨 +2 "m+Ep̾_B)h]Ckޜfw"Z%:yd@X$~"jGÓQB⧗_zDg>~⚮zV>< i9D + ul! ,$GF4̀' {` +ͭ|7Z|WzK͸>gso[xӔӤJS/u71IVB[HMfBݞ0pU1 Op3vBV=YZFp~YwccbOI*Pj^웠ZcmuV_{B*rEJMPc +v`pR{51ަblڏ7g;A/~< .LqUGtdG+/1aAg$z*bUPE,_뻪n ?l/E-Bf.۫A4Lx?A*m갅Ǎ~JCF\JH˧x$q3&6Z7@Nop9 aKXk,v3]Ф;j4ZW"D$;F+3ΏN[jňqnmBQS ;mK ͢:\u-d] ֊?,-~%Ua6=yjA_c7n҃|am S5ǟj.}E`VqR|%,A.kw#2cOCGŽ4FKOwL?D2a +,na%uQGO5cKe(]JSz+Qūh *} y(?蠰t!R~ʯ> $&vwE[aQ\QE3g[|l&;(%깥aRU3~L} EkQ$ )Y rftևp׺ qn p_VTzS-Uj§qKɞET60CR6sQJޜ5vVe:f +r8K OӐ* t[B,3u1%GA xT|k{{y;E7yj~fckŵl·/1nBv^bƷJ,WQޅ)R?M}k^-1`˭nx&/? *,Gd5ܿcLl< ziH$ATzE:{wn 4Zؒ?ܹ' ._MZf䔲CCKsD&fRvU9xAsy _{myo~7ʾ4~ xa@ϵ7$\Zߕ *$:7߻$$s~k1n6q7&DN2%`VFl'O[v +(LVYyomŤDh{zs`aEFa 0krr3wIpi^r>@_ݘp7 ,4%E7LyaeL*{b7d }RtzPZ-.e$9z4'aߡ۷ f|UHnw [VTPH.4TugţKN%oyϾ(z</gl`A|Og_2~vM6]}o)+y(8D֎ GKrQ^":!m[F)ɞi{;,]S-IRV +iyZox -b.K E&= OnV6 +Bl +.ݳB[?%~^D4f"W*_3׽NYkwa޺&x`ruGTR@F(v1QK1 Z:dDG)pSC U@cuKLǵT51xUw&i猢һ!9kCSKmbZS +s_eлD^eTE7<^ix׷I%س؜ka =#Mmgq?}Q~ GW}-#cbn~íK#\pLѢAe㇏VaG@@ϱ +nj7?ZѽÐ +fzC5wFnIYߣ2|[. 4$_JVzk/v/hI m*ýzѲ>zbVs6qde94d[ye#'Stґzbg Oı(9>5GizPq -B9&pIJ~Y9#L3$[t/HݍOC;7Ӥ!$Xu_.X˭\ҡv- >8g)^%k|j6T;OsO +j>)$c6&WZ/9?rO{0o?KyMkfagOٓd$2356_wvͱjbRQOD{{EЇ?Wpe +d1[s;40"%0ە5/4Ȥm)6#f6El[B.1w~L,h[uϽtszGG8"6-"R}^wDt, 9K^ru{x$[F1>Pt +6Ws;|ɗ{Y`>9`exUвgW A^}O}|#c>XP0&*fc<2zLX`uow*`İמK4GI+XTvט?' Jt"+ᔊ򳱽wSo `͋]-al(G: ڃZL_N^,%) QF㒸_ܳ;N֛ǎhR>^q4zaMS.;IY{Zjî!.8Sd}V4_]┞UΨUW:w7*Qh܋+7{ ȫ&2|#f^u6%B"6͵ݔ0*}J>noE( =;:/}`|ʏe%~'֝{oȴ?;{qhd_0w646IVPq䇳rNolU5:neO_=6E~GgV?v$Hf*⣞ +HEkʖk}伟0geĂ fERN6W}GzStQ+Tګ^ 5"suq#=wJww4\B۵1X?zfoa˘BXoǹCK%66ɚ&&jZQgeOՀdUh6~˾EؘAdD|rr,J` ґebg\1{1UQ"aX{Y.TKc:x9GͽdH ͢m W=L(EHM1q;0Qf&>b35잊RJOa~39kC#dF@cr.- t_r)$Gp +(.~GhC+VP] +xkPJO6[CF}?Y[?ye$`o9hFNmkOuZ_W~^n.?Z! c[o;:-isl驸^tis徎ёk7`ӈUaE(]0z6گ$,sH1, >·Håv>PJ/O 0JRD_2Kb@}^~>Le:zS!v>~F$(f!>{U ;zޛSElX]dc[_ӯϿҵUcW^y/i?͵7_ŘKW_>[Vį_N`nT1u&&şj8񷁹ƤYL49󚩽 ЁJɮP#ǟ^|[˞  1`[rn楮e:q9.Ho + c\PN >\`@h8 /Uzh&\WVYd"*0W6J:,0H)Y71 X>7ׂvMr+}sF9ͮۘ书qɀMYuK>|qYkuMՀ[Rw bcڴgQ@m=2^4$ $>)xm +v~_l{aI $ج)=0@U(5F +ҧ$@Wc shbL [N*3q8fU*~LQK|s ?OtNT{uwP%}u ^S,}ȗ룠[Sc+1~yPXj&yf}C5\j}hlxXq涡Վo zb[L h5"^C$NZj6,XDeoi|!5ֵ^LV> hաw@TƦgQiA <h'9een>6uvl!>|䶺?C}vw 1yZv_ ZۿSBR3ܤSS_%5Oa(:KAL7ؔJ[-c띋h|e9dߘ[(ɏO{ };m]5tMN?.)R./Չ~%e -`s=ИEXbh-jGo_"0I̊9Sɟ,UrӾSBeM~7&g=C8@O.<F&*iTP*FmHSζG6r6'ɥe&(@ȳBgI}t:ɵ5 <zetBP|%FQUD$(VM5Ry?,>_G,ߚ-q`JWaV]𤠌PQ'-_J * +,ꉙlep?yU.|\!m<C5f}Wodcd24NݚW.5|s:41_s]򋵧k eAcԋrϙ/hE9԰޸wL–)(&!9gn=EF6Ffnå@e|IRj?2Vǫ|kjՉ/nN.ʶM⫫=7'ꞄT t|GBv"cW70ѳUgln- +>91F`+hn 8JZIHGjd6@*؍71k켗N#NG:KXĚ't3N+O5ל ,c5њƐ7Az}loh^JA8~}w}kcoNR~Xb4鿵׼=H1_7SKϾenȻSr.r/+ó/8e/n(?,{j{%'_Zʞh! ̇OdcOTءZk|7Wp{u><<-UDτ<Zqq̼%%|F)~tFˋ!&;1(:TOH7Tݚ!erYؒ]c R#uLT)z7*Cb5.?Z?Ofaύi]-oy` +HU@ɤ[ѥE_؄ܜ*ƃHi8,*5k1^.ӯ4z0]@o +*H=䙹Y2{{w3vݷ?Wt豉>z~}Q:Mݔ[Uq ĭn4RRs#UT.oG%D/<7ɦ+I?Y;soE4|[9&0;jj]HC,ha`>Z-Ae.wVaa 4yA!}}2a:)a[bD;fi[rBQxw/1GЯ 'j-75Cv 2|cc̋Mo{ s *9}CB-v"nCª]Qֈ*1Yght˸Y<)g]F/wəc>h QjQ욧9'!ltCʯ㲍U)ߗ;Ôl8;0$ rlԬ}b尷$51:5O,J >%ϽS'+dmjw5XK1%82㴆[yսHYid,/J =kĴABOA itO:s3;jTd};bJqW ?Kq_]%Wt4ܬO,]5\sݍ +Q@ܭOA_BPE:f"'YܧW߽R u ZAWUpm~W׵. O[C ]lxo̸$gdbB}Yax(6{Eڿzg\2C/"\ jE4LD֝k[em͓-ͣI^ef9?ߜ, .y{X@&ͱFw6 y1~gk,Yflܚ`2WS*}BKɴO_$SSc  $>C,xP'V +lDVa":@?YLʶ=_]h8h n_Vq[.%e65^a'&b=4+w.2&t[Âk^,K}|APMI% <%}C΁͢KH[Nm8][[Wf &q3H;_ꭿ?E99}8,kJ J1E~95fȷvxg;?pxC',Y\vB6E!v]Ҍ\a[s)JecDޜLY$ |Up (hhmPUMӎSLDtm_olCƻA.d7-Os;ui?HyJeRkypmZh&m d6\H@, +4M-h 1{>e#$!t zsնCnZk{Zs{7e>XꟚo,WAi& +f1m׏rjS#{u¢19ua-zu=:0Q4&L '%X1qXn%6t[z?R⣵JrJzDw/7eKTW@GNRDV4=wvs9,w5y;w]b7(i!H֫ܽH. (]#u:ZB3NvU1![fa*tKn Z`U\^{5"KJ)tWAqI.)4VƖM 6FVE!P#w1r-sUGl쒟8&mͣ{&a{$ , *SʍfDPsb1S,a$*qG/Uͬ嶾aP%B_.żjY+:Fr]NO hA=<ïUEzq +f栮sȧh&, ]$ fdئEuNVsYRHYFlKE7$|Sڌb#i`~dԆ#@SZ>Ḵ܀hۄ>M׳-qQA5Yes}z +0NLYh=a16q38ؚgUDLԶGu-~=yy|&lֳaC[Wзp=OlФM1(9 ~jK&$Sskctc)fK7Cj:knyXMk1ǨrLѪ݋8h9X\j>nC&nzf즲Qi& ȃʵw t iX1xAeؠБ +x7s=^8 e1r,d(j7wy=3 ȶŷ,bХ\kEcdK9vx>B goC8fdjjyvThoGx8ߘa 4xm0ݪ/^m]+O1PrZeg5J:H9UB +!m8Xz,QԷ2: z[NrV4Z/e=*:lU9Mr90v {m#M &'42 oͽCKz=!5Z陶fjXT );eG/u}w3;gOw;؎* +HI7 *HI# H53;9|Wy +Z5X!Y{7}eem::mw=p4)[5ҧQiM?"o*+˾M|fGںw{_&@\ȨMsC?0FxY_ [h OK&Qu;rBH[66vΜbHUqF: m)K +qzU2gUKƢ1J QuVSRm+tIة3lBH7pʗ[SRjhkɋ~&lm:jjtJEM˽4^e`5{5vk;j])vmֶ_Eo֌'P + /G:ƪbSQ3FT@'*xȫVYcFEǞ24 ]M QW '05&0ݸ?עe5?cn 7qdV;wYE5@;]E-]MQVTO۴wwGyVT#oe&N);.}gtI z\`-uȁE8[#jBNJzxu[ՠp;c΃t~mU28g$.Z#];@_Opxg?$O)':rڂ^;ޮܝhs.#2"h&xc}Mj4KǚZ&),⚣mW.Y$֐.kwg*S &tXvoMlYY]@(H>T\m-ێẐ:!(2 ꆞ.AQmlu[vL݆HD(}p@,,țubEnqCJҩ5zu#KR_QA7<0[I";R6bNr^dNJ)ʓ8Z2 3Z|쒑Zio]zT݆6)GlC"fY׌76WMÉVٷvcU[Vlt=b-Cy:.B|f<Φ}\S>"a;;)󽬼@뚹Bj:l;jWzČ=$j, %3 x>vS^j)VIot ~(uW=mNQzb/:鋀]#MK:K@ZȪ2I}"&hD75Ȃ4{bE=byӓ*y[Kɬ bi$,TY{ӍWm퉚굡:hoưz _ t]ՓVDdPh#F܄ +U׬MnָhxgڝlL5O],¿V{ӵu}(h6- c sPʞr9{S5KVhFG]uvo&Z^/I0:GF+SԆSrv_F7[Nv%>|l.[ZO$ ƶEWcHfF"pįugKyr{"?ϹU^j:ړ|TLf3k媡6drhu[qU䑷JS2RNA+a%§\Ο=n_EGD MOcqNfwN+4(VЫ6TwVq0Y-f +W3WQy/}Bu{|a?6$"zi;g͍{=_ v(:m` +n\Nfs'3j/deP >`$OO@Wd(СM %K{j ~W:&Q-QtGx{ٵDniHlb7Mdͫ(4E{I1?#-,۽.IP/edYUد"LIkIdA9"?C'%er*Ƥo1\u3䇏dq?}vI$҃7P7@e4UI4g+*W=DE]Υ^Jut$w7wK~D|o]MH$=V:{6똓ڦRFeИ*Pwd×lԳ$*ʃL- E%%Qn%EgK9+&.qVSK=bvL:~>NH-]LD_xS_np dy b)ءUD&y/Y*3SF +Dtw}ܣk/[x E3ܞzue[_⥷~ n Y5o.QcwC,si$0qQbZ|52Pg`o݂ؗ>Qp{v k]_d(WM ^@c]jfXu$iUrgHԎ_/~_ѻ쇈8a)M=Iv媟npn.}!^ZoΨw[YjY˵%;W! z zR~Z\jy%$J\Je/Kwƺ󂸽XvQ%@L]H4c|lWe׷f⛖Ác~K &.w)2釘W{OK|n=nHb@m]|#ȪF/wgfmohh_F"ΗrLO]z BU}90Ux9xIX:%r JuݘhȆ8(i˳%iM-"+ s=CpddWno#y6=%pף_|?De~I9;d :֢*ޥgeM -àsث?_>p=>_o>>~Btn7C.E& +f.Y%%}v87Oǡc^(_W|cH?Hn/<@~Hͭ"+%Sz\AOӒmteooE{(b%o8B~x1-5dԶ+Cc7K𽼽7$WvJ_Wp$$\^&0t'b}V"E!1Q)97rѲx &WgMuj+.eLQT4(Aԗ mj-Qмh浌)"RA 'O.5b`+ƜXXa .]Kil/^B=(r87s)!{9+zӑ7c,źvAڃʫk~sٔL$g.{]Bt yWuug0ԥ+QZ^UPU즁X؆ X{wY4d(T FMOh#.H.hy[힒W6WC0Նzͮ#$p')YqC-[h%PopO>")2|'_ W !qSt/iBRhd˃*R $VOR7)2|mQ vA +pX5K[t$`4 +R%NJa:3i(\+, ,kll vm2 eo۹ԣQ6qՊ͘7㦞.^-+T1k Cq7q9!q5Z~12ᇈ柢@-RKe7vQU2,o(v0+Њ 'ޞ>"$V] B3ʹ},X&W_<ңn>K_b~7#M@U>:V΂8Ј_6סl½)2-JU)˘nFafS$^EaR56`XW b7҃f ߶<ܥ%@ַQhR/k֋GD5Uce&`\p>b򺹺|61^Lؔ8-'D 4-!mAٶP +/fH"ԕ I+}f%#c*>xN+9"Z,tIOˈISRB9&,&dI}+fD^1,KQS6UswJ)[}YGf׉VŜ)"iIYgp6 fR:aꬶ!v߂ϩ0 =| ,p+:bS"xw`YF_pArZ?"dՀ8g1;VDP{j} 6Y~oEӲF=]v^|Áu2B/SC-2{JaӢIk];Rqh6FTpcpI3aY%lԤ6.^rvt,XkQK5+llF88JՎ`F&晖zOGTU KE9:tFZgd?<עvM5 !e_ׇK;sUkbkSnVV8Fp UΛ%qʃ-m]:nƄ:jIj$С-Y\hQ +YNirVeD->ݥ/;&b7~H6uؘ Et[y^ùy=K*|&;D̋ EqϦ2Ȅ;ĺ$pρ[Rl;zRηArӀ=r{&|s`Hթ.@cTcKYїu +r 9:枵fRr11|]+,nE!U"J,ZU~hskp3B˾[Yku-&~ÖtA2|1ᳶtCse?IJY5goԓG= oaFT̺2fϊM6E8h>r `Yʨ=3.d]NK]Ajh@ߢv=-"fO]a[Fb:`} ;^Jr?/tg^dygYmF[7]ɇ6R *|2l 4Uڰ3WMװwؔiQ~0;lߊ_}%[}i9"|ƜN7tOfjvkC̪=[U(tA.ٳPf +do`ct1 yI +&+q1G\*iWV `ܲ OGv^%5SaATfIV~U[Q:J=:+>9:7wa!18QWSn){&fن ~*w_vbȝΉ:NT艓V.u/K3^lLUI6Bξ)gYA$^|>؜|#錰c\yۅ&CP )w ߕߴ iI70+"B961JMUE؊4Dk}g!d[]=9wYQf_y^?:n]^[泪(UİFldLWۑ]-3'&Ć?0r.f8Xkۦ_#L jgU΂㬠VXP}žk"@dP8p!*H 堿*yXg+r6큋{k3rTآo2I#f;l4`U}:Y6MEg[xombFfj 60(Gc  =2Њ+:G6BEK֗2?YlkJLqOKYS6TK>)]зzнd੅\"LؤCkU݆L( 9W,kP=g6j!gMY}C| xdJ)7OD_#xQl,bMz?ݖzw~{Y 'y7[ +L"xEQY/&; +6 +n+RxVF棉л~deh~%FF8yW"G[6G_ sRI OPq3~fSB[9.7=5vbDK={FlkU:mSKc24?tO79)오9oNI^EwE9vmQk:oeizNvlh\bT\WeIނ V~v6%\P d(>.QMEާx +P?5=q2LGI =dC,ph. :N[S_v5NG:@+e\ɏ7Uχ?ɑ ,iZվ2zC p`K }YV>%o(;ztU3b8&6O,LU駶K,H2_QDkag&Vn*!+4;]92SOEc؂'Ў>b2ŹO؋'q0Mxz`æZp;FL̶껮,8ȍe *%OvUx5>l=ׁOtx%)6dY ur2ov_W!5.IޫָR}#2]d/> ;zTĖ국w`.joYُvM\OɧE kUїB>,vuIP2rAEoݥzJֶ蒖kRoB~[O QRj7|~hW oMc_E\sꣃҷI;z\~$Ɵ(~FUPC`W5![`6Ek?) +|SsG[Ǧ3֊j(P)j ~q<_EU?K=%Q}8bi^$Ȧ7;=ױ[g$p:엑_fA.D[%1jco`FZ)!쓦:󙞙tAZ0֖na%ܞhK{lz$ 2Xֲ^+\=[5:~A&n!KҼ bԄg.Ai1]BŞwۊOVV55&~[{rw%0kc1wuЀNBމ\8'D|?SRMYĶg5Dke1qbښeIܔ-_C$Ⱦ=UOz`aݴ1Soٙ ,:zwU- pfg%O Yu eSy +zӈ9Q ?s>,VE1J+Tw6Fr[G=Ǯ>F `U[PCo3ͽG"xCWkvMр#@69す}Qb:Yڊ9Aƴx#$m0-hA2˺ߋE I=Y%'h/â':qVcw^U&>poN5њuw쾨@:̋˂\b{3ܜk=0m-2bLKe Gq6dm{=l*. ?Ń,2F5%Ѳ7ZtQ I iqoȁG_k]KNAޣo-4/,宱EXP"+ S-"tPxଈ{mKCG-pҵ>xȡy&hX%v?rJGXU{&\ +GY,ƪhNlh^LwǂcfeMetWɇV >d?٪xSDž^#wQwW$0].Ƭ\cSL $1+ZR:F!>b^lje+=Ɗ"lHg޹SmD {3py=^|U\n\ r"J[K>,aJT00]^0"7T]-d–E B T[) *!N4/`#ݼ"d^Rg$7]o6O}0v{UvG;d`2fX53;2qh%B7ŜXw'#EQ藷U%=ioWWȁCˢ_}ׁ<ٞ}*5?޷Rg>T*3w=3*qA;Aeo{#0O%ɴⷪ(YǜX1.2mtG KS]>YaφsF |ߎ̜&?2iU#IU_' kQ+ertxAp--q +ױwj%nK +|ıXUb& +v/HQ8wvdftG;Wʀ0[jd6Ғ|IY5/{k*- )ンpyC +Ww5贉ܷ9̸]!}UC>:t?;e-%k 彦+^tK +mxiG9q T%>kSa6 +{ex%0ӣX8mknJ5rnq '.=vrBu9X֚G} 3:vU] +p3>h=竃Q1}cB̉!Ho#o3=epu-1Xofff'ccG&7+haZ`-KT({*/fJ1׍59G6*YoBFD]I8#x/&WۏFOw޵THYݨ~Nj{Nb%dGyI9Elm*H:GliKB$~€#kYؑ2 xj?}~Tjq 1Tf_V̊^\,nD y#|x7Q Y:0r z%3 +Z=TNCNw{4ƅ?2ƮާCtX-"hV~kdP|:y*nR\u#3m6Y>IJ{s- =Μ \Ly Kv̸C; GK!8?0 +t:ʞXĝ)C+ oCHS'"쪑tCO[nz6PxГYvrdOji޷Њd%Qan)d[a 4jL 3mp;U9$lI< H/zYYu}{u垉S#D?PC[QGqJٖ=8"ce.ZĆI9&~5 |l&QgCf\ۊU4pGG/xnb$]?K.-uYڲkU0)Aan֫S'hE]T#.7u}>lʁVe6t"hgiٷrφ,XuK~f0;xĀmU>T캺"bUSa@UfdvtwEHGI0Ql <*:s[&b׎:EXbV @a3QwL¿;NFi#;1wQ c~g=\Wf}?'(]#"|I9NG]%Җ__ZTGUtF`V|hA'JfEt$YC%FZ濫3N "s׍UME[VNGuCK%{qr&%?1|Ty)yk^ ?gM;2a͂J:9*i3 SOreOu߸zKr5gMٗ5"hYQasUhEb]EJ6V-))Yk:Rn?P۪="V}~Szd䞣;%(>d?fo('f|7'M-ô%dzYxsq]W {^'fYAL5Yw ЁQxgepAy5.Z?)(rt&h#ج#ke ~=Ŭ»~{ׄJ8NԢS>kT:(Eg`|6 Uߧv̀v^ST},ܯ6zB!G!tm +ALډpb:au "P750uEUiyAǡm gn.y5R_xHv!j ~?0W&~ZJg:N8u cI/m ;3ϮߴTX_T)ǃ _*=0G1qC mm SCVz䤗;1rIyMz_d)q;.왫Rvf9 OG6BޤshR=*6ٜrp:FEM.2! +:bϊCEm]{iQCU3@$Şzx \7:Щg5-'S!~k(}X:g^Z%"jTF?6qgPx6 qKM-.y}r0 |F._S(xw?6ͨC+tSGLڳ'h6uTY>Z|:4VYٛzlFYTS7L5]EK *|Og$tLvx 7qq7-w4;'zYϡ8+Nw_¾,+V{+C\Rt씨vN(9c޳ۣͩP=cy |h#IY:R҆m%긛u\2h辙>YM6!bvZ:;8QVdkj؉9bl衍Uv2@-H > 'MݚH|zfH`T055g*a(*Zs|}e$%I}-sM?Ŋ{4"y;ƲL?KM+* +zJ=_-!v1is҉[fNIyjY%yY$/ќvMڷ^:U]J:б' 쵉9OhBM54KS#i!h=r7 ڱA>bgO}`P>%wNȜp2vβu^ =lI2Ww:`lq}݁z枵 $ pU\VAFP.V + L`KxZż&RoYg9ygÔm2)'{}iaɎdQ+=i<&[ s-wϲ[7.qZ*x״4kZ5"TnػѦboz̝3 2h{Y׀1Sd^R*G|/;v{+%5*ĥ$ZXrﵗ{5ueOsK]*תJ%CDIH {{dHAx'<DzoP/fvޞy0HBqN^[w^]]ʣx5B .s}?Om5,g~?sC_ߴ*eQr;l(zsRVQZ-z/[O旴s)7ؾ3qd1Si11PQ +I{Iag>AvEԟs>~WMm ̤vyM̧E,Ώ>#{38)N`n>{39%U%}i 0^'*Y7H<سq!"Z!`o;4Z%]oji!w]swhk2֔}k۸_ +jpdo{_>]_Cښom̏;sh|;qn-•)'޳h7?yew # {YXKlpϷ33g=ݗzfOnD8ΠCOQL5aϻ֍ N.ꛝiVcDEzgqŞiݚO,w+?TvN)ORA)ŕM0~VI=P{p'.>=KH,ꓷ?Τdp FCWa䵰X1 3n\bi!tjX׳p?/~7 Ul,3ȳ+ʅ߼"}E^wNE4,n}rsk\;=3yc_4w +~0;>٥˯7]zNGa"wb\ӽ2?9ǻ'%_cK?Yw ۙ7bm_sJ q}YZ-hgu!zzℜQ3]W?BO9 "z!iᑢZB 2msVd}2[||_Χ%W xXƜ֧ 7~]8`\hme p к%LpٕW@_hOwv:8DŽAEZ(g~c[Ƀ- +QvՔ-kjZ@cE (UI=!4l;iC;|Y71=4wM4G4lH'+8(K/,?4|%&f%$⌃YWo w~|0<߯vt,"nzY,qaQfāI@[P . *ZSRC|gbՙF=R yyREO:=ŬٚbhlA|7*Y̠i? 4dgF 6f ŭ/Bs ^j?3=+W:_ACK2gF|.<6kB _N8"a +~ Nn_ANEeǕ_J?))x;'O0q%0611ĕs TW=[;S=J\MD,XD=<1+(#yRi`T\$WԬG-*o.I0%~O, i8Ѐڸ91:.zi^o)MɃ}+龼:iŗ[;YX>=X٫}_r`jDӹof;9U}!}~chlQo'C." }|wռ|5L/^έVCU}$z:o z|}t}gۺ6Bi =@f>ԨfYc" y{P1sS='rTaDGnohAn;ƔW3Sěǖ' wz!k} +z5&os|9AG)lap_Ӛ:180C +!;|B֐jJrsRsa@6J63sy)D!ui+DjV.clcxc; z[^NI vE Jg'Uۓb8BTOi jhM;37rqD7yew Vu.j^o.gzFGJKj,o:OoNGm".&\xdTTW'rwWIk ih-! ѧ6FAs0On.8x +!# 4 "nD / \(UlATO+@]6Sޯ eӿx+ݣ<5s,fGBRhDC,b.@ ٭#`pIǛõ}=]`W_`+#zasK9lq\+skM&= i>D3u"i`ϰ[1Wwŵ(kEUў3k @?43E[_.‹,Z&'iA4 nMn}:`rVfY ]U?}gN F 1- +&4ilI.%|jDCo=a6u}} rSW۳={K/4,GWF<:c[􄞇(ͱ[~( E>ov`m{Wбѹ^ +j&"UN/N47.+^̔{c޲SCJ(ջ Ҕ[xuHMyC([|kU xt)|짨 쪀%ʮ +)On*I A@Ajڙ'5uTH„ |'S~VBI8ȤљE*ݳI tO٭ roI epb>Mm|+|rre@NoȘ}t0a*fBFˈ>XH%Z-J,2l`,lSJAhwQۙJWQܓWzzb`\y{T}r"4OI[!=OvOn\h@ ȳIj5ktzAnv@]x(U>9nywٻHKq==wvim~>$U=:n2$qCrdm͑_߭lIhyn~Bŀx%jBj>|I;hquť9"~'B,e9 c|󽗶'ZLu^8[@U@hEO,JA<`%~Ϳ^tٝ%B5z×3%foLA nF΋n55ry}!q>qhȄ[n=xg]Am}p̫S@>fhj @՟Wa+##o~ 0 j߲=u2xޚX2 557^s{ōx狖B۳濥Bn#>Q c{~uhe,n*۴Nj=sI͝EjM` 4 Ɩqs0iz{_{ϧEĺ9f{U`{HI#1k#;HI2q@cb +$Dk̒}KN" 1}y4$gvGMTD?(]ztw{^4|KBgYwo&K@cls[ O1gmLN,R6& eo~І9vcVDŽH Vݛ㘚%f_#Ŭ~mymsZ-JETEKh)-Mkqzx޸1 [ ފjНZZTɃ&4<$ז_|v1BD$, # p@C/\K]%V{_)kgCF.mm ~ng_ILv˩mc_ )DةIby= p?r|zys?YUe[ jS]|fvYܟrKӖ̏۾ܙ%Vf]BPKFGĖ*0s>?Z6uLW9~ܷ*0q11#o阝>wruBMs=)`+m;ӢKKgv~H,"3T ̒@g\Egv&q-cg"z6 +<\k$$BLk6^ +OܣmCJ4rWX'E= n8|v"h[ f>=YXGߞF +endstream endobj 43 0 obj <>stream +~h7GEC'^`°{x{ +s}uq3Nns0YfCX)&f 1%9z /x9˹q'~~MوWUaKC +Twrs{PWxa@,<20lޝ¥_*hжhaT2 M}<g$MR\d5%u\DZ/$D q2ra5=cu40G얘F Pq't;,Pj3&.09c& =IQ=8AiPObzԧb¨(U*Y~NWHRa &94uE͵gˎ|z"Kjx\}Z avgYIJLhn{&ۣ6}Y\Mk,s9$! װwWmO*b}\iDIlCO>IH +(xبx/#{F5f?O,x}/`M!-\ݞ)z;zipAvOW޴|v(b{gC"r.|sZfʋ,1h8(5-LxfYOZn?;OLߝ:=K+ǕgT{'&m$ƾcu=KalPкӶ>Y;13΁ 7si7qqPcC|b4`gp)?;%w}IP3:0r#ԧ7 *Y;O +ZdznN"ZzO?չw]gVFq,t$πXa bU;yi3ŗvu׻z/&};w ” QWrlQ\MoN9縎ݓ:'cP3V!5gvT[A5#cBjǷ8@Ȅ *-Q #S&!9 6nkLGτF\l@Ɇ.[w];2u_&EFcԴKMل̸V[c&|]@~1D1R~qpٮb{7'vgi6P[o9F1f@BJniwΧ_'5ƮT{SŁʏ;I)M54f'\DPAi|xrc+,BF%w Ґ, ,q'K + J9z]9a*5No(׏عbXJfWBI .@.Ǻn-JZF{~Y$X>~@y2y"-bvh[ *"EVٺ6)v3)MK7ߺs=~a6v8D3+!y[]QRg[[7 ,X@m,o~KZLCmjDx"}sQxE*=~hV-0TQp YӺ1%,c75ƐVHa͝-EZowF.X3nr +;W~"g{'q6i{2>fc{ _!Wp׽5r_]1NٽEIQ9?OJ*Ly!-W2ZC3wK?Y%zw۷3#=oe.qs9-07Vlݞx^tD;o]_+j4E[{NP0([x9Iȹ3sØe_A wAؙf6|tTEkٙ=;Pn%AԌWX•m#*Jk:Op5»nڶMolnS곶i!ka‚JR_OT/@/ p"/MiwZY Xy*rc𖭩ܳQ%g5~Ô]:=VrB}F|ltlq3 7[3v::bFZiĵUkX)% ZdXRlpyWA=Vr><yۻzpWQLxF_Ôu~:_ Dw:Ng:NTp')+1{3 +8Ԥ-09h%m$ؽq# K2697tưh+ͻk/ڎ[~g]z>A9^ +w!_|J` t"lD Y#6>ίv񠞂B$qAg<Ҟi`0*gEՙ yRˆo_XDII[y={4i`6!/+?̶Nj{&&4k#NO0~ȓ8w,ط@ +I42+iB +jkijY|QWQ*mufmtؾޝ6~7y 2Q5 +iAt [ɴY6]d#ccxn')堊RF+j&nKbPEصC_z p_N޳]=4cTĦ2xwP| +J8пy=CLߙ'Vz1dv9Yux!8Ǯw+n@t3[w52 [ *mtnO/oF%><0G.bJmTJJ?xq8o=:#jfGDmχm'Kk%z}X-x\WM[ݧT%]{u<0{/ PZp_[6+cb*|ƻo/+>q஄0aŐ ":OmH(yؔaph%w +~7/ ,q;o,q51!I i!3"D]7OeY7һaGӕ6]"TmP~RZ1}k =u"hsU֦Yˣ|rwP!cE;ppi!'EZ]T)e2[BB#%/ ܬO0 [+H^,wh)ݧ'ivD789&#i-7s3׳[=Ttsc(tA5A1aWnH%vy}gx+F.z*f!xIPxbr=ߜ&;^Pj6tqI# +d^X#t|[žOFlBB&.$LhnMG>p@UO,ol|rLm7 w@˶UTP ~~=5l8߫YzS.8qSk~Z o鸈==gbfΌ_2ZTaLؘǹmAݾ]|§ M~MeMƈeY]5-%zCt|j_JlLUyLA`/xuLE(['cX=gPsqQrV\c3V|_rzs// EXėj<m9ӛbZ*5̶QDZf"j{-؛iuKyyw;ce k/+?4+p+w0!=qeS$$$I=sg+LJ?؈,6ߵmwy ؁*bL~D+MR舨)u=o8o# WYܵk' x?u*Ă$RyHͅ'|]"HCz.vWA=^Oٝ_{py&edIoʸAC?;2("ɳ.ΣuFtNhǕ1쵘_& *{Kt[BNa27SrKAaq] +ިg Nj-X1?9anNk3jZU벧?x,\o{4b r0W1lZn dE,/ 4\D*ȑfG6;rّ#͎9lva#G9rȑfG6;rّ#͎9lva#G9rȑfG6;rّ#͎9lva#G9rȑfG6;rّ#͎9lva#G9rȑfG6;rّ#͎9lva#G9rȑfG6;rّ#͎9lva#G9rȑfG6;r"5~{x9 S ?T_oZl +4$Kx .;ׂER}?6>:"4e+I8v:?~pXޱ%3.ͻwJW]:vg/jՋWQ*ً\˻|݊V澼~Vs]ϻz)mzD‡t~?ڱO9Xiޯ:D +)0lVʰ<"{XwH'r?98_9{cyƔKs{%pyW=Kr'{3EG/\ + IŔJL 9EfOI#Vʨ5z!#fx%%ru`P)ЉiQp fZEk= iZ[\gW^$E 5:@^OĈðAL_fiջ3lflV i:Bq3b:v_.ޙG\qOq) =*݊kI2q-=ԅqt~BzoOؘ ii3rR,7+Z5#‹bwBK(9(gۭ7&{/'ZK녤I3wЋ#FD +M%32>^"L]A}sLh$A KBEֈ\Q: +z}TlOmm']rO9< (mR2.!/fv&׻s -qp5) +Vv%e>meZBULEHꉵ &,t̀LXHK)7f)k'\0/ld#$Mi/ $$Ak 7l0eSf1`rfzOBfBv +Q[z>1i(iN`oo iccFBcLh!%ܻ@hPnky'q1)s[;SkdB++!5~N4C5_$8ȌY/ -xs᫤WTRkRf1#ac6w36>5n@,г΁YvYh(-q95c`dLޕ1Zc +vWZ/f&䖸P5X06 i ōbfPGD4RI9ĂO[4\.m srvuv1^vI{&=X6`i#)ǖfm6n\SỎhmI %k'emԎ_G\i=#I+Ԓv؛>5$\RnhvY'ET6\;IR$e1,?6CJYޕPsQ?fr2һq s3yrgtHCvv aNvxtZ`wS5s%0,u=8e$6A}w%4: oR61w?+ξݛs&Ji;j6te`|lٝ1 U"`ƧwEX% 17)q7iؿ,Ȭ,f"Nl >PW@Ϯ;{Y#{V! +Dà,/հ\c0OYIY5 `4{㻈޳g*c5! 'uMLx\EfGK\_κĭONZ +yDL|܌Uk|*v +Wlq3, : 3c3g Dj"BG20m 3uֵiB ,kï],H ]Y܍G_xr{#g!0ޓ%͌Ҳo҇! jCXM( ΄ );΀j/jF[L-/.EVKX'm-/u8i1iJFkDjPPZ"[T^LC sMuPPjwf73q7OcsL\!E5% W{hqp i1qzXy6>V®袘 (=;3GH8p`޺rLaHU`olL.VTZNJjYXpW[>_wW>)e fdͤF Z*|Ů+spّÖ}u i4 +:mx44 åLldFiy_fNjW-~>yM yғC.pX VLL[8]D 䯍'8 (!9nc!3.50s#a&Bz.H~^ 8^rteNzIpf%{*fWxSm)3 6)Iop +p:3q#5&V$,^XdžՔM@:o?[xkr.eW䄋ڝYqR+~ +4}iq'qO:%{J8H)M3:#*\iHE(I13' e<75KxA-ۿإ"f˛;rz51`J͘)aUɖoێvo.Ha|[䥈c[\́x݀/AO wm:Ѹ 4Jʘk o;+[Nw$'qȎ;13e3C bɲ%l33Y*KrwsPcȥךy~kk'x՘.EK賞)n(o.hpՅ/2!;&,&;#\q#pu>!&'mTta.w0w7!Wܘ~)f宕Y@"o8'Kw'VsnA +jspBGAܝ垊-ī>+Xp᳨Y4j,|EcwHIICh3ГRf?cuASM}3hfUtd'pBoԔU^+B&FiH+/ Ud0#vFY t2lnx75 br[x[1q a# >6Wp2厼X)a#CӴ4$%is{skwקfbjhԋ>5h5})vE!̤U4VT' BSENqi CC.Yi햆^4W5.zIU^d`րm^+ FSYr)jd=D<r56GD.5K%KucRsRS3:z&p`)'hI&F6 @iAD1򩐍kUC,bLlLmKEN[𪅔_L Biy@K6!XփV~1ã*єI>3%7 +~«M>6áD1;sHXȚ%6ElB1EM {Q3K\jR 2˶YSj,ͽakZ!ZX]@+-OG BV89Уez5Ҡ^&ؚwLUu]#< 4 {S8g |&YDRw"3L 0؜XxO/QkD=B~& "mԕob暖ݙL/Y!=XemWB଀Ajv:5mz[7 +ATɪ* y@'?\5Rħ:F<|9.'o KFPK+ݨ!Ra~s˽cVikjmS|tP)yȺ{sw3S}4|j7RH+ 4gh7e9aSLJÐ)ܓwy{orjZTdoFZi!^ڛ7Uu~{Z);` pҠY*}n>S]:BK3B 5e{,h26>ca5L0IN >1 +Sc[(;<X2_6?}15MUUfÜgk^!km"iGA(aZ5uGlUҭqzZ,rQ.ʥoto^:|f`[EX%>ڜp6,Ȼ2q$d@S&O +n_[[=-g9&a6tLYnud ('<;c"Ϋc/OVsxa6T3ͫ\ꩼ66DMXhCf' =v4,BgΑ9 rB{3QyL3A^(=Gh=`6[8#ge}qgY6DIPU@>ǯ^^):cdB;Fi!r#Z1ͧb_2?{wU*qGU- 9j5y󛓄䐡եs !uDA> ;-g{zUbrń,\R!̏K|KB6| + ٯڝߪY5 J!k|vϫD,"v%53d"sr"M j6ʩ2Aw+d%{LG-dMj[Mč|,RUDִt*R=]U[f)Dx7p%h0}zݩᔛޔYh8e>亴NH Sr0=)fn؝GjDd_=JZ\pt^7\]]ԊijlGѝ B&p_hPUs1.5R6K=GAn¾nBڮU23 uQG/[~1&^=EXP~58DžGI@v_o8Qt[~5>{Ky=0>/h\ +ztu30J-oFuҏ"i!~tc so}s7: +ZՔ}`]i^ l;cLp KcѪ|5+!kcK.E|ŭe-ޚ gA3.0< ^y ;d gUk"7z󰽪1dIG$1X$d &W[>c퓈uЁy&!dd̾/6?\Q +&hM$:S&jI +F1>&I>=$ +֊׽Z!qr83<&2c¹bvEZLt,pU#zqlK-RƟz3+x!~WjW9IKqyYJvW+s+D8ۊh?_j/OD}z$/rG7vS#z!ݧԘFc:+.TltP,qOPSK ODU敘MtGKX!3dASs;eֻ~izŦS9agO(4҇훒 +à[SD3 uֿo( Gwܼڧ\⮭e,z!í*F%eqagZ?ho4˫=&ͭgmO:1W7ňfՍ^CmS&mU", /gqJ!wfó G?bCM%:}0շ-/4>nYJHh(Y!# ^({mRZ6 j_l1h?΋aAk{tk^=] { b$ȵVM^T.1M͘C N p^N<Iz00O8=ޱҏs5)pX;L4x{KefarBb r1d*1vN1wIV^@-!^>TysM嶼Î޲KI`mMvDղJꙢxugROUv;f$dlt-dyT}jūٝR];UҀi_7Uܲ gۆA֥2V\?x-AcuܚQz䤰GEnpΫkqflyS{F? +\u韴{O:^z2(=up405 ߭q]&2 ]࠘Y7`jZARCF[4#Gݝc6Ưį3NJ </4SVہƯp: ꍆAq5͋{R 4$jfL͏kC'ާ%+߃FDrO#&6bbGB=T}0ZlLгg}'.%{VYOA)0W:K:3P|69>lbWu]0>$הUV9Hkfx2h> Q2:!a x@\VDM[:z[|4hlx3pa=ԣ޼*Y\绳b鮕 9>yN-ٳJ\ڎVaYCAį憌\<ͩ>XմLʶIg 2Kfvyzo}Kk|v_()m}$&kAxOtoyNjT?ԿB"ky!D淲1s*:A|d=cwNۛqnYs}P+v%,<` &)~H}6\Tޙ\ݻ|m\.x\C>d-!'l"ȱeȞVTCs2#"1˭e#Q໨4tȭ'iȱn*yK az0b4'z8J)!S~K xIIbd6%r3ۇ>WH>9j;L@E+g 3 [G9ӤOkiRd)4cSCI`|ɸzvG(DIz\ ,ݛaT|9Ľ.5dD +q>=*pM2Q ؄O "fE -c{\ jz)1BL"uz"` Q0sP/4Bbu€V01JfYI 2G'V?Ț.bVdllCwpFZ~+g!M\ͫM*wf͉KAȝjAT}ԥz!:#E,>r6hbW|y_4HFsb"ނY.95kI*Y~V=5˗{+.oOO> d}Չ~ldM9l~園s\#3J򫡦 l1&RԂW#x2QPW2Vׇא}>荐S<>V1X4p> fu06ifc=#l¥:?!3:BJpi)킛޳3̋qhE3#54Am)XE!MuMD34Uk'o "8xk؛~kgaC&a,`dy^mMQDF,MNuIAy) {VڧѹؼP&6oR6.pCE'|ڭshvE%%ti6u{<6oLKucVsBV|>+6_?&l|vbf{2[V=B.vFY +YX82.lC3"fRq@7##5P'+R + n"-5ڷn8UM*ZxZ([ǹt|p<Y 9,fYbvi՞]ȏXDd_p4>o|y?~-f5n'l5/1f %*])n>k.8 >\ lx0 + =dm c y4lB&36g 1FIX&CIw+X YV _S!Cu=rm-hmx} Q , >]YuAe8ȵy7 ِ#v8?<4B%HE̼k`A&qmbIrp*U]55u-S>KM&BD; \bhTI/\u />d-~[zqHdM!`Cez +5{TBV Yؕ6j^9Ax?W̘U;{[3 0_o7M" #^f$2d=7 ,׊%*-`P aCz<"2< +^K=AA4$;drbb>4#{!܊d] sU08sAзK`^ 17TxfmDLQ]Ɨ )|^]t4,jWySZԿ?#YgbvN6cV]c!`ZoZ>-feb?^dF4ʝ&`OMa(hK;'ii#x3 ̋h ÝqJB@+'o8Iw".0,/= Q1=jf> foVC֍:"9 =:n%m\rʷYC~ZQ@CpNS3v1j8M|WqmZTw^& +UkkZr+ń]\9s|7==@afԴ=+@OQӠ_#fVy@K);8jz!d~Lj d4̰42|:vY$ k>5&gs[ҏnuMȿ a$lJ iX V NGM^HYhw&.x2ĥRbGLxr- <``!G6!$#Y<$YS=a"fZ6<'av穨ə-/1݅6wjG 8Vfyx@C]SUk%#g>eo^ZPgRKȾvԯf+nxQ>BfߵW? [2B"cٌ ^%)y{8d<>q&`qԼmE-Ұqꅔ1f[MN Iy({.>gB` `o YΉE17d\&@݄tRaK>j661Ԕ 8K{s1zgZګ#3"ׄdF̏Ck">$Ή$A 2QrFZ+<x s\Zf[4LKj뚼 fr/J#&mA& dW2Έ'ƀcz.9NzrzD*AFf[3ϐulm{<Zޥe]ZꭸSyy{ 5!D}(:d񞥵w#ne!9^#[@d^sg}[%\uz=$?@Wbvn:'Y9KÔjWŹ7%GgVZ^NrP徊)J[IL(i~#˫ĐWPQ}S;qKQ̭Wг7I1<ܒӫG=A#3Ix&&x&)8ߤ*=+rOJ6}=gS.51٫E֮瘐܋cA^% 5-"jfcF<;MILQSRp ~-UJ^de{ +S ;%esup5MpӒCJv&.n^577ŜUZ iVw曤$m nђLdgTPW+ +.js`cw=E9X4#ۯ~'It85|O'n+iT/;b1H 5Rymk}qgDC∩x7 +X#ܹ~ݵ.3=NVf?/c# }z7zB#AYFW%?>UyyO{Gě_Niևϖ;+#1Vyjm1Dmtqb樸r{\\ sU=F~ԉpup3`D4 +r/-9JO~3Xqe}H ꦴo;Ŵ\{;3K.j$P p 9I)qg\J^0}[vVZD.B>ltù0[dM)j:NYlF$ex +f$!~unsdm7[R0W)u33:p7{1W+up/~k¥7Ks/0gV:(f_aάwonRnAL 2K'|5*J;Г:I7>;.<ڑwQ7DŽ+b{E0J{{3?2rr7f?-i{։:Ջ=Xvy{]1i+7wu"RK tt@_ovF(Ik=W=bgV9*)_xOkݞy;km)9줣=Z%J!aOAUBK 8xQR@nS + >x\{qkkA۳_[| cOyge 0ֻֻpWm/ ~Ttpt'0 +~r3ps}n]řרc'?rԔwh|w ulCYcsA88k˓_:77GE%ߏVp8F)}[~}8KkԔ9w K{U̢> FpwQ'4uߛ<ߎ8]StRy78 VzKٞ6pl-:ng]U64lK;p֢ͤS/6I;Koڈ[g7PgJj^j/ݧD ϻ4ܖWK{8һ3oWU35ډdy'N§loT4,Tծcs/346'[-o-<3BE̝qzԉ}k{=NN;)f_b//'27ږCo.^<-e{HLI8oM i_jRmMgO7VttD_Tyk-+ZztSK'L-%o,cn&40dkK }ve]i 1e?wZ/'c=`5a[IyUrؘ1'EmڏÒoiPO+ݘk %쯋'~?:q bxߵ,W]E<{z{] -i jsmEUeUڷ 1f}LPHt+ SҌҤE_<N%4HO{ސn.vR~&8HqoT>RY)>lBHe$9ɋ y?Lӿe~Ue'üCw#Z;UП͹o|d٦7[}4vkeoǟ?//gm;f>pMT.FwJJFH-$2=J1թl/W/0 ~15І:+ ^,97CZ4k]G-)N1_n~%䄀7WwߡN蟤5%~3-MJUS3w0VҧϦ$9?!YW?Bqf jxAט&}*1~CGһZf& ~ԐmIa׽k5T=tWSC9xgDP v}}rl ꨮ6MU~{+( ǫ p6smGŷezf`h}=|CڍbGI_IU5iY_0nX^R-}ܞ{Sy\rRrDPp`\+ÿw\C{hl*y*{0?˩ ?f\+'{gTX =ie7zpp_];_~2,L>`.Y| ufUqcKRR?ѶYlU[x`yZ /(rBp\r'5ZqB]WYJ ' c.ZLO{w ONj +>/;ACcA iyafɿvy1-LPyɱk^1c)kտjNmmUҒ磻&|G'7Y$^|sdI[3~7gb]yf̨s\J+X8{6֗w-maY1C OE9;"ׅr%L5/X,uo/G>/FmdM- %Ud뛬cz%n~lằwۋKﰗGI>d$q3Rkj34e?R+껤SKmW6)E,d 3ISzgȏ>|\u$H9V4 i+WcXZ[}S~}k\N/'\3;Ê._E?Po%_1@5js~&~T|NҹR7k$Jzt8()Fr`eˋG_o}e{[~z@R&Rjt=zgwG/2c\"kU4It컲Gk[ ~]zZzzOPt 1-Y ͍Y?-wϸ& 0˯}AN>W;UhScAۓ-O|5@wCq1vcLFZ"ހzR]Ô(_ƺį^e5MMIBNZ~ʛoeOf4,eT>vd%17Y'y|iAۜԒ6ʵ_Jo. +HS_x@_iѯ +I?Mu?,.={gܔ46b:웲opg'Kup3}pr0~yO`Zzw?"󲫟ӯ}[~UXvkA6Y 1̭?7s9Ш(_U *oOTtMs [5F涺)۟"7CL.|>p27wNf}1-52)5[a.E\Ptbd[k}bh|8L8=$}Mz|X+Uբ>zGyGiyYpܜB;񳷕bCOU֡~}|zw/=Իkjs~6^u*"RG六~vz?7ڊ>~B07Av⃕ +c+:Ro]M7k[,u+kR]&޶W^X觧oL*을Ӳog)]b[0g^.uQuo~ ^f<B[qCcڷPgYMo/;k}'f^,<1'벫]o4E'm [lJg wk OCns +6G8yŠyc]A^N^Ϊ^ߛ%e k3Zj+Uqg{23EG{̵򫽜{Ͼm/\N,cߔ,aK' + }6E\c:'}Ӆ.ï'wE $fT…YVzIB{TJqO˽ǎq v₹9.кkA,3M\ U;2z)n׶ku1Z~mtѫ@53?^='\46A\;0-ſ*/?07N$}nj(;I{C[ߜ_rq3Iԕ>q))YiY,w^pO}~GJR<#^mWJ~sug[蜀bs~sMCU-\`Y^؟.,_Ttt +V:ٙ#Y'q'|=.LVY[|vj*=!e b[#9<񪪥ַW&3ҿQ5Z ^[ynsPJ 1f/8= J;d}Qx3CP)h5:7Qŝ}Q|$Iɽ/x׿o/83qmmepj3ԽZRo)Kpn$K ' ! vf~7+\{쳖{:W_G"}C!bu)7\Kִ֓ <(f߶q`_?c>)c|ܝlcP;,:MZ/!ݫqŶW]eASD1B 4m2fz1yQz"Zr]3!ḄeKKYׇ߮zo.+6f@8)fwӑJ + >Ǯ%8,t/!꺩.3Ы#r6ʫe8e!;O3Y 7Gƚ#P?&^Ʒ_N1"h%AC,߳ ; +BFi`g[CY/ӈ$?À[n-}km@^,d~lBG_g툙줫K_RR&RRģ`VsI~#ث]/ Z"M"?4VFWa? C >1])_N#|gbWr*8!/ -$=Z')1-􊡽<uUˆݏ%s]rfTz 4֭Jy{ҍ|筎C2-AlV~|8!ASx\d,b\Gɬxt^1tkڜ3Nb1:\KKdJzWʊJvٛ}g,=lRuRf{YSIcX.x1-J?ʼnd " Į Trq jW +l(|#G@䙇`. "#EmWoBI>͸b"uB]) 94 V!6 >q⥱!/@RqyԋJЩkZ /GPv:&CX 6x{w2B&ljYI/Y/W|tj\W0+jh,cW_/u?^z8ߑrؔЄwVX_d+*~> ؕ`Rt2 +>ۜpl6h)^ǯ.MӞ5W[!좈5~.`ry +А  +ew&8Y-;~ДxPs9DnJ4?3=JJo +LsZ阫hZ,r%Dڇe}k}AN5:sOG! xzZ.Q\r$ IN%bP_x$pgc +vy~k!=$ Sg,\m4&mJ); +f{=\2f*tkwdAҖ3=ЌBBw;o_Gj:M5-^C.< 2zg,tsb{j_ɫt\]I앶t?-G/JkQ|Mvѫ]R2CǺgVVT+oN0B/@xԵU |+j'!a x[è|UL(~jdžA ;ϳ?W俖kL\_eȎFr6K;hu1$R]؞pW= ;S~BƞfdylkÅ';*Dڏ +]eȫ{egI( t,e{WςΟ> ;p 2P1ϳ?YTD6_OՠMr;KppNbcc5iRcNb}=(񦊬 Ф +>$pdt"V!.vT@!*V%/3&$754.ɬ7?y +sHzU̇Ji<2l[ut5'tۃ_@]ţ}ř>u tyPأǩ)S +ka!o=sHY$2HQlSL,haZ7̓=T,s'Qb?9>ilqn%嘦!zmc +)Aoi`M-`m vQ߃ɼϋ\T/?==rW:I^X[MpN Ŕ-1>ީf"z6ޮ&uxgouBǪ +>9V1|L)xS؄?HIq }pXZЋsչq75oP6Ŭ~x\gQh 4K ӷO Br|{&*ɉW: zp<(Koܢo?x2iTbV%<6 +,JK rL[C))-1-u@wiQg"/~aqj5;J?2 +vn + +gDQ5'7OQ:" 49XvH({Ч@/~+K:.~ +_~7@:w:PW+c"7.$jAn7))'~:5(3 (m(sP|\=ۉ΋M]}!GX5׆&m%=OKa '!! A/^ܸx 'A/ |g~*¯4=3OVJߚ'QK nb嚊q'¾?žx +qE勠/.tK?`_<jʥz.RB6\Հy_EDG>x*KwA~t+ :y6pׁ jf%M0=&jrruh<` ! =~[Ν: \kN +rQ@[p@Aή_V$1f*<eU>Bז\DDHx3w+@wPyxIL]߭In@Q 2Vx>J; I:c_]嫠'"_|—-b mJ2FlXf*@d5zn`Dt;ˣ}•.`#lb땳^ *Z#aV8&1n]Go*D H\,՗{5:|YE.toC~mN2ˮ2+H;6Jԗ~;Qii{bQ6rrk E5;*VSLkh80wxDf5 -'e.à L U|t5ϥlbQfHrl^nhޝ9|YEOqfi59-d&!BF?.|y9jx-f}_OII{j}W!VUwdhzj©`]5.tOW!˦blXeb\Ԛe"mrv%,{܋* ћC2Ǽ@d +ei{W5 mӭ=k5Ecn5?/X L𞑆:0YGF527U:65)߮easZ}zDZR񑑆pif @V.}6ȇ(FSU +Jμ^rnuðwVԴV/5V\L ]:`m49NXꁼU!~wckAh~"!<ږFe<ND^<#I*mBRrW͆{,,3[e6_.uק=ot|#\4d[Ayzȥ#WF˃'xU1veЬlmTSAEjM==Lɚoy1[1a;qgxu\e:ؤC +ZNES4we1,5ܑ2fJG qIls\Tr2LGCpɹ@Zf/x,4`[Kؖ`wesv/Տ=CcS%Jҷq's6 67J-FezunMSSbS YO{[C/[ Y 2NSڕVQ_Olu8x#S{5;| yWD5UnO]Ew)ie#- $l{tBcvd]Ȯ +A'5kO垡7ֆ`@>hqN9$1'QPņ æm+(%V91ӦbVg8Y꿛_۽ḳ Bѐs]It Tn{c$hG€;][jJs_R7sEV9 ,@\bQԸo5DK5@l<ѡN%!cc pc 鞎 +[%!==^pD=KfR,mJ_AUj7()h LG:e[E_RVlhʽNԙإFC?[fF + hv.#d1IT,fWMܟI3FyUk˵)ǡ@oͶ%?ܖG.{Su{gGZ/ۣEA 4uA]-t1r7:iy##h@n >-W cT6bG|5X9/#hNPck*BCErMSbdZYyQPdIrv_s4e|ցM s*6bW,bl`J +ĥQ4!4fseBUe(x( +jS"JdERjqkJ\/UʣXIbU ^{=>е^脆yZ}Эl +zME7O" +L?:𞬡.`}`ErJO+1W?hSP[bVU8V% gÑOmS\Z\WK.;2ԴqQ ME1lܡ9 gȥ| !ġZ RHEJ_iTzJ&WlL}3He,IUMv- |oŁK0УAzut;DM!a澴HpV,¤0)iL@.۟jϢ]ZĦ:LrWڝ!«jA5%lr_ +kB' V`_С㈰ h5n-ȭqnﲠp xڵl:ԩ"znbu|Ggx,$R3sWQJJBijR; +J+TWШoUnEN֢o"R j~=Yuw(ZK|]֭1tԠ 1/E(%PYEtXO)6YghToZ*2c¬2LEMth){\[AEn˶91R[BNb"1! g1&ica,,@sPpoHG.9ҢTe;-1(,xGN;0!6ꙩskP'N_.&y~\ҏ,84մ:b]O?&;nq<,]Vg\]@;}=U$0$,©c>1 +NP u]GDmOF9UI`gn#.n rAsDɏjby6ehrhY)zsuwgБxўi/rgTt]JWhJhy `aS˧;4g3$p ߞ@ m1?8lXEff\j\a7!r:Dʡ WrU{M|3$ujEiԣWke]%kx؂9-{Rc7:@,:&HosKomSAؗ€nKq;+ q9@ޖ8Y*~7~Hی)gGFq efksoMKw R gCȯK)WÐ0`EPCU ]2\S0ߝ̮9RW1;S ".~STUش!h2l񵓒rcs:lf\$?ĭS1S۰.hʿ6}b{J!QĮ T~NbRMTׄ)#S_1_c|ů%97};*͡c"jF垁C?ZjUjd*$d[C-j{]*i{1_ٕnGc;)ɩtN9[IXi"WkqWW,B!:|h.0~)qiaB%?2v8TZtTW~e:ۜp}o r@暓nfp([`HYWIXUTMqj L F?yCt91G/?MQҮ1dԜ^nx%'ӝĤ7)}z?Q}a:y cIpؽԷMWoKvFJ,V_&$G֔yHA5u@r>;!(~[G .<#cPnc5g[{NXcmT'˿펗{"@O9R؞bZ%t^YF/H9͌0tj=ƶ,?[V̓o&#{h3Y];7ߚ鿧]:6*7& iԫg}5Zb#ZBJgW%b):ť3 v-fU.jm8 ~x^ngYFK%AQ +9w6IꪼO+<2%L.j3m%#l]ŀ( ?4CE-D@_E]hs_D<"ʭ$dm*`@idEF-"Yl?2ɷ/ _Y GLP3 +6!uB8]~zq~-塚{'d3W>L@ɻLkh-o@?(xQBsqܻ22랬hO[iK[hM{@R=3ώ|wv%WjHRjOFr3ܡKpJ%V1:|ੵ,h_E`·_$ h MhX%)om5Iwih4>˭G$9uecj`)G&:ЋdiCfRPrk9!V14d:xE?cg:Q\lS!3bbY(yvqn4/X|$ș0 (YEeeཡ! *lu@`ZiGNrJpiv SeD0ꄍc},,TRV)r^~YAObcBB;+>iץgMO? K@01e5i/sx5VWKޙG>Ys!5M-?4 +w'Oؿt„Ks-Kꊄ[ꪈb`,co4%oqNɩq#\j{;Vqgae\&;:)^W&)ؒ44>1AsihVC2ZXʶ\SF6|m[E\*4$p-0?)Rzm{rSCC΂Ήݱ赮 +V92FaF$wh\48 +]c9cm,j:%0 wFʾoJeհevjp=t\k}pCF(Wg?R[ʃ,mDf~d^jLkAŹgXulT~tNyv=# 48qn?Kս@w*?MS?jB oKY7ĤqZҹQyK[1أ$Ĭ Cthv9`g"bGՙ /}#w\r/!^ԁn, ÿp|)AqZ馌6=@Gi.|(-pw Bɇ+/ίRFWNHwWϷ>b5B 9BV_QS6zK^mٛX`1,w)0)fz @oAޯU[~ߝĦ儌 1.F[Wtw~wo Wj"/Z#"o\ xL?Q?k,ޑ_;dB}=ͭ2z1 A=nGF,u̷>]W{e @yRgfk= _qd=BWï9.gO/lIh9.5r4'gYPՑL ُL`weRc[ͣkB[h]1MnY+AJe?S7=Ǟqc셎7Իϋm9gR"Rdئw`3 ,v0$lU]6GtSgA <44&胾 YD!#.>k *'/ʹO'[`z6헩'px1keA6ohhA~3KHM3*R=(a[Ę[N% :I nZЉ%6?9NsI&ث⠭>׍ʷ]~H# "eu/ffYȽ)TVakűioSh}>P֟x}0\sƓ푒 L,'m;!٪} :PuOÏ(*i&:fzW&rjj S߮<=a)ebLye'.,5f-6Ur.5[qJnX\|s}R~~踽>kMUBØyPIN>ת#Q{)vF(9ۃ$9-ZcA'䤈]E/C?)yc}Rpq5 % zP!S,U^ß'/ S, "ER? \ޑI(HݛMu]&Q"8LdIe4&!8QJSv{"6Hņk15sԄG̵KCNEW3Lu(P35=(#+vsp`y7)fB%*ڏK2C`_]iZN ƶ {_]"!rpZos]]N̓mU[D~-p1`+GCVz? B"f u4kd_ic`+ӥkqxnkd$Hd,#-~_՚S[ 6{{ϿtW02?IFL6> 7n-s0k͒J]LK1Z6x$',QzPhّ.6R3>JYTMg f&l"va|tcỵȥNe7$6MƋ=sH^gp:6$x_=-GIi~wiU{n}ߙS23d&dR'W5{ +*Hc6TKS*`7ef}{qy¹ֺYwϞ^YrM\w?5۳?Gp9W#<'=kS ҶMiˋ]{g-[ :rfR7aG;vvO={@\ Ĩߏ 5><%ؠ*I5!<#`E(b)j}6bm=˧T|E16qҟq5QWW.{9* +[A@[[šGRooN'frץ3ɟqt* UkdeĊѴFpt >Xyyd + moeWF+of X/w7&fJj?*|G+idwVp5;F|Ӈ6)>ȏ65@(@>;脰R12xm]Խ;)E3ơ3΁kSαքcarb2*Hj`̾;i |˶'FmYG˘:[dPajbUw l⾩;eclCx1"ס3:vݍXԳD 5 ~:7 +}{)V,};)8l|a"2Ȃm<\Aa]""KdPd += l1ǎkUлS:Toe[,&3Q+beNEZʯ1(^:`><4_P(tsm[ΠX#656:!Hk Y{a'GDڲ\ol + kђpxgR!sצAK=W]hBPMh8v0;dc#kce:"j# ;G6pٷm{5ud ?p;:ZgX=| [dn~E(~}Ws:lGT/[=/|Jt sK)M_?ڸm-d[ k@[D^o岜2nbgGu`$Ƙ`8vpn:00{:&`н%`GG-R;F`]ڷe̝|QD+ w]1wV\uS{[2~Df/>O)<:9譹Ggz#P#G ++#ylOؚGdiWv}K@&(t_ŗ*z9BEw'mڄ +UQó#jD-}d'!>:Phs[/iy X_+ꭅGQ NW$Fjkҏ!3JXJ__>):M=j:ūzMpms6 +Kq|fucWEh, +o9:a PmuQQGf9aKHȎѣJb뱫{f뉚ٔHL݋nG[CsڒS*g_c)t{FDC`6G1G6~?W1&̯%T{{V~7Y|šc`q6t\!%jЋkJJRxgixm<Ҡ9t>p3PuELU.4ʨ3Ӑ֞[;:0 S3M>UKCst`Qعa|Vt$"378Vdա*jyB`n#~ٜ=r^vʯ:I`N`ޞS.yޘAϻi:{)i7vO>Z3  k0eG|؈nfK6xٝ5Q9D63қUXzImϜ_.R /oϹRcʶ&AbT)b5%`3wIJݪ\䘀9\܊8^A|p:PӴ{<qPLHT$Emi[J`V˥+cS%볨"*-%}r|ǖ`*MOX/zA2+v!: +۾͋y1DaMexdyRea}/}o~;*Q|gyڑA:\aS}JDs~Ek|}qhaHRp:y6xNྛ%8A6ޜ6 XȦ;MGcBKĖ=' [ĖGZ^!~C Xvjm]XsyY=F>ט|)l,,M-CC;n!7=6RwQl;`$YFՇNF'Ƹc}T"6=aT%k34.9{an"55Ol-O̝%x7qX30rw=5Xía凜 c3%l͟IOn[ݑ_h;У+vՈQ]ڐ4 +̵n#s?䲛c$`]q0_x~f\ 0WJ\<:Ԑihoiy5]s?~ꛯ{Qj]<f+8zlõ|r?:R&1K\kp_H"8z,ef_C9'P%r-]g}8>n@ Y2>bic2ħ$aU +nh!Ef< ~sǗz[~E&:莈SʂԶ7ñ~ZlMX-uHZ^zknw^f| g@7EL@tfCt_Pۚ.;,oS-"zh&7g3t}9ELPʂ iltgW8֤oai)t)'=iGʎ*;A4{ko$Oofȸ.jc1~d'u왙Mk8)]uap"7yo߇FTcቮ+ǻzJ`G/˝UYc~dUQ%2;ykv^Bn9_Ŧ/nL8/eh#cYPz9*.bw5c#:6KªE; P@,tO5>݋}eAP N&kG5 ؑ~@жդعwU/Ү]}ۜ~k^p, :zX~.:FG +JW A\"wWhkܡdm婄R|}\ v͂nY7%&BtKwu ͷaȫ!dmlᣟ+fh|dFW@ +i1E4ga6=}`b>٩{+d1U݅[_OJnRzɾ_] Zr4d[vKM/ݿ@XzX+V˶5 ]d¡޵>8T9Z +b3OxOޞ?ЖtC_9td皟O?80!qa% -cҍQTY"ShX[2|D-uI_9+njn;JoS4gg#*bl%:4 iWE?2ڏ4(*y0+]󨌟 Zfy=O(:#A WO-Ҷk)3$E|).sOMαS8TW%mo}=h SshDo˱[Dٱ{%ևe"hwW9 ćqlybyfsgdɡ_ CПք_!]?yA +M^oH2yvMR|vm$M!r?룲glGT7?aܚ÷}\cО]%(w՜ @ޕA +BJgí<#*'EܨYUwf 39 hƨ +֣vо +9E~]Lؚ[_ְr ;+F1NV1o_fW3`ۚ䐺-9'ڒߔ!*Bђcfv159H7Ʈ]n|7˽0AܒhațAIg/,J(9Un9ќ|-9♅%oηq`Yǹ[?>p}#-]BM'Q^i[cOUIGJpIDoG* M(EW]UsHA;9Vnx#ɾSiDvH֞b^t +I D5収VLGgސ=6=qW/ /WwVƇCSMVA .=ӞU˽yhֶ5pIKƠ]AO>X}34 801aO":*ȥ훘V4Jn:F$}Zb`L_F70 7G,\4"ᎥzsZe]Sы/.y薁[5N3 CgsodDcញW{sxKaf@GZ3~PQ]$slXci|L,{8*ᾅ#s7oz*/(JD_r54wSVsV&֙K)b^|gEXE.R e23HztI@ +Nv32.E>I-y6.!!%$(&{'@):AS)&w=ro눚TcsrFSjHwtX!kPNJؚ+Ao+-. "Yx#d _~H+,!e+ǕYGZuoO$]rհhCZįgHpɈZ9XX HT:系_ɪ|47Jɋs6#GЉjkd5Q=/W\xZtlc>83  A5.m%< E.lz[dm}a`4RX,7K2RܙQ)@{!IcEC4pH!98Ghr#HaVcJ܀X=({gΡf/2B)^&@`l}*=U=f)Gw_Aۭ V{R-u r3&$}XK )1[M4r e,mQo!C˘elmNP{scQi/ q囓 "{8"M˪=ёq+Q;6>ZqDC3?sMO̳zV\d^ըݽgĵX͢~fޗޢlOIm.-!ޑT[\ kZrp!->G+adcvj훆^ox._˺@(O7AƮ?ҋKFJ95naY^Y= [ݢ>-f*ȓ?ڑ }*nSϓ+7^sC7|3時$ ~mz,:7[Ou[ϵk/녤?:64rH 7">G +,#|Jr[w>֖_oN9ҶW tS !_rA=5hY;FGVe[x|rdG';l@T-<˯WD ؆6x%6KD)Tl5A.ߒ6?sIn[ƛ;g Ć .g@lU7Ňgjrz~EF5ⴖYs~|-Bj/3~n??"lh y7RJ=5[ 9:aW [s[c"&=@*s>AY뱶IJ3cĆqw.x򛧿%8¢[KKJ:ޟQ1*.!2>X[gd^42r_ O!SX`_|xD _ȡX?V"ٓCKgΑʫAUtqү͙!(E笼p%tr_)K +FJïa)yy5t? M5doMxЁC᫁[eoWmqX,aˈ&={ ay V{6$[s EL"GL!YZӥ;뢊ǡZG@A-"/铓-800s"5EXHJ#Q#6 <׻ʰy|*y}-nWKh:6Q-_{zSv8)> }1G̿8J;Asl2bd K|޶ j0>9"UzwC@Z+gaS 侖Ԭc_Buߎ*yGf:1 O[mk[2TFD* + Y7>hز#;b~M|rǑ [jZ/'蜏}+- }h+ ŽC_|y2JO-G5(l6^S+~[旞<\;[o宑0)ecj믺]racŁֱcׯϵ%MC>ع6.wwCWy=K2P u1*[_+8/<;9M)KnZxUA-2 X ϿjKNîeDFb0ozmOcДĬ3v]k7gD}G.xs*lZN6>r=zƮbzd5yj_W`"J.^~S≜gO<%1W׿l{Kgw6ko[S_K: ͵"W-U xȾe*Lxm9W{「X)BWnczd3Bo㋯EܥTjKT.ʉ񿮎:Zgh2=#e%(4D >b? +v޿44{WCn ² +Bεpw-nSN [5 6 듄iT*eVo3"&FS˨oέ&qg]hM6-H#P `sRgL&)(S+dg` R-?ߖ Gopm,`sUEB݁`+Lqdc-Wط0V볃#N'X7he9LV'0<ȫ) kbmŚTCeJ_]i;)YjZh-YpPCS,#MOW،0- 1;F5zlNƾ[6a򎴐T1KQi*vAhޖokԢITo&As%WIUk3G]bje]blFp*C;Fi+$]'#bMb2/͑n b6nGcFĊsw_~N{*ٷ?pKSO.SNNM=q<.[< t=WaiOj|o냅t>(uLS27-[{hd\kyjW2TS|r?jn<˽!}݅|9Raݝo5?CGaK4v|QV3)$9gekJRͱ]57 KYR|kk=o[›`yr`)rjsH)pf)S}1OF^Dt{Lʞ4|gI jP5i:듶&_)7ǫEUmJޔ6r4$%T؏6?`ʏVhďVobq>ؙci3uPD:@/O*p;rdޡWrvԨ-8xtg\|vwPS~qӳ^ @$ =Ⓧ`w,vS}'m+ՖQГ Y=96w]-hT<<-ja42HIh{”DI"iyji$6ؚ!亇1&>Ԛ}>x =3M,sM3Rr/0崚e۹D5@3z搎Ҽg!5%rKE3 +) ݒRJI% Lqz|]rš[Z_BK!).r <$1HAt0rt5-+M s[ZZ^}\9kojzĭ/ _+rLx#eU .ʬCM-\VE &=&G䖵ixF@brc5;:Xd=R <* O +T[X첑󛩳- +[/9) +]WaJ=ظl$-U w.D4xlzcrnJoo /:?orƎlD)֓uAJaLETjiScٿ@,yLOeլ;Y| bWvAɩ{Bc TS15Ig~1K,ZoIH֑:GVӞJ8s{"87bV& 8hPYؠPcJԈlDg@t" ⒡Glֹ} q[NV& +brhGR4Mq+ ұ#8vDZ{:R_/ޚlJ)e[ +he#&P;p6T+8k{4Ͳo+uwhe:*Ȳܐ#kSW ^\b' ~h#>:mLYo*"ҶՈ\M yUw}gc==nٔ3A[ +zRWS{&rG;cPlPh'bb'F.eUE)1Us.eA󠒋ܔѪ="|Xe.i)jY ;}/庬0Y QjݓԵYLvlM,.j@ ZPga}z2ê,P+Cz*x[OUW[@袰PSe!s] |ގknK +. 8#SAE&¯cvxUڠ{UlҺ4˥wd_@>:@o6RzhCCk^W1A!=]w- [ȱ>;zr˞ +_b:tS˷6fh26 i쬻c}CDs '7S{4tHC6>8\}}dmصfz$b9ǮNɡFy}kjT(7B};9{FtyD51[ƅ`hKϡy5lT,Z;Az{5 9=d-KsuCE"oBاd_kOMBxJX_k=ӔyjgTk>kݤ&%9$6xһi(mf69g"'Otp$iH4ґ&ůgd+bD_#n)e7Մ-jKA-ݣީYнD-#{v^_Ă8EOqf{?yzU]GNhދYsmNcS>χNԻ^$FWڟN’vL8c{Q޶M Xh,69N">d]LߢXd:fNgL% 6MXnsJmQZ┩g1:6V!CQIkK?W;r2>b۱ ȼ:1G-fy_՘ucKJC%i7IAEY"z`Y[ +bɯ"ؤ-E'Ƴ0ZU0=rv,=Em^XgŚ1Jә9Շٖ }suE5TԾ_L4#ŠѾ>C,ؐR*‹Ԗ]#)'4z4m\*ginVRLd„tWCkqV6X+a[r黻exu/+}a;4ӶuB_ɂF~"bdgjn5p[H9mf}WEo*%kX! sqƯ*SԕY^'fk;|&Tv1s5;fymprŮk*daQpjy= bִB]9ڿ8& ډ >J/ƣk؎5QV:&N8 }FYwd- &`6ǨQ{$63DnP\goyMs`c&!`VEeV +z.Y\%j홎};V&e]A[MQp iv]7 +0':Eٓ$#tuRD]ag);)O }T@kjF`h;Kdbo)P/ )|Dɐp5"$BA=Fӵi쓍- +ںEFqOW\'i0 vծ9TڦPX~-RaFA߳ JT&D*("o|Cl/Kmz&sE7V1C6h| Z}`!dPy)|igyy5%5ݱ4)hp3j7\h\VaiKQݢbP_^4V7*ERbx!TƱ5e/ū6<:jsLC-3;6 x4'SFE)|$Fi8/fAv5m^-eb <'?j) tid zA_ӈA]9ҭ CcvdcRҶ@L2|bxyhn:SE=x;xNe'7a?dBV6mP wzh.ME1MSK܋掃\DWqeEao2 -w ]:.¾2,,e#lp6vS^ Q#)4Ym6 "]!7p'sCyq^|"h3ǦVTvX6.e"}(TM:`~d"Uՙ,MU1bMsғԏNE=nA*R3jl؈0eY'M/ +vim1\]1wo+/s_HMuW(15zLh0}.<7{%6H~G.XY5K=ovmpj[4n@cPWAeGJcu,N0g'IPA|bT`kt&P ҟ A{z]g8%J45gx3#QpW\;^62Z2'+4X0.ⱢWŸYgr QW<゘qnsLϋ ^CQf +x$y< ꘽XY} U^n$y7_C4^Z+nC[_Uj\̟NV LɫdNMö6i ݯ)jXWq sψvUt3SAkd];|aǠopfDd +#W z M4͌K"Ab3G1Fϲ;IQۀz.o>'Zcu(isseE%<~ 'F} ĥu'?ċM'+ʸ1mWf{2jzM)%U^IIis#ϳo 9 +*TRH/]?Wcy:X޻t˧6=<}IJ#Akk00i4Y<tkV磾-œN8yVO%ׇHa}_p?9=IP :gJZsQ%4ūEhC8Z~7*/9vyriw߽ry*9 "gE&$ҟ mZڜ}VC5oARҊ]beh78_1WSVD\K"7[DSy@ ]~NJb^ANx5XYhk*cC>9߲NY ERa_#D'q^3>.o2\#.n>XR]cI6X'lhkR#Ì!Y5]jI$o{c8sn$ݘ̦\cX$+( r.>DwkH.(*R UގywH+gp 1-<g Ffv~q-%8#ҋWw7AeEVz3aE޴:UW讨+kUBptkro#S~Ok{o"KviˏELϠ(BKi3G. .\>NGQ5}w|!C]:I +)U6$sZ6Rq#W~7U>0\\#]^XGuwyK_>o2?&!tOrCo:겍xBMꖥ2䅢,磧nħ'ˇ7BXn܋uVȵCȿ2(/ꚲ![H솵8֫/zVSp<;︥_'pʾIͭ<6^d+ sBNiVcZf&m,l-,~TzT pQ7R]RO g(X-|%>Z?;\*a&n+yd-iQV V &*Tޤ71o`#w?6*ʛYǹ#4E~nVK;ŸmU׬(Ii^p{W,oD#§MDW|Hz,P5JH{GyF׻Dk-+Ti e{hiv { Pr~.FI'4pE×y.o>b]{DmVN5,-oM/TrVPvo]^5W޺ld*WC HٰGPLby\A׃7|b2jm_aSQڎ]WU gGKFyS(Rշ s+:AJI 3ΩH` !ܒ ^&䖿Jɫx+bD@MHxH%ĝuUkղ&cd̏"~>k(A@/iIͷ]9?>z)]dNHP߭czU*|[R.t#%13eYQP ƺyyO,osz0v)_^u[q.O\!6# >ASZ|[{#tCC oli$c+-jPն(`*SOᆞfWCY%*;83cMMo?ɦ>jz() ()\FK:~ebf G Bu>KHBB5=,đ(jaP&\;)rچ)'zDzrz:U*Jt2z;#[:Cںc;EP@4Oq >mGMXS}]ZXl^$ 7UUt} pm\No㲚~,. o"r5]OE nZZ:dEx)*h[ɀTrԭ& V<͇b]`r{/R=~L9pՠQ(*- Z ϭ nUZ\k$=gz3?VE0P+@E#M ACC-ܞfiH==;;fMRnl9ԖWG)BVC[Uy+'.Fjr':xqVVB"i :m\X;N+/+jtMmMsOWrVT۵9y;e!3~ERh"F2 V^\mcZo&3D9#G'Zԃ]mى4h +=^"&N>نO k Eٚ~b1o/q8BG 8er_:k3i$/v3uA.h:M꫶YƦѫL)lt/轪൲ JRP}ZLSϮE`u&󬡱{Ys>?H(045V}U鞞ܜbo(y⊺wUȳLW2,:>k}@wٛckGM";]dž+xEBbyb-(5*Ar"]-h59&U+cY]]SV=5%xn:Ykͪrj*O5T9 i?%iJG!cRqH^6,gB\2L2- Z8ݐp#/|M$g?n"Gon05\{ћ-qEyo.{;[N[.fL)o +!>r&&WL|mv4E862t,躲k}s̗3ݑOFC*r9H8-٢,G_)_ċY`2L ['.P#=Abf.&W3-ͱu`WJ|VABnpiCE>Fˆ#*4i(#YM1$%-QsFI(^|м k&N [1e'eogpG`U37%E7^䄌cCMBc|՜)6isdSƚ*teT)dCI_VZQDJ[V %{r:dK^nPԕa W΅m"Mmov%9rLqЋ"E^cW#f>TАWN; ٙGq_VC56ؚbj]EsM-1d9!7bn`O^.z ~z p()Y2¦Qf?Ys?m+joT|l.ym1ִs/Å^ tF,x̡c\ټ// XJ}a?] _?VUG vE8GmSΕJ[Č]Sm]]ߜ@oON#|M=32 'j2H-n/p=Sh!<0ؤL4HFMDoBGYHQNSSV:0qrک{-6|V]/dО(@}I#!ή!*eݖ{NȮZRER! ц)&ǝjW ظ g5|e/#LbR`qUJ.[k~U|GuiE;"Wld5% "!ORtĦ|l` /YC%k_r*KbVVqT 9`ӕ* +"CYDIMMJ9P,2lbSzlut"* Z^eSS.T].W֛0bBCS(!$ )@nparZM9UĤ?_̢)BZgڛOYo6U}Yb-xpISCG9 Y,تiHW3W,U夔0/gqX8wiϖj47.WUpR4l0(RMcm[q5 isaWw5GGYH ؗ <~`|p zbʩPsf8+ +>stream +(TG9R¦Ձ|76r֦qaU:C  _&$k=\{F¶@^cЀs`{ `'9#!îCE !xl:T R9ϮcaFQnPl 1C^,4;_VZk|1vV6 1,%0CuE'j*XCͽ6d0h1+2J^A~I9"rԞX(6p N[އ R0}ϡ8hw }gć +iw{0?ud̬)%3PLm' +gZu!b{c+@ئ0qؘ^|,|(Y}~r4Y 7.: +V%]L &"ǫ9sM9/թO.X3 n.ϕ#9$lw$eo{kEYgTmh6~Tڳ#Ei 빺,췶whjߞ1p|Ї_f;g:6\,tN@>k9g:1 !?w|3eYPBNUD ˽pwuusUE}5?BCm9RPF_HNJ",g$& ƬRLPƊ;ojr~H O ҿtY1ߒtkFrv^Gqys/:`%,|KS}}֓QZ/] Xo ?(+{_9#3ĸ$ts(5BֆacԴT }yF +?e1Lj\zs=qow}*] +j*SA9GH|ӟ7gn ~8,Mb7ȑ!m,mg0u'B;*ebC jߕΤf,!tssN7e>7d9ӊ&J3z+]k]ЀՎw+O,i> V!'2:pO@`o1v2r5ve7=d_>,@7%:tvۿ.t]Iɞ*D=Tnixj|s4^hi˒JgU *{I 1N ]ڑޑ_`~\Sb8Dbw TLCt]߫S.;RiP,7h=)&Hw( G +t4i~6|NPҳDG˷Mbu(D\ᦔ8AOօY)a?CTYD}2b$WaqoIG@moO7'>WU~5טr{9b+Նo6Pd{,5g_nyۇ<"L%[ G}m홖bvڣzh荡u!\M}8L{5J34x4#@{2|7'}tPF@o%R{A#9:ԉx_ x,>@d=3T|tsi )1rzo0&{#^몢Y2߄Ԉg'*2̮$e9&qTȾثdc[suC֣Lҳ@W3SjJc 1b'%Jftm޳&*R{Λ3@gZš4?ff]SlK {*U& СDI^rTL)FG8x/"}98`Cv/ձ +.+))Gf_eK>9 '*f>㷆݅n,Bbԡؐ%K]0fm!6q\bG~\}ەikikm" dKFJQԦ?X,ٖS7ǩQӢo ~#ń䜼I^ݫ9ErhxwNC̴!EZ%S(fz/so6J0"=џϴ5 |r$@Gy~z/JO.7e >ؗ^띠WKo ukQB=691Twc1jGڰ;;s-kDD\W|mOW3/6]\)z? (o(xY-2J4> K{;N qnE+~ +zTuW bQOl89֮(Mܗ'{( qgjZ+{SH8Q;+]Eև;Ȼl{oBxH? aM/6Ga^k`7$@Ņh+7‡GbB|[%9HAM}a; tE˝O:>nKZ{ +kL0!<ixd/|oPF93 +wE0TCQM]馦-6#\5%ЛJNԍSU%@]M^*rN]zOhsO+r؝1͔/35:.fqzcwu6Er5SHh'ں + ҍ 4"?`n]ԟ @ S@;#ô*jDev&g\om?I{ԕHU3T* + ~g-ޫ=)/g,?_D}Z|n#Ei{z :匬I|̾>gÎc'<‡RRF})Y`bvĤ8]]zc_FNoJJBVK_v&Xe!dm3_6@jmV񮲊Vz؛"{YZcP^AJq(V dq>\MU_aW7Ԝy!䓹"#e.8~~Į"cI"\}kD숋&p'r򲀘: +[ƄO"}?WEs=r4T[WpGZJ" y*y;'QɗW@27?kNgظc--wC}~ =f}Lܑ~1cX.Hߙ$'%>YXc-7^VbC*BҎ}S=Ȧ桍0g)|Ĉk#v(߫Mԕu婏2lS9:>!tӓ_xy>"v y,Ly="'ˋ7GYRz$Pۢ`6LSՀO=.Ҧ -Vzr f?[~7 ?URiWgH +vMYKtCm}ua>N̖idݳ˹ [q!ty}ykwX-^?=zy6ru._@<r^`'yws# qci(YЭ";ȟlJB +IF^\$9١*M=1dW>@X'J3.VW*8 b-#hs=R'ULburlj8gΗE ĴS%>TYup2= +Z%\NWbEX3z'ͩt-;%_9K@/ ays ̙p +LzoIx4י5~UUZWtf3O L򩁎OZD t]N,:sP':bQeؕcQWYʧ mWؔ)Grr +w3T T䱆 \x2vӮ" ٯ7 ?* g,BUP+aW~^iO3Ә2'bS(Y[RzҞ +waYkqcPP9P`cZ:'ꪳ*o @vǐ=w'zR0NMY񩉋_*0$6VW^iK|v$K@Mmm}tNEqTӮs8Ypi1AMȈ?sCԡ$u}0_CdbOlM:ֳ{)ʊqZ>g.R\eQZl83wU\7V-=rS=|sȟ[ ҳe.ת%goSjx6uUw/ 42pG +沪m@lx#CS빹f$NSaDߓFslrd๞wnbB&pO9n, #z3-%m|_Ze݉Ws-ϲ5 [jn q{xg~s~G^QbgSL"Vqbpi3-i-vAKYׇ;|&^[+0&DMÇG)G2w|"t`VaqMQpX o,k.ym1igx#eʦ]L3K"d!zmXb}f\i)mk$?h5jwWd!%)^C8%.. "Dpey=SKs+a{cݱ| )ĆZ LVe>eԛБY!( +tLa31_?2\MWWnײ:B扖n=Ȩqxu82y%D^,Fr]}X|9זt_FI?3Uq'i1\SMu11/aW1ՙ ZRm2S* F-&>xfYLM[ÎM|4i,2Tľ$ѩ/fi/j̀&W`b\5)>)(9vcMվw~Ї_+>p*s2GE.0\|] -<9R򇹬b8a9rF<|HGr3o +]MJ:Vc" +tp&'$^+(2|0]Ӕ@Wv} .0N@y877d! +7 &țj8/oWo7 j q`4@ۋYj,EÅovEIjOUZw1M+&&G<rh[Zmr<})651ya @^-aQ2zƉHp1Gj6tꩩ)41G#i; <.xtRHӼ9.ɩ!嘅][ZtБ~~1UYqG +L!b}tibtmo iO-1X]˸Q/,@{$@/t<ے**uyfؘ˅M9.7y3Mr^z{6Vx5K@=ҖmljlMSk7Q"lԱuC;ӊ Lԗ+ӽqx[L-ýwӔݩ}I8ep\D@k­dS;0 {(qKsN:_U|YW3N=9 +:Ac^s>W[k0rJU^~6Hq)OCe9-uk+%FJ~v1,;Ĉ^*X)p2sD1k2-|wב"-/ke 嫪БPq}3=Rp`mrS|'K Ws ɇذC8D_zo]c-F<⩩aUPvPO$-9T&3 &Jjr>!ʰWE&`)*'ƭVG!JFSFYeĘS{$'H@}ir;ykHI*]W_5Š=LrI_ )MEov$<-wxՙw`Wum·/t艞Z6 0vA9ӟqqv-!_%ͳI'Z\]}W$ޭڴv:ܢ+Gx;g~_omO_,Ͷsp/Nui6ʩrfe:uٮΓjrqqZ8-e=뉒:<+[Ifю1q CG9?{Ǖvf4^ʹڨEEA Za My/(zmy{fx&g&6f{Ëx E`*{eUzlNl])5! 1evƈx=9~g<5E5|CnL"lN.m\qyƍVpEf7G;E@{zZ?aspfm,9<h2);4K9ZŶYTC.?,q硨VHy\RPo?}cV)+nY+Dmk-3}蜺y3<~o/]qyn;3КCpj}C & rir 81 S1C!L֒*3؋TqҀJM fY7櫞·4\zdqnQ$Po+owL;`NA1-hw{|c׼9'ޞa#F#|j!bu|y-[ⅵ1fkAUpFr6"V pdk\ش5%jgWnSnbNMѰ=EqX%э1N[QΙՖ6PBJ7ȣ'ë(@cOR:>26GJiuiՕ5o7>pQ9JTq:fvyY)Sk&]7w&X5)BRͮ+8MFYC/[{Q\ƯUQdc O&ƴЉVmЯm2~ ]ϱg_z0)߀&?R 1 SFKa?e_MlެG&ŔY̺?s^Ju[}{2fQ;qP;σV 3tTԚ<|c;@!蟥 ./>wi0=GX⡃M.Ydo Rm[{J<95<6ނqٲ6u)c^<;H /[z>5jz}I cڜ#t-\BC̎XxiPI ,T : *=@3s K"şWTl.=eR~ +"jz\Xioa=ÀבI9IW {9a8n lmV4-_x}xcqKa}VX&o߱2?~^WC#To՜IqICs8s9nsHmK|TW5x[wu߅f A%)tƈ^8\|Spc[,oq*04iiCY˓UDݘ91r(A_^3}Z6-HhQ%RΊkqe+#]GA&&7<C^:+ٍqFyX%%k@kو֤pd1g(O֌ jt9ꈖ\00jWFoLK2.E?czfs\5Q 5i +nXϺoNQ5*lE\Gl2FvW.fB&17&4}IA.GH;қs{)ulT@ZD-MbOOY>J6}p/ >:\I+i_uDz*/$ǝ憆eU?xKEBbnI$YĢVKV& W66z! +) ۳ޝqspQ`pajǠ{aF5g"jӝ6)= r]; +*eS}t}z50/ꉨQ"pFiV4}VTԊY;GbQ-@cD*):4k ))yF +4">0Rn hd԰fX#g۳Wc<W[TQhZ yd5EYZuƮ苘dYz$ZP y`"/e!j/kش9C?`utfx}j^漰QM) +vZ,&1;4ɳM_@L<\*fX'WmKԸҍcq6a!*a}cc +S|ʛC) >礶ܔΔزg|"ИZgoMS;c>5uI$$abwƭ@zh OYyy48(in ;H3dSvh+Aen/LB鈅X~<=KĔ_q Up/AnvEb%qr9G?i,K->)k|qEHCM"R'xEEt{ռZx}L':fwT"a7#f>Ưe΁ q-)V+dVҮ;[JPMmwiy"b0$b ߍZ傭YRQg-zekJD8s#bJBEҀ*q;VgGg'su|=g!6FšT6PZfCD'm +4zct5.8a2c&4FK{oe +K,;EVMkDC]zy(gt\BL[10{v&'2Iv"&1VJc֥34-kc?Xx^5e% 0+d?l f-qKKjR=-)+5W;90:#GtuӛsVczz}@܅X4p"&Sa%.{]2Z*ZuxӐ2QSqZSFnO\jOȴحq#3f56%"\p߹0?o|uHIZ ANE@{RnMثQ= + @=M8=-hVuL#DU쎸TxXq+w:"F=4RbXNJd^Do 3ƯD- Yؼc~|Ou {mh{euQϓ!ޠC|z@kS&4FD(IhԘ~xhk~}swr ׽//KM  C2JI=s{yo0zG)\ѳ;Bj>nJMsq=1r0f 7_2 js`tέ f[fiX)nsn,lbc۵#뛣,;#e1b(RHhLHݏDۀON:9MWʳ6zϮG/8I*XP'[ڏ\ CjFg)z2;x9¤O8yGN1zOf@&W6pSO{U/e؜z_Q/y^.GM7yHͪVo%P|X +"sfп3k1 Q|[qeYe}Yq#"'_σ(8dT IĒ]߮hj +.a+,Hi'\7 0;f76Cbjq͗SmF*xvOLLJ4nvlQjXIS3>=>S[TlNX!MkQhXۡik4eد795ie鞋+ P[GpEz#~c4"^ &mϲj)䄴 eۃYzs˭YGd N~YxYqڵFIZ4nPXˁ. -wuǃo3"ŭUz+a2B>z¤%rY#}Xy,8];I%DUk"jZCxԜޛГVv]]݊h- .v +H! ¬[…øs\O9Jߚ_ 32f"c汗?U}|Te`q>7oߺ6ƪp?\?9{G|NQy0YqZo8= ˲6b]`XퟥGTZFo os3ӿ~q +~PAFn+& #_gӽ! JLIYE$Ot@&am+o|(z|ͪ<#V윧6d6vo!bBLHn Ro %!-< ?gLn!-1i`Sf7-?j>iW{x}_ +2sϣ&#7]bFnל +))Ob8%Ufgz|QbMi/,~ c3 y'1{ظ’H[ +J~+3I 92{v9٥}xh{x.su\n=|3"*A&' #;k264ldw:_aOP[y\py74q󶡛ii|**eI-Z:ĔIۄ4|1204 ~\/g}Z-WwvP#|sƭiAa%~ϟe7+lq3.gg#J X_T^|tbِz2 t"KW "ևoY닻p{F9ƛ_fp%@r6>dZLyHE4zb!>n?/-EmwJYu3I w`$<zNo +s>6OM;h=V%Q_ġohsA]zs⫎K -Mzp8rW3JXoqW}jhOYvyǷf Q#spy=jK8D_{%2$137i^,QSzbCHZ8 ğ_zr~; +a +PU$d6su9 r-6>" P)GŬT_p39 UVTsS*:6KY~VAqTiw)Rf.bmYoBӭSbyمĘy(| &IWJZeXnMYe9)Qo,@*i_?<ԇ؀R7F&;YA{Ҡ$ҵI 9H8Yx'lb#q`IC*leè׳5Iؘ5 #;DWnLݚ^ i L KҐ268ix(m&^N'^ؑ2%͘+S]?&A} +~%:.p20}syzhLj jUxW|\FVꓶqq@waӱ۲VzS\y~)A6u}Xz}"_'Zo}QՑ7Ǒbr>R%m^HEq$cF]7Vg@]>iX,aT갖VYD¢| b Z$&'-|EϏ +UIKF?/;^6 ֝SkHuPe&!eAy6}"jP"F!m5hN~sr2r%+"da9 ow1%ڇe`gU,pU׭@v`gfئ; KF]E=f6\"掗GFvyկ͉B3+y#7q{VNojZi *d\LmcQ~KX}KsIػKbzy,g%M¬rw@9i+28+)IZRkv?wFNT,bix~}ZAe61 65M&܉$1BsIJ6FB*fC$e2~Pn,Z1|/30V_7d2ΎWqpU]e\lTIZqS`c ?bkS&L21ڷ'qNn93fۖ?@]PS֦pgc1cQRfV[LG]:`Id\tC.H'͘v7C*(8[JJyDGB!2`{j{/yjd _>vey)b%[ ^[T܊δf1F+eq|'+,Úc>!E +x&冟f9i{c[5t?-{/emRqai[ܕ a'9@Je^'0{vc$(j9X+;|2 1ty.yA* c+~ #9* C"3Zn~ؔ?q9 G)pPl^O|w#e?e5wdپ#{k^}G;lߑf5wdپ#{k^}G;lߑf5wdپ#{k^}G;lߑf5wdپ#{k^}G;lߑf5wdپ#{k^}G;lߑf5wdپ#{k^}G;lߑf5wdپ#{P$Vw?r CIG姟,:[\'u`X WFL#>P o;~ǚhځCׁzHfX4 ~r?!#gϟ-:Zt3'O8}܁sϝ8zgΟ<}yS<~3Ξ+:sqS?w`N=qԉSo{/go EO?a|I㧟?po0,4߶,Z 4;qɓgx:י<~EۄN?p@w8p`GGwΝ]8oYt3?l(*Co}d![ŧ+ X5:@u781'8}_'N]'ߏc8qO<z8l(>pɓ@RT}s!Ja-8n hq3뤵AF|UTEq YkkXVuI6Qʈk踞 7һtnN2_ʷe9RY|TU15B,"njEMw~ +%yao`Q ++ %RV1jHA,XױPZ*µޜpi40[ZmTCŞԬ]g__'tޤQ8%܄))ekcI .e!*:'%DH= F'F:]YEhtJnMĄILYG^~vpUϑ=Eko ϠU3bN;dV eWu}JN)=vOƃI9 ^M1 6崅q=5e1q2c6-BS"٥<t{R4xUj5< +(Ӳ^ aۮcAy֐EŽK@K;yG8ظTВLHu9E̴l,DFD 1#1Fk&riEgR*jp=a f%b_P{䊴O6(K05102kL[ܴM@ιwJ9[McbfdS)HD@&,zFj5 ܧ$թ6q>ƗEtn@/|]OY\2ťl愎X2ZfI|ٴ1M1K.I11bf:!GԄ߳4λ CL\6S\Y@oiMa*1 ^\{o u:2.NiY7pGؤI #U7-d$Ue,J) \qI7_I *ݘY?B>o&J_K&!agag⌇O d\wF Pb&JmE&.> !!T +Y.a_VGIۅ|tܦKX%YZvAq+ig]GfVvUa܏:~f%p3']JjU)fgAŤ2X;Q)]bVnORKg>2`fuWr=ks +ѹ>Xw-#)TEB 3+oev Ċha2j26fhK/PGRDmqFRKmM`6v}p9bC:VoTB:l@$ =kAf#i̴ G+ z[FKyY!EନK)=7{s?X\vױI1tmJ+QeHEMv=buG_yME8~43tiL͚Wr=ܸ!%lWp$;^0ee%AޤR~/aLgw LG&O֛ fRSr]Ai{،ML~:np yI,*2&"ogt U)օu?2 yȰ\лVjKD5,v>f<|8dծ`OgWsVVgBkY=i +k 3~E*&Z\L"7ԔJݦ/3 ,=7bGԸQMӳ x$r +Mh]PsZZz]7X6@.rˣb]_o,^ Y$bhR2&Vwqb 㒔@Zchxݓs̰J)ID%]Fj] +`{ny%!8CyI +`.FDZg 5ms ublhQ9F֮jpWlȋAeנvTAO!³;__J߃XIX9=I4I 3|'sˊ1?ȋ[)X8;KRΒX[2 v_r/TW.xuQ*fd47#of;d]XyPozf[DOY97JXԿݛ0;+E8 xQaeine`[ 9e&z$,z9䂐sI%Y+2vޞ>=MuGiw͌{nuM;85Chp%k5bNnIq/έEu +U5A-o\=miaԱZZ|ipqjc_4E"ºT~4) IհczP V23Zs+1'jz1001#6#]i5S˾)Jw uJoKpIWGqvZ|u0d\c%y3g$ QuјhT8Ua/%\Ėݵ=E(<pGҎR.FT q58_[19q78嫕M_t/rhblt虪֑Cv j":Zm)bCY*&JeIͻ^M@}jY9>d kJxi5TLͨ%uG/9OytVhn +3+F\YTszm"5|d1&bZfkT0̯o}v5QJ%n.1'Fg5,bt|=2O.`'uڨ=n6dBjDKb.`- bO0ݩ⽋q1 h! "0OQ#ĒIdE)bp[1"mMtO)Q䳟\`%9 p 2GEoJYhٽ0fu!nJuTM*K=e:%܊ 7 >N&i!DYk?Cͻ7.&&`! +vFp3&n"W朗*:o=l6 mRv ԤR6 WBB#nwYy$+xE<9 >Br C?O "X]d],DD(&e2daCd65NyL)5sn7J^,$>:"-% 拴O~+w3jx}q g]q+7!KVf% pmbbc|k;hKI)@/Q&zNm?D4]'3]'8$t?Y_Gӆ>~ +/a5-ms hj]5zQכo{vT8'zmk=> %%219s1P&'7;~U[7= H +hx `D˨ip%i7e <6mFNQoGQ'㸳ǽǒ.z%&ad`3&'oQȲ61#bFM@C]w;򡀉ٱ +ktʂHYAQ U>|rͅ6\C` c-CϺbh]L7rxtļ_vwe9k~ֺ=j7,6?+S݇#K +^.9q B6zdgt2y.m$ιoNe]ϻ4|A"`!3v2ae.fW5h$jHlc*ՌM޾G"I8,8/E<~E$:kS.>\J>2SN62g6(fbr `>g7V?BfHkj5Ik"o8=Q-+P3XW,FKHC +~%W՜SHZM-r& +yz}L)FfD]# 9؉IKWF;EոR4g/yRj6f0|a@U 57E* RkӔѐr7G{i-4ȏ.HC .zODP 3ҌEL< 3m"*|IxHR{:gėg͸ 'ШU긆וY}7uCQ 4%Yz51  imi1 0)5m+V: BZ$eA1e!LvϋR BF@[$'bZ3 d*0Zf%,2ԌG*No!!n<7!VbuIk-,~0S5^7ܷ_xe~hkȹ5K$|-/"?޾ :!+'jDf Y-=f\Ʋ9vz2vq񐋋2qu6 4Aݧ<txghLOJYz.  7&"j;p,:n=yo[`2p\ip3VK`E0R6 n,n{{ff?wLdNf,ڸ)0/ȸJ٤܄ۈ5b&nc vO_ !Af2*R9O ǵIJm57KHDCH*Jn;5WPbĖ +_1I{f kros(8GɚŒyp(mB#D2: J1ۺ?'Gf%VG|{*cQ&!%o5|淑 얬ELI@Q Y)bj안P|zK@m6{rs }1g´GH;X ##sba#@A:!>cVFN"N'Դ:Hl <2~%z¼ILԗzE VzN ,d50鿓 4z,f݃>Rf #ef{ٛv1: ,tnA]T R}/ ]BF.f_]T8!^2˹iֹ3|Pvʥp4/f2>Q1p}Nػ9˨\΂X êt8 `&d OaN1;varQD Ϙܜ]mQ=@%|BaiyD2<YL;SN 8+$8xֶ(<^ّsw|Vy߄y@L'@uyg_ dn)QOce`<锈BZæ\x>Iл}ҴCIL@?<4*8A;k v)uJ.a`TQGXM1clsSq &c `@pw҄ս_  YIS6(`Fg&q緦+ϰEY&<昊Xxӻ-1+/[~q4ĕ,b1vbEټ +q$CiMp:$|2siМ9CaȞĦ}eJJߒ1N~=r)vnh)an=ߥczui ӔUYg%1J̑YZq$*;Z0}&6έ\JNmRpr*LUuegi pv_'9HiWqAflUWW\Ҳ%P2rvIkȧ;cpm'+N6,juSsj~Ԓt+dfII9:]rR a8rGΜ{fʛ:x'ce`|zAFs(s=JlGG-0d\9P Xf:';ȐQ[j0EU깭.ڣj@1!ܟ'+)K#نQk=%@WQSANYޛ&$ffOj}&NZႼCgh)>.zK*cKq͟r'TP#ނY`s#8wG|@\/BGO*xXϜWUf+m8Z{ۻt"qɫc丅NeY)Yޥa Y XUuvaUV99ɭcA?>=QsӠo~S@ -%0okt# | Z+%"[gCkiX޴ԣ/>6+:!~c y]ϖ vmuφ6]L e-.L"}JcRk1_wy8;SDE%l9>ҥ=zce8%hڡJ/S3dž{\ gѣsRM.ܝ!9 YrrF[.thu[n:G %r=Vb-}Q<.>D?^V +̯9Iq̫.N~r4/d4V82~C{3϶2~?R ]9*ԣ0* +x<呭J| gV ܛGZp%Lל`\,P֑r1!o;QGTq@C\ss=Nvd> :Mvq13&{"(f-2ԙ\#džcZ'XGbGCYƠQ,r e(ĸl辂Z…b#P]"udQh%'. qr!n]i^ KfpK:AXeBx w;r?Gx @ʉ3kjP{flKEL (\~H1Wmׇ? ~}l)k\^Tn-p!e}Z@~&ZR\sx9R`]06=uvRYng\Jf;F ̓?EB\B>t0 +z4-9̵M33V?<柋\6A Gт:db,:MKRSlط~zOɧ|x(BK֌KցG:>ڛؑyu",%5 ;=GzlD=t%|bG'Հ9^b>-Q1"CWU?t8푂+ݰk]()uhxMPcne]_/-u똈Ykbz6֭8ԥen%Zh)VgY>s@{< 񙡢ѭ&(ۃA*1ˡ-rAA({ 8hVա3}JrցIZF]^7BM:^`H5>WF{ ,<1 yv- 8~t}]%煠Vy4HGF  6Ԏ$\&ѭ_Igw֣нZ,ؚ$-BEix{GHkJܕI8^}@¡Lؔ言 , TأҖʉoA\H>4m:HJ%|P.6 p &ha|a`Q\Rq^Y4۞BΒо'%=FKpp^~sl$e1[7W~κCNtA:Ѡr0Ʊ:0 +Gz:}03DRؙ`fLSRg7r{fyb䑓]2265r:sn8k^9)ͫdyf*=tw6AH?42F9N%j`A\4sZdGk9`^\5 2%2] ͧ'u,?6mkK˝ڊ__2HB6Һ z_xQ :63A=!ͧ[Hyy>:a_w*I9Uz@/wDAr..:&` xXZJ<:FWKH.>a{ 6;/-i~IQ8UEpAZ{Puj Xb<^q{Pi8^?-nKV}I"xLl@ oD K |"Gf9Z*k?\.m,Wu RE?`}xༀ]E\ +99v%ȓ&>xI _xTS3yCw&@ZlGM+uWFXA=rN@(m*Zeh13ccW ӂz5c"49ɧ?4pH~h"_##'٦(ëq((>>RL\GMδM^M? kkE\h3s}jE.ӢPťVH9c82 %F0 酀NJ9Õn=\XbXb]·{37fOʱ vV*"Cۗs3i;Nӈ; [U>wUaTO&"j]jJW.K>DE*m"fh("1`E\/YU>⚣?GV 5IxhE\Ui@2+!έg1ahbywpϿ *[8 V1a3"h)0/h ,xY w,U:7/ި8\LBLHP~p_@/8-şV$ oW#Ƌ ">U00uqQRr3|}mOQq)`?F>2t8!-iJi.l x@.ؙDuL!_@-U\[&5Ҫ^Z0чbhKAnĝB92 x~ 1@לsLIT ٘=&5h@e֐ ^ 6.9s]sڧeA sUFZՔqK@m9@Uy/t*;[C7lS>3ݛdec|Z6[言谁^3 +|ARԱ 65 dC(9obbfjŭ/pTs㻴LYy:YaS"Gsnr``B#RSYZP|hw?)krv -os6r E^cL|51&EytriwApN=>ѡx,d-ZCy7 8^SI}p-X3sinA-s]j1 65p@xxAvd,.;2WYDş,<΁K/!ˀjxhp(⯹P!ljBZLٖH-x, N?45M<27B\KEy7/ +ktik,B!8GʪK~-1@¸uMŸtl̞ehy[Ӕc0 ,t-pPLH $:e7X +>ۣd:i97WJAnogެbc %ey +z$M/s:c _y`IGV +.U3##FRUALs63 r҉yp]yrp,9K*uzY;/x-#*ÀB/|r%cr`[67A!P=G< A6hYE>5uBW> z.T\>ySxok }E3_˦fxq8$h}c_V[R34%-` e4hDCcI +?iA\v +Ct(9΢М\`B-vos8020&6xY\s*p*;XԂZ4,y.moo*stNZT}0W/bM]CqTV6֎yL5#~KqdX Lhqn_ѡwUvcqW.;IRj6/`OP2d]h,oERƂKE ??4qGfZHBe,Hjz:n̳TP!ЖϐlH,- ڿSӸ)bOɆ(C7c3=rڥeLP3O<64op5:DkXy6%ͭf)Ԙ!r$.ܧ"$HIrr$)6%-ޯ =ZnQ2BVHE}[#,RHwcD%IK+toPm +L)), L)K恑)^%1} +ejWu +)!9h $Q7AКGA4Ğ5T7*p} /xБ3fشO+JI+vh=~r 4c@^r< Ū,)KбGˀMvLC4]s nSR?hѡ=ۥ80m:X*m.J\Z!-</'`,oj$⣅sI[% +x/d@GSfQJFjq ~(Yn-9oxuYY{r.g(?72&JmStok|c։~Ꜧ$8 Q;6dFOA5>1T.91-''T wN32s|[-"[Ɔ9gh )9KNO#lw{{SRKppNPN)qwiNԞqy{Bw+yWAqС> 1md?@=Sc# Sr|m0.h8&i}@}6Ƨ'edW.9!>Bcۓ$A!&}20ZF_M,؛K΢B҇KaC AYL hDL5Qs8)I)TC80 Gz*z"9Xs(PB{hM kq !.~%?` lx``A˚|8X`?9LXzG*X#GeUn]q4-c}$`b#$Lq pct8pLPB٧^6گ"$yӃiAZ K-v1h8q##&eAܿΡ@q5~{8.Tގyl'FnDZIF .d3|]/9W퓸ЙuB@\F>uL#}*J Եs;BxW6{/3BZm-ВweV{U6G=cr\[AL#ᦦ߭#耂]S=7)ؓG +QRwW:\Ӭ;ۛ=W\S0pQb0!3Åyf0tNs켝ax?;CY{VCsW,Ȁ +9th=- Uthd !<=Gs͒gsk9H\Ds$ =խdy4> +=ȈqȧCWVKs$96 gi`?䚝!!K,-,*>2s\ )3Cƾ7IuNS nW)rX;Y\䁑rQSJ, c *ڣlr2zb] ;ܧf1@hp$h-̫fq1N% 璸&t<3N5%}Z?b3J;j4F!Ce\@?2TWۛY!W)j}Po?2cdBlPnTI8+݌(=ƃ 7x7{?6x|w{` +X#_J mvpqgCb'mtSCM꼋}ۃ~j<㗝A;7A}ыxՇyi" BwLj[ꊜ@JZg=P,"n\sOvMw'۔Umփ~ލ}ݏ}ѳT"+t~kă~~?Rd/fwu;(eU9,s=/`GծNOEMHs͈pS"3}.b[uKkխa5p3一 hS:4 +~ hlKuhl@aѣحRj055C ¬Գ^Åּߗ; +ocFgYmkÆAH!vDQrvcukj{͕+m;}-ǠO8z=] R`O[NΧ'hIÄ3*0h=ܠƐ/ll5z6/3ЃL[8:|8g8FA2n?ǿ1f9'"G9_q:LqB. h1vkC3˻g1{lWȧ9mb?AMGdq~myoQ~%k:rsۇ|چzݏaB2ft-[ zWS]o:a&f죥䝍N&˽R/caZZ|[ IM ׻/AO?X\=[lξٙrTzWK$hc٦7ntv޺Sϯcl[{Ƕ1Ss{IQw>kyFFQ/mcqbV?kZq͖rw'.JR+B.J[M >9IM+9X_J!ﰀ@!@퐁&a=K9ksX +~4~_mC?c7D wMS1۽g۽Yn9كivp: >y9МM[3߆;=˭ֻ k2+:/̈́''%9gA+k=[äio@u+h9ۃ7;Ǎ,wPl:܁|8WsXxs8261-?d3d#JF ?(t+*9y˝hKF)aZͺι߆;Sx7}wg0`ˊkW=2r'c7w[r*Ol QϬx}W £"g^nf_[jLU_{rkX[}kiSK)ԕ'(uf]WƚK+軆oY=Sl̝VJz7⁶2geIfn/gIaFJ'B &fko+.n>B=6}Ⱦ`\eafb캡"ᄡ$ciIcyɥ֜+や*ī6zzg[k7 uySƚ߶oV;<}7B}d~XR[ww3inoDfPB.+3Wf&:&qo iwbx Jg-Ks5mM⯪/O#:>9.ƽRy&Woua:G1{6{06:Y.n?'9ݛ 'V eK +xrVRuZS\sB_s~=ഝ\4(5\ė؈/Y[/W{!IQQ|c3҄uUx'&e<5:B]d2#HnLs"1vv8;kg4gM9KKuy~r06G% C -)/-'k*3.kGoo}ؖ5q?䳮9{4pOCONL +(y ZK. ||#驥y1낡&Jk$%QVu7j^b|;(+[-uq'WZ2.g ?YiC775UѧDQ+J _~[yߛ]w+诇1_0⿚0B Qqwg.&5ml=TQ$'~;J 0L)lso6hQc_r|iH>Vpk}bs]qwf[U r-b FR}ź+_Tũ-'loBHѢZ-f o IvyYhGԭ^Zf?fZ7jgMKcoӊ FҳncMES]eUEֹ)q&tؿ{(߭ucmU9gSOq⿛-NY]CAAq>Z `,餶*Ga2|'H/wQpg ^WkuDQamnAZĵfӇ«M _ޑAK{Hʲ3ڂ+Эf +Ysű߯v!` S>Te_XxyF3 7Uy.j,b ᩶}sZ~RW{~}wN}}sm5^|; 9?stgL=s+pIUqO?WU,̷Ræ <@;9Ŋn]/Vn a½C#_ږvyo,IXjO\4#̔eߙݰO^9‡ow07f./zkjŤSxt"wϮ6g+Kb3%̽1N oz v1<$4c k/m&P a̵揸O~+}75c ^k\_w}e],ϹTu?g;DO&~7Ly4'KUqܷcЯo]n˿'=ՒˆC3xԖ2qV7gE)?ROgsk=w3sʒ+꒔3f[hM%HAuCz㖩>܌ 6n}F~#1fbKыGME7׻яV;Qkg_t"X qAjZ7!dS1~>ܒreրI]l>\o(֎z܊y<.INMrܲ3R~B)}L|I .0*| ` b'z{sCA/- 73KֺЏ +p^W$ %p0֧N;yNQvv?vRxȷu0࿴Me%[sYs/+ʒ~wpQ}זă!J ҃}>'N;d}O.MY]veCYj>%Žƽ绦 .?= rCcI5s dqbm j$;( {#MTqUۘ*ȾKzo$ծ*D 'ך1f +ֆMT%$\JДv'Pg)/Q´59gߍJ|m+ eeYc]Es') +sR@&퍠B׺of^U&ؔwR<5U:IMD~Ⱥؒq{r{ Me]s^V)UyMK+j7 vbTia~mЇٶ)f836+Zr5foCO 4w!KA^%4Zkޢ7qDksH[̐޸fمwȇI'gI? ϯz*~B/ُxcܘ].9Ջz^R uM=лiǂ~0 +RJwʵ~J/9!":>|uJ%Fmw=0֤_Ԕ%_gYkCHC3cxZLćڠ}ysg@bȽ&fGC}UeheX_|+⁥1V󵡢V9lދyܕp7gecunrƠYq9q98v[0;geg7=8)V}TjB*2=&J<~\ qowD_ć>Yʹ SzyKeM2.qbO]@CPn{0\"3 C}웮iVRXgؐ{Pӌ0a;n"RaňOVff{?ק]1~L7}n{2(>;I WᏗ{w1p_~_l%;eeQfB ޘ0Z[#}l'e]^3 wGHQWW K B꡽>e|u7)Ǎ.\X1q{LJ9BWR3v\@lJ&4 ^?X' +_m ? x㥲n6{߫K\s"05v#Qrt=:i{_):cfeܵ^ؽantvakYxo|C՞9\mRc^vJ8K] FbCϘOjwNu +:QaܩanKM9׻/=+)>bkQ^//^l%V@}5[57o˹fnM]O*% RJ3QQ~fNQnrޜT7kE։āgyH`u8:r_&aOKkLK+!gM9<Br;Z7F1lo*cl R"|S!NspT Ve]uXi/~N^zF_{o|2s,ܸ ubt j볮.Q"VYiRf?3X_t ,:AZsnΕ%\E9 ݟ'y6s봘7%@o --9W;otٚTL.> [}4->xAey RJvrU5vq[Su]Q{2C0BFd{؄kG\#Nv& cOy zF ˥\b?* !N~VnS׭O֢K]i M 4 Wq[+)׃|7CJrK+bmJ +6]0S;Gb/s}kw uS5_Oz@zJ %X98hw7ʁgIv%qfKLZR#v'-z/ȷ1"Ueˋ-Q)M+Ͽ0]wDab.l-!mB_ľV%G1z.GUV +O1L\kEd\ʉ56-b^:7F> w&J+BK;Bya=YlZ̷ĤPnmR7ZyjI6YBe,J5ڏ{UKܜu;i I!Jb1u6,JRJɷ櫙LkEI֮RZcq$l@=`m6HzkݘdۄfmNq&X9ğ-┅Z$cz$g^0_ Ym;Q?ĴԓŎ{YbCbU]N}UU.ro>g [ [[]{T'g[<[/Nl}e6N\q`K'ks8'ke5Ňω916/Z!Ynߴ Q氰e:{])-P+t=4=縵M.mES]R;)ܙdd9ɾGvyĭQƜk ;K]Ĩ*XDr1z79^Y/.֓_oˉ -BEib16ʳq +Sҷ?|giC=V@= +#B3-f0j/R%D}?+(:+][M>\UHjhz2K 5RjK&F\fC^DNi̸=EfkLXI jZm>UehQc%c[\K%/tޘ Gmtb ӿ%߾ٳK>;꿟}~٣'Fyq~]AFl3x-X}K8D K^o舿U&KoFP^#"ZDڙtSŨ[˝a.J _t Rt-q)V sW.z}ew4mچfbf&Kfffb3ےldJz3/fֲg3{A@]š}+gC? ~tPG)a o:[uHpiL܆1$B?NsI&O{xJB/{7@.}{p()TqVvr/4ۭT8ULqr8.Ωd}zή$fvPp nē).:8,qL| wH'`1!sbOl Jή7ƙy+}\/!Tߎ0/ +<فF=b,7')%]7xޅ3ܯ/~};6B9UȌ7g{sȯ,Hqn5&g7ǩy+CBI 8_pyJ'"/N +NP.KX鷗)?Y\dvQ<{\KAd8(Us[gπ_{n> +yzHKEY5?֮ГK@=AƉ?k@fR@%|| +|xiPwoN8 +{ 'Gy`TUsHiKVP$)HVp0({@wϞr-AO:vIлWA:Q ow ?,RDŽpe;+vB@zՂ^oE6*ΐ3 |$kPƛwo@qϟ^\ +ǥ3g@;{.@ܨ_uWbXU _,~mh8@N.ڒsP~j˜/=O| + +zEAN:/Nn'ɍuauMEOSЕwITb|'P~"$<. PǗ@/^=:9sgAt◣ù7y}혌٧PL*9$TK0Eu<*Wdxь7@/.]=px6(C`ρn9 zz +~9z t^ JFyu0pkMhWw\5U~<(!Ťsr$")ٕ7o]wqmw.\<(%8"qz=Mm>jw'\rYlhYhyV[}q,#Okb/< /@a!nOq*ZH8Ђ?yǑiF'g΁.s{%УA?N<ߕ%)߮f'%3Rywmp-K5أ#1QG҂^޾z >< o\E~~$co;7&Oq.Wy AHUj@T#1๹z +| # A'/n?:'ςygSEc}"dOO4rƫ)UY *N +`P^=z tWW/{zxڙ[zݸx@̘.EJ+)𠹎V !myŎW#QW⣏| +p(Kгׁ9t_A.]8u PԛG_*?|;*lGLYEGCڑmtn*6L~X葤O@oހ^=qtk{OAO (#ޟedp(e^#0&ơUMu$K߿=(s .|@^2Ÿ@Wq?v t5w@B~E'E]T>/̓4l-$x0CX΄`s/Vg^qc У+Xu(MqBJ66EڜdX|yd؅L&yLz@5rT@wq{/@/ {x454haB fEa&\;]QEL1 R&̧U.gVְõq<,VQF޽;;(NܟgAofU\%0&8ԠZUP>*i6:ͮ\WQnh5M=~U\v172xrPѠW OzJ ;H.A`.WӬ2ZUBrL|;'hZW0\ o` /3!ϔ'o?E|4>(x؇G +SSKoLBِmQ u[\Hc,m 쥱4Uj%Wѿ}~w"3,7hz԰_ݿz g2b㏓KqL;e샘yyL4g s#șAl\?C#|7(,F[.* h \m}31 R[d5ɀ\\*cl+Nw6:g +kJ&|KJXV&љV(>0TܪjK.ħzu-)&i[N+uwjNgskY\Mwt$!:^dž- 7&,#*@CbROɂyL 30e7^.vק>®.weuUT_%a} ]@ˣ偓ܸҪ;Hአ6S=ݔhZ{&߫)Y[t̲YM`v 18N=1]*lCI)6T +ūiw:^ۥ9$8.f*m. Z0Ng%ZVrv5v ؛2-*b%mK 欀inϩvDh;xgs6:;J)aFfunuSSbz +_lAnN%5Xu2sG\Erg=vulDqlG5S4oxdmˎwuMWYEsɩe#M8tyc% .5hAg%ӫirȰϫC@ 4{h$#gU9*J!oY0i5v;-*%d +۝#!׸0Sx85 l2lGM*ܝerjlE6y: d-vC>oEtW_隩nT :rďeaVy&m U.p>q$l5@lzki>b} WK +a;4t/ٻžEAUC.7%?]AVgkקiHPv`l!xZ_֗ TjS M94I;do wgLÈ1^ ;XK;Rt8;Q*w8u"NXCOaBplR\GAvPqgڒo/o;JFTꞩVs0V).ug}h.cB9=u]ˁMO"BzK_( c&|[Z|9X9<x৑X20˯j +P3]*r8"z,眰gmK)ss}bcBт \Dn #fZhar2إ7T$Zseؖq66&G2<^u[M[3f{1߭b.uw~_}[„LR'[c/*l|'$ԧkk@hE69<Љk{@n8ܽ,֞`H=e:p~Цd"7vr& +φ*ߟ߳&ǥ4,Tv`iV",c}'H|>}&bpEL]nTzJP*6'Z˾{y"n7Kkݙ&> bO!z )>-2ͣ Q9/}"lU*i +tMNHeU@Y\KåX|OXJ5uBɏ%UT>.!c|&Dymy4ۇ +YnLB¶\skEn}U4[-Y~].B +b'6fWcx4V1&u})Y saYgS84e,BgxM4kAز! Yv,w5nWYx?Wfs 1CڧQjҩOK6 ?>5|&=p( s,_K<0Ӵbuww{3ߑp#W2⧖T=fI"^X7N9$<0ѩj>3yMD?ɿyecl*2;4.Y?X4PօldXlc7.*AZx [RU[Ǣ>i;5B iJ(8pGj[*/ <^\㘸v Ĵv\ivomӔAוnkqnܭ򄿖kg9l oK@ ?iF~caFeOfjsnniWLIb g,MD-ݑ.%Q w KO,e^v]jRCN1XK_S-g\m -}.+';t-FGoBuMVGP n925Pz{i\Ҿ\5kc" +w[^Ƨ[Xa,*jsotEt*IE;lSN.O2mxc\RjOW'yuAUOpkjFY2PWU0ɩT$=x,䮚ߖ SbT\o_3wAcIe.58*9V2 N‚m"ts3KI<8"vsֲ|\֧={f +I1!B@5Gٜ&nӶ&f`ƦNȧWaXmb +yK ׈-tVYQE`S$t~pTzm(l"h.5$zu=[dy|E3Hv rHgX$c\5 +YOӊKJ,v?c.(QLY֊7[BQ=ł|K'a)V{a:Xݑq`)ZzS+]rL$pe:$\bƺ^ƿ x7 fz'бkZ pjD|MCיOQlZOqp$'1S]3zD[9qH;Ut@}ӷ>ۚD: ]IشŪ*D$L#r|. j`UT띥vĭITc/t]l)}mnuovМ`(hcS{kӟAӞ6{`p(X v1 + ](1\aWehL2vu4<-yEV[?6ÿuW46ޑU]'n܉/ ?5CBR|SpUf>?`WNF{-d?VN)qr& {+I[}Sxm=&Į!?ZPL"j^"r9&?팕}- '";9 +U_z c8-=LȷK(4.-G4!ҧ \֏Y5]=xOO,)>gن>.ק]ȸ+CD^T026N70뽰ϺNje߬R:ď- gмjt_ǣەLKEnP!Thm*45)%tZ p.1?2ŠO}027֤Xlz5XȈ<͋?k˼y LUub|W?nS$NpۓY6uc Yb.zw!T;&%1BE̷d^7 33n,<o&j h4>2]̾?vbq-Ɩu|ֶSl!Y\*-8ߜx}|gśwƺG;#6E/\ꀾ$IOzgUDjEIaMШSKa Mu/5YyA3GHYwfې{z2x9Jos 9^h) iP/Ŝ>-so@o ¿þQj:-HI1j yzqtVBɾfܜoy K\#guf?Tg>6 +T_09e=:6 m;}z|Hۅ+*RI9)զ2B1~q#@pZ*d{VUpFF(["L_./>Umgzȑ*4*"yҷz~5#(A_iW˒䶎0w}7q@^؜}#prV֌^=Ҳ0V)p}P!>=௹ `*dSthՃynXm@_[Ĩd?5 UF-tiU# )X0=2Y(y%k35I7$ˀưOB ֖ ^"~Lā @F\gQycůcHZ[}ι)8hiJi|QV97c^3]h.zQ6 եQ.-=WU[&3绊̵Yj)]6kk5BNkya[M-iОug%6ڦ#8}MnL[J/B?IyKfs2@L>ǧ=(ƔM寏bo&orLO`StMizp_T]M*s2J^t_ =#Rr*֫ 湦i=/Lyw;ၣkݐo1JuwN✣nD*7or^7qI(%*^9Cm޲3$ĞE+`ysQfcr(y;( "W@t0 +& WI~F?wQ3o |M"jxʿto4ȥAD {:2z7y8p}#TsɖIJSƂ8$NpTG+NLϬeW + Z2~T[Y(yա3vĐCco ei[%+_,3_K,{"p=r[{N58ţqpžڑ\ k!jr+۟qul쾖w%qTVock_ٛ]2F|uL`]t<)KDsW(YS00^$2=m>MCuiהZ^c}/.^Nx=>Tl8!VwID85E?]?n MydL22А|kksۤG%:ETЩtyuFp1gz*^o㓗)Rn )=킌vY\%_Y,tV|VS)FIOV +?4O=cg>S_~.rkl.yotTyy6ۣ%tRoWLÖ: +K1'-Oʿ=u" +uMJ*U n֗;I 8.q+%= +jžw#9$UQJ6as PJ/j\hɻZz?YaTe3}*J񾖍u 4yKh}#sۭTf%Qz*V!?ibZmrqdžⷫE_;cQ]d.qe{}ߡ]ip){R:>Ӯ̷j|:EP ^l}spoBi%VJh9 +t\K!뫳lʈ7IbɞV[onf= ^lLmAƺuLq6G*?9qוnLEϼ;]1NH&O،9H#z"Dj'-tD]Յ-f9cӖzXU{Lu` y 1ɻ1Ϲ>A-ݐpa'?k( +>FM|zDΎ `ʨgz N1'ŧ9&*ZSO_hI]jQ%6zUy&}o{g hGCZ_>)L]O_FkObBv JuSe]9Ck>\ĭ^Du b3taOX(*gnʂy@(OsV `›)BWͧ']Z +OR/We}9^|2P_$ثBWK?u>iϽ;ߕy.)sM5ۥfu/}ZR!끊~y1$&nG,5߻\ +&ԕw;!&fƝuL@cobV@/O0?M?ML#:K5e5ޘis5<-"rጰ@$020*r[Rџb{(%^M+HzoI(N[˂hxIBCE.u(htׁX-}ѕ}=c쿇n.z>Lڑ蘭 t8",ؔx}Ia`~pWs^y^`Y@KVخbn\SGZ~եX\WVBƀlOP>*\%mzpeuq0OO,t6EۑЋ +ӡdBv9*GOȷ1ItתK:,[ ䷆:!-"XL{>DyQ>.m72"gP/e+Ĥ +aecN\i u5QXr:Xe-LCPֳxW^Ancc <Ʈ=eQfD7rd\w  &>EȞ},7 W ϯBёm˭ůtlW&TtIV ,i*?s,Y +p_Yԟ &ǭU2#'+CؼqFS)3yFɩ% :PЙn=J5p9k偫Y6?) {kb2)GgjwմJ|}5/@VM3P<ġ_K)+}O}20,{ד˶QLe䯳Ԙqc9z~1Z7SăuC#kk`{8/;#Ӱ^3*QXca;CEصDc=#Abv1Ǥs[]EϭdC?1qqumq%sw ؼm9 z[LUZÛ'-L <bI H]S]מIX32N%&:Uh* H1b},aAe=\몈8;P|f}yFy1Nɘ.|ӄGȱpCONYyUTңCdEBC-9 JP"ӜZt]C((c7GqEvDm]a$ZK=@ !˙>x06ju:{:Zy[>8s_/RK(2kKN͓?׋ Gf 6מ@J %m~>mjN}fWGY`kBFOfBXd_Cݧa`*jNػƶ ;^sY$"gں{쬗򲞞0}aGI(-sgN{S6ܛiy7at[jٔ<U`]/>ֵU\^P3T \*>4ɣd8İxH(<ٯw +t;+-9O-}=)~ҝn̗ߖ+ ܏9b`uCr> p[>0؇&.cb }T߉ ]T^\[3]۔,#'X߱6rk + 1oI.ޖbRƂ]!Õ &qhm9keQz6`6 +&D + ZXk~I)QTzJ{Ah4BT e^[)3]m=9BWUSN)} +ji/o.yasu}sd̞ jfmПlL/6r[ɡk;We*ElK ^*ymd;DR_h?X7 㘸Ƣ{FA _[2t<@0>]hF}t +NiFVȇ짿3kxbs9NHIn-1ߩ&y^אּŭcaf،)\WcEi 1<\`M +U~?1θUnu.̷d[Dw?l 4 mdQ +F]aɊ`EMm psTUE*ߑ£v$2H_92?$OOf#Sţx45u6? E'kD]P +v oF\k@Ǯ3+,wA?u"c.On٧ \l7=|AVS#*Z宁P3b +z$DVa1ޙY^ǽVqXWg׋&xU;}cEqs3 Oz+x/}NS:=PSJ;뤮*gB%u\]twjXx=F&k4/󮶾)m1 +Cچ }`\7ǩٔbN=O[v9t,CX"+;3ziGS#ܔ*!wW/?4V8L%ya4DKڜ >^Q[KA@8-8t䃩%4.mUC.K}I:ޑwd$"bc,~?M𘪸LCgAUSk^CUݞUQn4[+v*Xm9#!ݙ8'Iib8"ؔzG_}wX}z{N.̼?NO=0I8ט<4V@:U|EJztl\](((/ @7ѱ;i|P*'W|.Q1+ +9Ą'f bl]KЉ汊ΌWKRʗ-ɲ괛:aݥ/5gPy`mLr(P8b|t7X|f Ʀ[#< i(( {ẹNSݰ ب1NrOٻIfuyu^E=l/9{GU#*ydճi\ b`N5Įf8TBݰgVðiwkG4j!!&T/EZNխO>-%m~2(kkayaX(BtS5B8\5MX^)|me4@!:P˂mSpͶa^6߇縁_'. S*Kc g_ܦf!vT,]:1!uSB-WW']C\l %=5公 #{a-Ӑ]#k|,曣Pjteϣa 4K}L4y2z69&a-k"j()7"wϴkՆ0 \Zt+꒧*^11\yiRK'꜌~]tӋ A~M*z}dj(y^tP^}>z!8mcEǐE^_3Zǚ@G. F$<2b ^}4NθLZ)bӆ1TBGŞ+}w'AWFHᷖ+VFogGn1oW Or#,E*|پI*Rѿ3㿋\O@3XC /S3v×*ߘ3nQFgޜ>9*gs,潞m- _<=MɷV1$a}#on%*BѦ(HɲoN DTicPs^c_k8dh/.99#MDĞW?GFsUlQm!fܳå^KLs`jB@-)T3N>6`y:M/ D^[Eϙ+};hSV:uZF' <oyt]U 붪4A߮/ +?Tϵμ(ݎpxMV,3[MS_En53s}D=զV"I*U 0)n}QbS`S4,~oUײ߻j[K)wv09!]QR@۞o>=Ͽ|E˻RABҋQd*应x~Z_s,hÒ|bciKgk:%{v}&3)3ɴLz=1I{WD@zTTz E^wgy}ٗ7@8U>ߋsucB*Rp^0*Ύw:dʲ +6:(l} ]h%!qӋ@{~FQbՊ-`ذ5aJVSlxbX%iq6̄#$ eX]'?d$߃@|b+oW &d-Პ\ro?hwNJ0?yo@Ӯ"嘼/D;5 4yI5E.i*_.j^n9HSt/! K}mB3- ?B a.X{ >tٚDw=73qR*Rs{ڋ,ZB-6tKgm &D +kµ~퇰 [! +j\8cΟVC⦕]V׿Ϗpn˛]}/=&Eߴ+6]wfUuOm57zCFo BͦEa݀)4o2dN(9{藺Y=Jl긴G/ߝQy5Mqz4lˏoZJM?7nuc6 e!qX }膭1&}Gj^W8=@[rfxؤ g)["!N^h" iڬUۡ.Y5KZlڄ g(ޖ㦶3zR͜c!el߱2úA4sɄ cT{:2`R{@^%%7+ükntKR11^śiY[dV闇y{ܸafͩ6vUks?SR4(9nP[ќ饒Rxy i^j懵JS=Ȭ[7>pϣtvCnLSea/}0}b3%Cx_SɿvV?&*w&)i]́ ^ж rK;W,2 n +6  +/Sd§ zwN^=UO'-_:]=C 0<}×Rpك!͇3\ccԼ9vm٠x?,lDP~_WռZ'O랎p/n9SڷezL^hymThS^ +R!i':<ְ~v3Z?8>T¬du4?5~ xw̢^^թ>t 4meK[p 6Y:WϬ~D(yw*ˤȰW*}/0QaŖ-)%_BK#A\f !< V~D~x9^qS;^H/lY@ +]l|m-3퓟\HWЕ^^ ݯi`w9eM |0.uA4T]<cQ灦u=%urɈc){6tɒd?rr zf \GkiVBɖDl;n|qfAޘmIDE L{Tȧ h"7 zu$ؒqWz|uAV!_.WJrNXw져]/X[2E32cA(<,y%]k j\Q|Ϲ/KSc|) J +. 9hLjɊMM1{(DNj@Ӧ=oeh^KrP_;>M +DSW F"zgY%,ìH +-+e †J~p7?tSz_qs/Gjs@oOvĝ^"2ͭICۮG_Ǚo4za6Kߵ7-QQQAS;YOsiiCܲ77tvtiY2A36Ԧmt0_9mԿƻ+i~:f-tm<}V&/7!xD >~3+fBIFb2;׬S?iEONRoն9fk9/hq'ڪ7Ě薝/=fk^o:A +WfYy Ҿ]Ŏ {i׶ݮ@=v!vTњV5'y%/^vq-'iU,턤uukO>?vR|yIۖfQa%:wFoR"炨AZޕAe[V\ɘ%_tS77}Ջ(sw_v3c9K$ꌲ:Ǵmy:"c>-weLӸ)u͎b^p>xgų#VL_kVlWvm-$Q-I$B؊OsFhgY:OV˟tsKsڧ^N!Aѣ$rZ%}]$9)5ۃl̞KڰJWe-2a +ૐ屸%7fѻܖ>F}'𬏕-؞͊f&E=ґ{FmXPAa-$?漼݂ٚBu}m̎%7~qHVlᶥ-]}go 6DaR˦S4 hZ5[7X8L8 4`L P{=|" a흙nć%iZET`NtVu5> endobj xref +0 46 +0000000000 65535 f +0000000016 00000 n +0000000144 00000 n +0000065112 00000 n +0000000000 00000 f +0000786621 00000 n +0002266025 00000 n +0000065163 00000 n +0000065618 00000 n +0000786920 00000 n +0000396752 00000 n +0000786807 00000 n +0000068721 00000 n +0000065816 00000 n +0000068159 00000 n +0000068207 00000 n +0000396787 00000 n +0000786691 00000 n +0000786722 00000 n +0000786993 00000 n +0000787667 00000 n +0000788966 00000 n +0000806410 00000 n +0000871999 00000 n +0000937588 00000 n +0001003177 00000 n +0001068766 00000 n +0001134355 00000 n +0001199944 00000 n +0001265533 00000 n +0001331122 00000 n +0001396711 00000 n +0001462300 00000 n +0001527889 00000 n +0001593478 00000 n +0001659067 00000 n +0001724656 00000 n +0001790245 00000 n +0001806902 00000 n +0001872491 00000 n +0001938080 00000 n +0002003669 00000 n +0002069258 00000 n +0002134847 00000 n +0002200436 00000 n +0002266048 00000 n +trailer +<]>> +startxref +2266249 +%%EOF diff --git a/libraries/arduino-lmic-master/assets/Feather-M0-LoRa-Wire.png b/libraries/arduino-lmic-master/assets/Feather-M0-LoRa-Wire.png new file mode 100644 index 0000000..090f411 Binary files /dev/null and b/libraries/arduino-lmic-master/assets/Feather-M0-LoRa-Wire.png differ diff --git a/libraries/arduino-lmic-master/ci/platformio.sh b/libraries/arduino-lmic-master/ci/platformio.sh new file mode 100644 index 0000000..11d70c0 --- /dev/null +++ b/libraries/arduino-lmic-master/ci/platformio.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +############################################################################## +# +# File: .travis.yml +# +# Function: +# Travis script for test-building this library. +# +# Copyright Notice: +# See LICENSE file accompanying this project. +# +# Author: +# Francesco Zardi March 2019 +# +############################################################################## + +# Treat unset variables and parameters as an error +set -o nounset + +# Exit immediately if a command fails +set -e + +# If set, the return value of a pipeline is the value of the last (rightmost) +# command to exit with a non-zero status, or zero if all commands in the +# pipeline exit successfully +set -o pipefail + +if [[ ! -v TARGET ]] +then + echo "ERROR: variable TARGET is not defined. Please run this scripts as shown next:" + echo " TARGET='foo' ${BASH_SOURCE[*]}" +elif [ "$TARGET" == "esp32" ] +then + ################################################################################ + # TEST FOR TARGET "esp32", i.e. BOARD heltec_wifi_lora_32 + + # Compile "ttn-otaa" example in all regions + PLATFORMIO_BUILD_FLAGS='-D CFG_us915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_eu868 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_au915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_as923 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_as923jp -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_kr920 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_in866 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + + # Compile "ttn-abp" example in all regions + PLATFORMIO_BUILD_FLAGS='-D CFG_us915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_eu868 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_au915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_as923 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_as923jp -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_kr920 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_in866 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' + + # Compile "ttn-otaa-network-time" example in all regions + PLATFORMIO_BUILD_FLAGS='-D CFG_us915 -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_eu868 -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_au915 -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_as923 -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_as923jp -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_kr920 -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + PLATFORMIO_BUILD_FLAGS='-D CFG_in866 -D CFG_sx1276_radio -D LMIC_ENABLE_DeviceTimeReq=1 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=Time" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-network-time/ttn-otaa-network-time.ino' + + + # Compile "ttn-otaa" example in US with debugging to Serial interface + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LMIC_DEBUG_LEVEL=2 -D LMIC_PRINTF_TO=Serial' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' + + # COMMENTED BECAUSE build fails with + # src/raw-feather.ino:119:22: error: 'class HardwareSerial' has no member named 'dtr' + # + # # Compile "raw-feather" example in all the regions + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_eu868 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_au915 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_as923 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_as923jp -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_kr920 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_in866 -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-feather/raw-feather.ino' + + # Compile "ttn-otaa-feather-us915" example in US and AU regions + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_au915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + + # Compile "ttn-otaa-feather-us915" example in US region with interrupts + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D CFG_sx1276_radio -D LMIC_USE_INTERRUPTS -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + + # Compile "ttn-otaa-feather-us915" example in US region with debug to Serial, at level 1 and 2 + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D CFG_sx1276_radio -D LMIC_DEBUG_LEVEL=1 -D LMIC_PRINTF_TO=Serial -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D CFG_sx1276_radio -D LMIC_DEBUG_LEVEL=1 -D LMIC_PRINTF_TO=Serial -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + + # Compile "ttn-otaa-feather-us915-dht22" example in all relevant regions + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=DHT sensor library, Adafruit Unified Sensor, Wire" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino' + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_au915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=DHT sensor library, Adafruit Unified Sensor, Wire" --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino' + + # Compile "ttn-abp-feather-us915-dht22" example in all relevant regions with sx1276 + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_us915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=DHT sensor library, Adafruit Unified Sensor, Wire" --lib . --board heltec_wifi_lora_32 'examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino' + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D CFG_au915 -D CFG_sx1276_radio -D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS' platformio ci --project-option="lib_deps=DHT sensor library, Adafruit Unified Sensor, Wire" --lib . --board heltec_wifi_lora_32 'examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino' + + # COMMENTED BECAUSE build fails with + # src/raw-feather.ino:119:22: error: 'class HardwareSerial' has no member named 'dtr' + # + # # Compile "raw-halconfig" example in US + # PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D COMPILE_REGRESSION_TEST' platformio ci --lib . --board heltec_wifi_lora_32 'examples/raw-halconfig/raw-halconfig.ino' + + # Compile "ttn-otaa-halconfig-us915" example in US + PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4 -D COMPILE_REGRESSION_TEST' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino' + + # Expect failure when compiling some examples without the COMPILE_REGRESSION_TEST flag + if [ "$(PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' 1>&2; echo $?)" -eq 0 ]; then echo "ERROR: Compilation did not fail!"; exit 1; fi + if [ "$( platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' 1>&2; echo $?)" -eq 0 ]; then echo "ERROR: Compilation did not fail!"; exit 1; fi + if [ "$( platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-abp/ttn-abp.ino' 1>&2; echo $?)" -eq 0 ]; then echo "ERROR: Compilation did not fail!"; exit 1; fi + if [ "$(PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4' platformio ci --lib . --board heltec_wifi_lora_32 --project-option="lib_deps=DHT sensor library, Adafruit Unified Sensor, Wire" 'examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino' 1>&2; echo $?)" -eq 0 ]; then echo "ERROR: Compilation did not fail!"; exit 1; fi + if [ "$(PLATFORMIO_BUILD_FLAGS='-D ARDUINO_AVR_FEATHER32U4' platformio ci --lib . --board heltec_wifi_lora_32 --project-option="lib_deps=DHT sensor library, Adafruit Unified Sensor, Wire" 'examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino' 1>&2; echo $?)" -eq 0 ]; then echo "ERROR: Compilation did not fail!"; exit 1; fi + + # Expect failure when compiling for more than one radio + if [ "$(PLATFORMIO_BUILD_FLAGS='-D CFG_sx1272_radio -D CFG_sx1276_radio' platformio ci --lib . --board heltec_wifi_lora_32 'examples/ttn-otaa/ttn-otaa.ino' 1>&2; echo $?)" -eq 0 ]; then echo "ERROR: Compilation did not fail!"; exit 1; fi + +elif [ "$TARGET" == "avr" ] +then + + ################################################################################ + # TESTS FOR TARGET "avr", i.e. BOARD feather32u4 + + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST' platformio ci --lib . --board feather32u4 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_au915 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' + + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_eu868 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_au915 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_as923 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_as923jp -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_kr920 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_in866 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw-feather/raw-feather.ino' + + # Test the raw sketch + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13' platformio ci --lib . --board feather32u4 'examples/raw/raw.ino' + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_eu868 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/raw/raw.ino' + + # Make sure debug prints work + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D LED_BUILTIN=13 -D LMIC_DEBUG_LEVEL=2 -D LMIC_PRINTF_TO=Serial' platformio ci --lib . --board feather32u4 'examples/raw/raw.ino' + + # Check build with deprecated CFG_au921 flag + PLATFORMIO_BUILD_FLAGS='-D COMPILE_REGRESSION_TEST -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS -D CFG_au921 -D CFG_sx1276_radio' platformio ci --lib . --board feather32u4 'examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino' +elif [ "$TARGET" == "samd" ] +then + echo "WARNING: target '$TARGET' is not configured yet." +elif [ "$TARGET" == "stm32l0" ] +then + echo "WARNING: target '$TARGET' is not configured yet." +else + echo "ERROR: target '$TARGET' is not supported" + exit 1 +fi diff --git a/libraries/arduino-lmic-master/doc/IBM-DISCLAIMER.txt b/libraries/arduino-lmic-master/doc/IBM-DISCLAIMER.txt new file mode 100644 index 0000000..6f48935 --- /dev/null +++ b/libraries/arduino-lmic-master/doc/IBM-DISCLAIMER.txt @@ -0,0 +1,6 @@ +The original distribution from IBM includes the following notice as README.txt. + +DISCLAIMER: +Please note that the software is provided AS IS and we cannot +provide support for optimizations, adaptations, integration, +ports to other platforms or device drivers! diff --git a/libraries/arduino-lmic-master/doc/IBM-release-notes.txt b/libraries/arduino-lmic-master/doc/IBM-release-notes.txt new file mode 100644 index 0000000..53d2cb9 --- /dev/null +++ b/libraries/arduino-lmic-master/doc/IBM-release-notes.txt @@ -0,0 +1,38 @@ +============================================================================== +LMIC VERSION 1.6 (13-July-2015) +--------------------------------- + + - License changed to BSD + - Modem included, see LMiC-Modem.pdf and examples/modem + - Additional stm32 hardware and Blipper board specific peripheral code + + +============================================================================== +LMIC VERSION 1.5 (8-May-2015) +------------------------------ + + - fixed condition in convFreq() + + - fixed freq*100 bug and freq==0 bug for CFList + + - fixed TX scheduling bug + + - better support for GNU compiler toolchain + + +============================================================================== +LMIC VERSION 1.4 (17-Mar-2015) +------------------------------- + + - changed API: inverted port indicator flag in LMIC.txrxFlags + (now TXRX_PORT, previously TXRX_NOPORT) + + - fixed offset OFF_CFLIST constant + + - changed CRC-16 algorithm for beacons to CCITT(XMODEM) polynomial + + - fixed radio driver (low data rate optimization for SF11+SF12 only for BW125) + + - fixed timer rollover handling in job queue + +============================================================================== diff --git a/libraries/arduino-lmic-master/doc/LMIC-FSM.cdd b/libraries/arduino-lmic-master/doc/LMIC-FSM.cdd new file mode 100644 index 0000000..4b158a8 --- /dev/null +++ b/libraries/arduino-lmic-master/doc/LMIC-FSM.cdd @@ -0,0 +1,802 @@ + + + + + + + + + + + + + +Initialize + + + + + + + +LMIC_init() + + + + + + + +Idle + + + + + + + + + +LMIC has code for class A, and class B devices. But nobody uses class B + + + + + + + +LMIC_sendWithCallback() &&

    +deviceAddr != 0 + + + + + + + +Normal Class A + +Transmit + + +RX1 + + +RX2 + + +Prepare to send + + +Wait for RX1 + + +Wait for RX2 + + +TX complete + + +Prepare frame + + +Prepare

    +MAC frame + + +Post RX + +If valid application message, deliver to app + + + + + + + + + + + +LMIC_reset() + + + + + + + + + + + + + +Internally driven based

    + on when it's OK to send. + + + + + + + + + + + +No downlink received + + + + + + + + + + + +No Retrans;

    +txDone() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +no downlink

    +received + + + + + + + + + + + +Downlink received + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Downlink received + + + + + + + + + + + + + +txDone() + + + + + + + + + + + + + + + +MAC downlink needs

    +reply; txDone() + + + + + + + + + + + + + + + +Retransmit

    +needed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Join + +Prepare Join Request + + +Prepare to send + + +Transmit + + +Wait for Join RX1 + + +RX1 + + +Wait for Join RX2 + + +RX2 + + +Process Join Accept + + +JOIN TX Complete + + +JOIN Failed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +LMIC_reset() + + + + + + + + + + + + + + + + + diff --git a/libraries/arduino-lmic-master/doc/LMIC-FSM.pdf b/libraries/arduino-lmic-master/doc/LMIC-FSM.pdf new file mode 100644 index 0000000..6ecea79 Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LMIC-FSM.pdf differ diff --git a/libraries/arduino-lmic-master/doc/LMIC-structure-diagram.cdd b/libraries/arduino-lmic-master/doc/LMIC-structure-diagram.cdd new file mode 100644 index 0000000..f7d847f --- /dev/null +++ b/libraries/arduino-lmic-master/doc/LMIC-structure-diagram.cdd @@ -0,0 +1,140 @@ + + + + + + + + + + + +LMIC + + + +Radio + + + + + + + + + +Chip + + + + + + + + + +Radio layer just does what it's told.

    +It's dumb. "Send these bytes" or "start listening" + + + +Hardware: SX1276 + + + +Secure Element + + + + + + + + + +AES Decrypt + + + + + + + + + diff --git a/libraries/arduino-lmic-master/doc/LMIC-structure-diagram.pdf b/libraries/arduino-lmic-master/doc/LMIC-structure-diagram.pdf new file mode 100644 index 0000000..4dd1850 Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LMIC-structure-diagram.pdf differ diff --git a/libraries/arduino-lmic-master/doc/LMIC-v3.3.0-redline.pdf b/libraries/arduino-lmic-master/doc/LMIC-v3.3.0-redline.pdf new file mode 100644 index 0000000..bb69ddc Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LMIC-v3.3.0-redline.pdf differ diff --git a/libraries/arduino-lmic-master/doc/LMIC-v3.3.0.docx b/libraries/arduino-lmic-master/doc/LMIC-v3.3.0.docx new file mode 100644 index 0000000..1971141 Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LMIC-v3.3.0.docx differ diff --git a/libraries/arduino-lmic-master/doc/LMIC-v3.3.0.pdf b/libraries/arduino-lmic-master/doc/LMIC-v3.3.0.pdf new file mode 100644 index 0000000..53a2dd0 Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LMIC-v3.3.0.pdf differ diff --git a/libraries/arduino-lmic-master/doc/LoRaWAN-at-a-glance.pdf b/libraries/arduino-lmic-master/doc/LoRaWAN-at-a-glance.pdf new file mode 100644 index 0000000..00376ff Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LoRaWAN-at-a-glance.pdf differ diff --git a/libraries/arduino-lmic-master/doc/LoRaWAN-at-a-glance.vsdx b/libraries/arduino-lmic-master/doc/LoRaWAN-at-a-glance.vsdx new file mode 100644 index 0000000..4f10f16 Binary files /dev/null and b/libraries/arduino-lmic-master/doc/LoRaWAN-at-a-glance.vsdx differ diff --git a/libraries/arduino-lmic-master/doc/README.md b/libraries/arduino-lmic-master/doc/README.md new file mode 100644 index 0000000..7b513b5 --- /dev/null +++ b/libraries/arduino-lmic-master/doc/README.md @@ -0,0 +1,30 @@ +# LMIC Documentation + +This directory contains documentation on the use and implementation of the LMIC. + +## Usage documentation + +- [`LMIC-v3.3.0.pdf`](./LMIC-v3.3.0.pdf): API documentation on the LMIC as of 3.3.0. +- [`LMIC-v3.3.0-redline.pdf`](./LMIC-v3.3.0-redline.pdf): changes in the document since last update, marked up by Litera Workshare Compare. + +## Background information + +- [`LoRaWAN-at-a-glance.pdf`](./LoRaWAN-at-a-glance.pdf): a wall chart showing key features of the LoRaWAN 1.0.3 protocol. + +## Implementation documentation + +- [`RadioDriver.md`](./RadioDriver.md): documentation of the radio driver interface. +- [`LMIC-structure-diagram.pdf`](./LMIC-structure-diagram.pdf): a structural diagram of the LMIC. This is somewhat UML like. +- [`LMIC-FSM.pdf`](./LMIC-FSM.pdf): the operating logic of the LMIC, modeled as a finite state machine. As of version 3.2, this model is idealized; the actual implementation is not an explicit state machine. However, if you search for `os_setCallback()` and `os_setTimedCallback()`, you will see the links between event callbacks, and that will generally correspond to the implementation. The FSM diagram doesn't show class-B or class-C operation as yet. + +## Historical information + +- `IBM-DISCLAIMER.txt` and `IBM-release-notes.txt` are artifacts of the original IBM distribution, retained for reference. + +## Meta + +Source files are included for documents that have separate sources. + +- The source for the API documentation is a Microsoft Word file. +- The source for "LoRaWAN at a glance" is a Visio file. +- The sources for LMIC-FSM and LMIC-structure are [Cadifra](https://www.cadifra.com/) files. Cadifra is an inexpensive ($50) commercial tool that the author uses as a UML whiteboard; it's really lightweight and very cleanly implemented on Windows. diff --git a/libraries/arduino-lmic-master/doc/RadioDriver.md b/libraries/arduino-lmic-master/doc/RadioDriver.md new file mode 100644 index 0000000..1473e61 --- /dev/null +++ b/libraries/arduino-lmic-master/doc/RadioDriver.md @@ -0,0 +1,160 @@ +# Radio Driver parameters + + + + + + + +- [Radio Driver Operation](#radio-driver-operation) + - [`os_radio(RADIO_RST)`](#os_radioradio_rst) + - [`os_radio(RADIO_TX)`](#os_radioradio_tx) + - [`os_radio(RADIO_RX)`](#os_radioradio_rx) + - [`os_radio(RADIO_RXON)`](#os_radioradio_rxon) +- [Common parameters](#common-parameters) + - [`LMIC.rps` (IN)](#lmicrps-in) + - [`LMIC.freq` (IN)](#lmicfreq-in) + - [`LMIC.saveIrqFlags` (OUT)](#lmicsaveirqflags-out) + - [`LMIC.osjob` (IN/OUT)](#lmicosjob-inout) +- [Transmit parameters](#transmit-parameters) + - [`LMIC.radio_txpow` (IN)](#lmicradio_txpow-in) + - [`LMIC.frame[]` (IN)](#lmicframe-in) + - [`LMIC.datalen` (IN)](#lmicdatalen-in) + - [`LMIC.txend` (OUT)](#lmictxend-out) +- [Receive parameters](#receive-parameters) + - [`LMIC.frame[]` (OUT)](#lmicframe-out) + - [`LMIC.datalen` (OUT)](#lmicdatalen-out) + - [`LMIC.rxtime` (IN/OUT)](#lmicrxtime-inout) + - [`LMIC.lbt_ticks` (IN)](#lmiclbt_ticks-in) + - [`LMIC.lbt_dbmax` (IN)](#lmiclbt_dbmax-in) + - [`LMIC.rxsyms` (IN)](#lmicrxsyms-in) + - [`LMIC.noRXIQinversion` (IN)](#lmicnorxiqinversion-in) + - [`LMIC.snr` (OUT)](#lmicsnr-out) + - [`LMIC.rssi` (OUT)](#lmicrssi-out) + + + + + +## Radio Driver Operation + +The LMIC radio driver operates asynchronously. Operations are started by calling the `os_radio()` function with a parameter describing the operation to be performed. + +Various parameters in the LMIC structure are as input to control the operation; others are updated to return results. + +### `os_radio(RADIO_RST)` + +The radio is reset, and put to sleep. This operation is synchronous. + +### `os_radio(RADIO_TX)` + +A frame is transmitted. The parameters are given in [common parameters](#common-parameters) and [transmit parameters](#transmit-parameters). + +When the operation completes, `LMIC.osjob` is scheduled. + +### `os_radio(RADIO_RX)` + +A single frame is received ad the specified time, and the radio is put back to sleep if no frame is found. + +When the operation completes, `LMIC.osjob` is scheduled. + +### `os_radio(RADIO_RXON)` + +The radio is placed in continuous receive mode. If a frame is received, `LMIC.osjob` is scheduled. Continuous receive is canceled by calling [`os_radio(RADIO_RST)`](#os_radioradio_rst). + +This operation is not supported in FSK mode. + +### `os_radio(RADIO_TX_AT)` + +This is like `os_radio(RADIO_TX)`, but the transmission is scheduled at `LMIC.txend`. + +## Common parameters + +### `LMIC.rps` (IN) + +This is the "radio parameter setting", and it encodes several radio settings. + +- Spreading factor: FSK or SF7..12 +- Bandwidth: 125, 250 or 500 kHz +- Coding Rate: 4/5, 4/6, 4/7 or 4/8. +- CRC enabled/disabled +- Implicit header mode on/off. (If on, receive length must be known in advance.) + +### `LMIC.freq` (IN) + +This specifies the frequency, in Hertz. + +### `LMIC.saveIrqFlags` (OUT) + +Updated for LoRa operations only; the IRQ flags at the time of interrupt. + +### `LMIC.osjob` (IN/OUT) + +When asynchronous operations complete, `LMIC.osjob.func` is used as the callback function, and `LMIC.osjob` is used to schedule the work. + +## Transmit parameters + +### `LMIC.radio_txpow` (IN) + +This specifies the transmit power in dBm. + +### `LMIC.frame[]` (IN) + +The array of data to be sent. + +### `LMIC.datalen` (IN) + +The length of the array to be sent. + +### `LMIC.txend` (IN, OUT) + +For `RADIO_TX_AT`, an input parameter, for the scheduled TX time. + +For all transmissions, updated to the OS time at which the TX end interrupt was recognized. + +## Receive parameters + +### `LMIC.frame[]` (OUT) + +Filled with data received. + +### `LMIC.datalen` (OUT) + +Set to number of bytes received in total. + +### `LMIC.rxtime` (IN/OUT) + +Input: When to start receiving, in OS tick time. + +Output: time of RXDONE interrupt. (Note: FSK timeout doesn't currently set this cell on RX timeout.) + +### `LMIC.lbt_ticks` (IN) + +How long to monitor for LBT, in OS ticks. + +### `LMIC.lbt_dbmax` (IN) + +Maximum RSSI on channel before transmit. + +### `LMIC.rxsyms` (IN) + +The timeout in symbols. Only used for `os_radio(RADIO_RX)`; not used for continuous receive. + +### `LMIC.noRXIQinversion` (IN) + +If true, disable IQ inversion during receive. + +### `LMIC.snr` (OUT) + +Set to SNR * 4 after LoRa receive. (Set to 0 for FSK receive.) + +### `LMIC.rssi` (OUT) + +Set to RSSI + `RSSI_OFF` after LoRa receive. (Set to 0 for FSK receive; `RSSI_OFF` is 64.) You must subtract RSSI_OFF from `LMIC.rssi` to get the RSSI in dB. diff --git a/libraries/arduino-lmic-master/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/libraries/arduino-lmic-master/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino new file mode 100644 index 0000000..c6c6508 --- /dev/null +++ b/libraries/arduino-lmic-master/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -0,0 +1,1281 @@ +/* + +Module: compliance-otaa-halconfig.ino + +Function: + Test program for developing and checking LMIC compliance test support. + +Copyright and License: + Please see accompanying LICENSE file. + +Author: + Terry Moore, MCCI Corporation March 2019 + +*/ + +#include + +#include +#include +#include + +#include +class cEventQueue; + +#define APPLICATION_VERSION ARDUINO_LMIC_VERSION_CALC(3,0,99,10) + +// +// For compliance tests with the RWC5020A, we use the default addresses +// from the tester; except that we use APPKEY 0,..., 0, 2, to avoid +// collisions with a registered app on TTN. +// + +// This EUI must be in little-endian format, so least-significant-byte +// first. This corresponds to 0x0000000000000001 +// static const u1_t PROGMEM APPEUI[8]= { 1, 0, 0, 0, 0, 0, 0, 0 }; +void os_getArtEui (u1_t* buf) { memset(buf, 0, 8); buf[0] = 1; } + +// This should also be in little endian format, see above. +// This corresponds to 0x0000000000000001 +// static const u1_t PROGMEM DEVEUI[8]= { 1, 0, 0, 0, 0, 0, 0, 0 }; +void os_getDevEui (u1_t* buf) { memset(buf, 0, 8); buf[0] = 1; } + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). +// static const u1_t PROGMEM APPKEY[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }; +void os_getDevKey (u1_t* buf) { memset(buf, 0, 16); buf[15] = 2; } + +// this data must be kept short -- max is 11 bytes for US DR0 +static uint8_t mydata[] = { 0xCA, 0xFE, 0xF0, 0x0D }; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 5; + +// global flag for test mode. +bool g_fTestMode = false; + +// forward declarations +lmic_event_cb_t myEventCb; +lmic_rxmessage_cb_t myRxMessageCb; + +const char * const evNames[] = { LMIC_EVENT_NAME_TABLE__INIT }; + +static void rtccount_begin(); +static uint16_t rtccount_read(); + +#define NEED_USBD_LL_ConnectionState 0 +#ifdef ARDUINO_ARCH_STM32 +# ifdef _mcci_arduino_version +# if _mcci_arduino_version < _mcci_arduino_version_calc(2, 5, 0, 10) +# undef NEED_USBD_LL_ConnectionState +# define NEED_USBD_LL_ConnectionState 1 +# endif // _mcci_arduino_version < _mcci_arduino_version_calc(2, 5, 0, 10) +# endif // def _mcci_arduino_version +#endif // def ARDUINO_ARCH_STM32 + +#define NEED_STM32_ClockCalibration 0 +#ifdef ARDUINO_ARCH_STM32 +# ifdef _mcci_arduino_version +# if _mcci_arduino_version <= _mcci_arduino_version_calc(2, 5, 0, 10) +# undef NEED_STM32_ClockCalibration +# define NEED_STM32_ClockCalibration 1 +# endif // _mcci_arduino_version <= _mcci_arduino_version_calc(2, 5, 0, 10) +# endif // def _mcci_arduino_version +# define SUPPORT_STM32_ClockCalibration 1 +#else +# define SUPPORT_STM32_ClockCalibration 0 +#endif // def ARDUINO_ARCH_STM32 + +/* + +Name: myEventCb() + +Function: + lmic_event_cb_t myEventCb; + + extern "C" { void myEventCb(void *pUserData, ev_t ev); } + +Description: + This function is registered for event notifications from the LMIC + during setup() processing. Its main job is to display events in a + user-friendly way. + +Returns: + No explicit result. + +*/ + +static osjobcbfn_t eventjob_cb; + +class cEventQueue { +public: + cEventQueue() {}; + ~cEventQueue() {}; + + struct eventnode_t { + osjob_t job; + ev_t event; + const char *pMessage; + uint32_t datum; + ostime_t time; + ostime_t txend; + ostime_t rxtime; + ostime_t globalDutyAvail; + u4_t nLateRx; + ostime_t ticksLateRx; + u4_t freq; + u2_t rtccount; + u2_t opmode; + u2_t fcntDn; + u2_t fcntUp; + rxsyms_t rxsyms; + rps_t rps; + u1_t txChnl; + u1_t datarate; + u1_t txrxFlags; + u1_t saveIrqFlags; + }; + + bool getEvent(eventnode_t &node) { + if (m_head == m_tail) { + return false; + } + node = m_queue[m_head]; + if (++m_head == sizeof(m_queue) / sizeof(m_queue[0])) { + m_head = 0; + } + return true; + } + + bool putEvent(ev_t event, const char *pMessage = nullptr, uint32_t datum = 0) { + auto i = m_tail + 1; + if (i == sizeof(m_queue) / sizeof(m_queue[0])) { + i = 0; + } + if (i != m_head) { + auto const pn = &m_queue[m_tail]; + pn->job = LMIC.osjob; + pn->time = os_getTime(); + pn->rtccount = rtccount_read(); + pn->txend = LMIC.txend; + pn->rxtime = LMIC.rxtime; + pn->globalDutyAvail = LMIC.globalDutyAvail; + pn->event = event; + pn->pMessage = pMessage; + pn->datum = datum; + pn->nLateRx = LMIC.radio.rxlate_count; + pn->ticksLateRx = LMIC.radio.rxlate_ticks; + pn->freq = LMIC.freq; + pn->opmode = LMIC.opmode; + pn->fcntDn = (u2_t) LMIC.seqnoDn; + pn->fcntUp = (u2_t) LMIC.seqnoUp; + pn->rxsyms = LMIC.rxsyms; + pn->rps = LMIC.rps; + pn->txChnl = LMIC.txChnl; + pn->datarate = LMIC.datarate; + pn->txrxFlags = LMIC.txrxFlags; + pn->saveIrqFlags = LMIC.saveIrqFlags; + m_tail = i; + return true; + } else { + return false; + } + } + +private: + unsigned m_head, m_tail; + eventnode_t m_queue[32]; + osjob_t m_job; +}; + +cEventQueue eventQueue; + +#if LMIC_ENABLE_event_logging +extern "C" { + void LMICOS_logEvent(const char *pMessage); + void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); +} + +void LMICOS_logEvent(const char *pMessage) + { + eventQueue.putEvent(ev_t(-1), pMessage); + } + +void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) + { + eventQueue.putEvent(ev_t(-2), pMessage, datum); + } +#endif // LMIC_ENABLE_event_logging + +hal_failure_handler_t log_assertion; + +void log_assertion(const char *pMessage, uint16_t line) { + eventQueue.putEvent(ev_t(-3), pMessage, line); + eventPrintAll(); + Serial.println(F("***HALTED BY ASSERT***")); + while (true) + yield(); +} + +bool lastWasTxStart; +uint32_t lastTxStartTime; + +void myEventCb(void *pUserData, ev_t ev) { + eventQueue.putEvent(ev); + + if (ev == EV_TXSTART) { + lastWasTxStart = true; + lastTxStartTime = millis(); + } else if (ev == EV_RXSTART) { + lastWasTxStart = false; + } + if (ev == EV_JOINING) { + setupForNetwork(true); + } else if (ev == EV_JOINED) { + setupForNetwork(false); + } +} + +void eventPrint(cEventQueue::eventnode_t &e); +void printFcnts(cEventQueue::eventnode_t &e); +void printTxend(cEventQueue::eventnode_t &e); +void printRxtime(cEventQueue::eventnode_t &e); +void printLateStats(cEventQueue::eventnode_t &e); + +void eventPrintAll(void) { + while (eventPrintOne()) + ; +} + +bool eventPrintOne(void) { + cEventQueue::eventnode_t e; + if (eventQueue.getEvent(e)) { + eventPrint(e); + return true; + } else { + return false; + } +} + +static void eventjob_cb(osjob_t *j) { + eventPrintAll(); +} + +const char *getSfName(rps_t rps) { + const char * const t[] = { "FSK", "SF7", "SF8", "SF9", "SF10", "SF11", "SF12", "SFrfu" }; + return t[getSf(rps)]; +} + +const char *getBwName(rps_t rps) { + const char * const t[] = { "BW125", "BW250", "BW500", "BWrfu" }; + return t[getBw(rps)]; +} + +const char *getCrName(rps_t rps) { + const char * const t[] = { "CR 4/5", "CR 4/6", "CR 4/7", "CR 4/8" }; + return t[getCr(rps)]; +} + +const char *getCrcName(rps_t rps) { + return getNocrc(rps) ? "NoCrc" : "Crc"; +} + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void printHex4(unsigned v) { + printHex2(v >> 8u); + printHex2(v); +} + +void printSpace(void) { + Serial.print(' '); +} + +void printFreq(u4_t freq) { + Serial.print(F(": freq=")); + Serial.print(freq / 1000000); + Serial.print('.'); + Serial.print((freq % 1000000) / 100000); +} + +void printRps(rps_t rps) { + Serial.print(F(" rps=0x")); printHex2(rps); + Serial.print(F(" (")); Serial.print(getSfName(rps)); + printSpace(); Serial.print(getBwName(rps)); + printSpace(); Serial.print(getCrName(rps)); + printSpace(); Serial.print(getCrcName(rps)); + Serial.print(F(" IH=")); Serial.print(unsigned(getIh(rps))); + Serial.print(')'); +} + +void printOpmode(uint16_t opmode, char sep = ',') { + if (sep != 0) + Serial.print(sep); + Serial.print(F(" opmode=")); Serial.print(opmode, HEX); +} + +void printTxend(cEventQueue::eventnode_t &e) { + Serial.print(F(", txend=")); Serial.print(e.txend); + Serial.print(F(", avail=")); Serial.print(e.globalDutyAvail); +} + +void printRxtime(cEventQueue::eventnode_t &e) { + Serial.print(F(", rxtime=")); Serial.print(e.rxtime); +} + +void printTxChnl(u1_t txChnl) { + Serial.print(F(": ch=")); + Serial.print(unsigned(txChnl)); +} + +void printDatarate(u1_t datarate) { + Serial.print(F(", datarate=")); Serial.print(unsigned(datarate)); +} + +void printTxrxflags(u1_t txrxFlags) { + Serial.print(F(", txrxFlags=0x")); printHex2(txrxFlags); + if (txrxFlags & TXRX_ACK) + Serial.print(F("; Received ack")); +} + +void printSaveIrqFlags(u1_t saveIrqFlags) { + Serial.print(F(", saveIrqFlags 0x")); + printHex2(saveIrqFlags); +} + +void printLateStats(cEventQueue::eventnode_t &e) { + Serial.print(F(", nLateRx=")); + Serial.print(e.nLateRx); + Serial.print(F(" ticks=")); + Serial.print(e.ticksLateRx); +} + +void printFcnts(cEventQueue::eventnode_t &e) { + Serial.print(F(", FcntUp=")); + printHex4(e.fcntUp); + Serial.print(F(", FcntDn=")); + printHex4(e.fcntDn); +} + +#if LMIC_ENABLE_event_logging +// dump all the registers. +void printAllRegisters(void) { + uint8_t regbuf[0x80]; + regbuf[0] = 0; + hal_spi_read(1, regbuf + 1, sizeof(regbuf) - 1); + + for (unsigned i = 0; i < sizeof(regbuf); ++i) { + if (i % 16 == 0) { + printNl(); + printHex2(i); + } + Serial.print(((i % 8) == 0) ? F(" - ") : F(" ")); + printHex2(regbuf[i]); + } + + // reset the radio, just in case the register dump caused issues. + hal_pin_rst(0); + delay(2); + hal_pin_rst(2); + delay(6); + + // restore the radio to idle. + const uint8_t opmode = 0x88; // LoRa and sleep. + hal_spi_write(0x81, &opmode, 1); +} +#endif + +void printNl(void) { + Serial.println(); +} + +void eventPrint(cEventQueue::eventnode_t &e) { + ev_t ev = e.event; + + Serial.print(e.time); + Serial.print(F(" (")); + Serial.print(osticks2ms(e.time)); +#if SUPPORT_STM32_ClockCalibration + Serial.print(F(" ms, lptim1=")); + Serial.print(e.rtccount); + Serial.print(F("): ")); +#else + Serial.print(F(" ms): ")); +#endif + + if (ev == ev_t(-1) || ev == ev_t(-2)) { + Serial.print(e.pMessage); + if (ev == ev_t(-2)) { + Serial.print(F(", datum=0x")); Serial.print(e.datum, HEX); + } + printOpmode(e.opmode, '.'); + } else if (ev == ev_t(-3)) { + Serial.print(e.pMessage); + Serial.print(F(", line ")); Serial.print(e.datum); + printFreq(e.freq); + printTxend(e); + printTxChnl(e.txChnl); + printRps(e.rps); + printOpmode(e.opmode); + printTxrxflags(e.txrxFlags); + printSaveIrqFlags(e.saveIrqFlags); + printLateStats(e); +#if LMIC_ENABLE_event_logging + printAllRegisters(); +#endif + } else { + if (ev < sizeof(evNames) / sizeof(evNames[0])) { + Serial.print(evNames[ev]); + } else { + Serial.print(F("Unknown event: ")); + Serial.print((unsigned) ev); + } + switch(ev) { + case EV_SCAN_TIMEOUT: + break; + case EV_BEACON_FOUND: + break; + case EV_BEACON_MISSED: + break; + case EV_BEACON_TRACKED: + break; + case EV_JOINING: + break; + + case EV_JOINED: + printTxChnl(e.txChnl); + printNl(); + do { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print(F("netid: ")); + Serial.println(netid, DEC); + Serial.print(F("devaddr: ")); + Serial.println(devaddr, HEX); + Serial.print(F("artKey: ")); + for (size_t i=0; i 0) + printHex2(pMessage[0]); + Serial.print(F(" length ")); + Serial.println((unsigned) nMessage); + } + return; + } + default: + // continue. + break; + } + + Serial.print(F("Received message on port ")); + Serial.print(port); + Serial.print(F(": ")); + Serial.print(unsigned(nMessage)); + Serial.println(F(" bytes")); + } + +lmic_txmessage_cb_t sendComplete; + +void do_send(osjob_t* j){ + // Check if there is not a current TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND, not sending")); + sendComplete(j, 0); + } else if (g_fTestMode) { + Serial.println(F("test mode, not sending")); + } else { + // Prepare upstream data transmission at the next possible time. + if (LMIC_sendWithCallback_strict(1, mydata, sizeof(mydata), 0, sendComplete, j) == 0) { + Serial.println(F("Packet queued")); + } else { + Serial.println(F("Packet queue failure; sleeping")); + sendComplete(j, 0); + } + } +} + +void sendComplete( + void *pUserData, + int fSuccess +) { + osjob_t * const j = (osjob_t *) pUserData; + + if (! fSuccess) + Serial.println(F("sendComplete: uplink failed")); + + if (! g_fTestMode) { + // Schedule next transmission + os_setTimedCallback(j, os_getTime()+sec2osticks(TX_INTERVAL), do_send); + } +} + +void myFail(const char *pMessage) { + pinMode(LED_BUILTIN, OUTPUT); + for (;;) { + // alert + Serial.println(pMessage); + // flash lights, sleep. + for (int i = 0; i < 5; ++i) { + digitalWrite(LED_BUILTIN, 1); + delay(100); + digitalWrite(LED_BUILTIN, 0); + delay(900); + } + } +} + +void setup() { + delay(5000); + while (! Serial) + ; + Serial.begin(115200); + setup_printSignOn(); + setup_calibrateSystemClock(); + + // LMIC init using the computed target + const auto pPinMap = Arduino_LMIC::GetPinmap_ThisBoard(); + + // don't die mysteriously; die noisily. + if (pPinMap == nullptr) { + myFail("board not known to library; add pinmap or update getconfig_thisboard.cpp"); + } + + // now that we have a pinmap, initalize the low levels accordingly. + hal_set_failure_handler(log_assertion); + os_init_ex(pPinMap); + + // LMIC_reset() doesn't affect callbacks, so we can do this first. + if (! (LMIC_registerRxMessageCb(myRxMessageCb, /* userData */ nullptr) && + LMIC_registerEventCb(myEventCb, /* userData */ nullptr))) { + myFail("couldn't register callbacks"); + } + + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // do the network-specific setup prior to join. + setupForNetwork(false); + + // Start job (sending automatically starts OTAA too) + do_send(&sendjob); +} + +void setup_printSignOnDashLine(void) + { + for (unsigned i = 0; i < 78; ++i) + Serial.print('-'); + + printNl(); + } + +static constexpr const char *filebasename2(const char *s, const char *p) { + return p[0] == '\0' ? s : + (p[0] == '/' || p[0] == '\\') ? filebasename2(p + 1, p + 1) : + filebasename2(s, p + 1) ; +} + +static constexpr const char *filebasename(const char *s) + { + return filebasename2(s, s); + } + +void printVersionFragment(char sep, uint8_t v) { + if (sep != 0) { + Serial.print(sep); + } + Serial.print(unsigned(v)); +} + +void printVersion(uint32_t v) { + printVersionFragment(0, uint8_t(v >> 24u)); + printVersionFragment('.', uint8_t(v >> 16u)); + printVersionFragment('.', uint8_t(v >> 8u)); + if (uint8_t(v) != 0) { + printVersionFragment('.', uint8_t(v)); + } +} + +void setup_printSignOn() + { + printNl(); + + setup_printSignOnDashLine(); + + Serial.println(filebasename(__FILE__)); + Serial.print(F("Version ")); + printVersion(APPLICATION_VERSION); + Serial.print(F("\nLMIC version ")); + printVersion(ARDUINO_LMIC_VERSION); + Serial.print(F(" configured for region ")); + Serial.print(CFG_region); + Serial.println(F(".\nRemember to select 'Line Ending: Newline' at the bottom of the monitor window.")); + + setup_printSignOnDashLine(); + printNl(); + } + +void setupForNetwork(bool preJoin) { +#if CFG_LMIC_US_like + LMIC_selectSubBand(0); +#endif +} + +void loop() { + os_runloop_once(); + + if (lastWasTxStart && millis() - lastTxStartTime > 10000) { + /* ugh. TX timed out */ + Serial.println(F("Tx timed out")); +#if LMIC_ENABLE_event_logging + printAllRegisters(); +#endif + LMIC_clrTxData(); + lastWasTxStart = false; + } + + if ((LMIC.opmode & OP_TXRXPEND) == 0 && + !os_queryTimeCriticalJobs(ms2osticks(1000))) { + eventPrintAll(); + } +} + + +// there's a problem with running 2.5 of the MCCI STM32 BSPs; +// hack around it. +#if NEED_USBD_LL_ConnectionState +uint32_t USBD_LL_ConnectionState(void) { + return 1; +} +#endif // NEED_USBD_LL_ConnectionState + +static constexpr bool kMustCalibrateLSE = NEED_STM32_ClockCalibration; // _mcci_arduino_version indicates that LSE clock is used. +static constexpr bool kCanCalibrateLSE = SUPPORT_STM32_ClockCalibration; + +void setup_calibrateSystemClock(void) { + if (kMustCalibrateLSE) { + Serial.println("need to calibrate clock"); +#if NEED_STM32_ClockCalibration + Stm32_CalibrateSystemClock(); +#endif // NEED_STM32_ClockCalibration + Serial.println("setting LPTIM1"); + // set clock rate error to 0.4% + LMIC_setClockError(4 * MAX_CLOCK_ERROR / 1000); + rtccount_begin(); + } else if (kCanCalibrateLSE) { + Serial.println("assuming BIOS has calibrated clock, setting LPTIM1"); + LMIC_setClockError(4 * MAX_CLOCK_ERROR / 1000); + rtccount_begin(); + } else { + Serial.println("calibration not supported"); + } +} + +#if NEED_STM32_ClockCalibration + +// RTC needs to be initialized before we calibrate the clock. +bool rtcbegin() { + RTC_TimeTypeDef Time; + RTC_DateTypeDef Date; + uint32_t RtcClock; + RTC_HandleTypeDef hRtc; + + memset(&hRtc, 0, sizeof(hRtc)); + + hRtc.Instance = RTC; + hRtc.Init.HourFormat = RTC_HOURFORMAT_24; + RtcClock = __HAL_RCC_GET_RTC_SOURCE(); + if (RtcClock == RCC_RTCCLKSOURCE_LSI) + { + hRtc.Init.AsynchPrediv = 37 - 1; /* 37kHz / 37 = 1000Hz */ + hRtc.Init.SynchPrediv = 1000 - 1; /* 1000Hz / 1000 = 1Hz */ + } + else if (RtcClock == RCC_RTCCLKSOURCE_LSE) + { + hRtc.Init.AsynchPrediv = 128 - 1; /* 32768Hz / 128 = 256Hz */ + hRtc.Init.SynchPrediv = 256 - 1; /* 256Hz / 256 = 1Hz */ + } + else + { + /* + || use HSE clock -- + || we don't support use of HSE as RTC because it's connected to + || TCXO_OUT, and that's controlled by the LoRaWAN software. + */ + Serial.println( + " HSE can not be used for RTC clock!" + ); + return false; + } + + + hRtc.Init.OutPut = RTC_OUTPUT_DISABLE; + hRtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; + hRtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; + hRtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; + + if (HAL_RTC_Init(&hRtc) != HAL_OK) + { + Serial.println( + "HAL_RTC_Init() failed" + ); + return false; + } + + /* Initialize RTC and set the Time and Date */ + if (HAL_RTCEx_BKUPRead(&hRtc, RTC_BKP_DR0) != 0x32F2) + { + Time.Hours = 0x0; + Time.Minutes = 0x0; + Time.Seconds = 0x0; + Time.SubSeconds = 0x0; + Time.TimeFormat = RTC_HOURFORMAT12_AM; + Time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + Time.StoreOperation = RTC_STOREOPERATION_RESET; + + if (HAL_RTC_SetTime( + &hRtc, + &Time, + RTC_FORMAT_BIN + ) != HAL_OK) + { + Serial.print( + "HAL_RTC_SetTime() failed" + ); + return false; + } + + /* Sunday 1st January 2017 */ + Date.WeekDay = RTC_WEEKDAY_SUNDAY; + Date.Month = RTC_MONTH_JANUARY; + Date.Date = 0x1; + Date.Year = 0x0; + + if (HAL_RTC_SetDate( + &hRtc, + &Date, + RTC_FORMAT_BIN + ) != HAL_OK) + { + Serial.print( + "HAL_RTC_SetDate() failed" + ); + return false; + } + + HAL_RTCEx_BKUPWrite(&hRtc, RTC_BKP_DR0, 0x32F2); + } + + /* Enable Direct Read of the calendar registers (not through Shadow) */ + HAL_RTCEx_EnableBypassShadow(&hRtc); + + HAL_RTC_DeactivateAlarm(&hRtc, RTC_ALARM_A); + return true; +} + +extern "C" { + +static volatile uint32_t *gs_pAlarm; +static RTC_HandleTypeDef *gs_phRtc; + +void RTC_IRQHandler(void) + { + HAL_RTC_AlarmIRQHandler(gs_phRtc); + } + +void HAL_RTC_AlarmAEventCallback( + RTC_HandleTypeDef * hRtc + ) + { + if (gs_pAlarm) + *gs_pAlarm = 1; + } + +void HAL_RTC_MspInit( + RTC_HandleTypeDef * hRtc + ) + { + if (hRtc->Instance == RTC) + { + /* USER CODE BEGIN RTC_MspInit 0 */ + + /* USER CODE END RTC_MspInit 0 */ + /* Peripheral clock enable */ + __HAL_RCC_RTC_ENABLE(); + /* USER CODE BEGIN RTC_MspInit 1 */ + HAL_NVIC_SetPriority(RTC_IRQn, TICK_INT_PRIORITY, 0U); + HAL_NVIC_EnableIRQ(RTC_IRQn); + /* USER CODE END RTC_MspInit 1 */ + } + } + +void HAL_RTC_MspDeInit( + RTC_HandleTypeDef * hRtc + ) + { + if (hRtc->Instance == RTC) + { + /* USER CODE BEGIN RTC_MspDeInit 0 */ + HAL_NVIC_DisableIRQ(RTC_IRQn); + /* USER CODE END RTC_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_RTC_DISABLE(); + /* USER CODE BEGIN RTC_MspDeInit 1 */ + + /* USER CODE END RTC_MspDeInit 1 */ + } + } + +uint32_t HAL_AddTick( + uint32_t delta + ) + { + extern __IO uint32_t uwTick; + // copy old interrupt-enable state to flags. + uint32_t const flags = __get_PRIMASK(); + + // disable interrupts + __set_PRIMASK(1); + + // observe uwTick, and advance it. + uint32_t const tickCount = uwTick + delta; + + // save uwTick + uwTick = tickCount; + + // restore interrupts (does nothing if ints were disabled on entry) + __set_PRIMASK(flags); + + // return the new value of uwTick. + return tickCount; + } + +} /* extern "C" */ + +uint32_t Stm32_CalibrateSystemClock(void) + { + uint32_t Calib; + uint32_t CalibNew; + uint32_t CalibLow; + uint32_t CalibHigh; + uint32_t mSecond; + uint32_t mSecondNew; + uint32_t mSecondLow; + uint32_t mSecondHigh; + bool fHaveSeenLow; + bool fHaveSeenHigh; + const bool fCalibrateMSI = HAL_RCC_GetHCLKFreq() < 16000000; + + if (! rtcbegin()) { + return 0; + } + + if (fCalibrateMSI) + { + Calib = (RCC->ICSCR & RCC_ICSCR_MSITRIM) >> 24; + } + else + { + Calib = (RCC->ICSCR & RCC_ICSCR_HSITRIM) >> 8; + } + + /* preapre to loop, setting suitable defaults */ + CalibNew = Calib; + CalibLow = 0; + CalibHigh = 0; + mSecondLow = 0; + mSecondHigh = 2000000; + fHaveSeenLow = fHaveSeenHigh = false; + + /* loop until we have a new value */ + do { + /* meassure the # of millis per RTC second */ + mSecond = MeasureMicrosPerRtcSecond(); + + /* invariant: */ + if (Calib == CalibNew) + mSecondNew = mSecond; + + /* if mSecond is low, this meaans we must increase the system clock */ + if (mSecond <= 1000000) + { + Serial.print('-'); + /* + || the following condition establishes that we're + || below the target frequency, but closer than we've been + || before (mSecondLow is the previous "low" limit). If + || so, we reduce the limit, and capture the "low" calibration + || value. + */ + if (mSecond > mSecondLow) + { + mSecondLow = mSecond; + CalibLow = Calib; /* save previous calibration value */ + fHaveSeenLow = true; + } + + /* + || if we are low, and we have never exceeded the high limit, + || we can increase the clock. + */ + if (! fHaveSeenHigh) + { + if (fCalibrateMSI) + { + if (Calib < 0xFF) + { + ++Calib; + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(Calib); + } + else + break; + } + else + { + if (Calib < 0x1F) + { + ++Calib; + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(Calib); + } + else + { + break; + } + } + + /* let the clock settle */ + delay(500); + } + } + + /* if mSecond is high, we must reduce the system clock */ + else + { + Serial.print('+'); + /* + || the following condition establishes that we're + || above the target frequency, but closer than we've been + || before (mSecondHigh is the previous "high" limit). If + || so, we reduce the limit, and capture the calibration + || value. + */ + if (mSecond < mSecondHigh) + { + mSecondHigh = mSecond; + CalibHigh = Calib; + fHaveSeenHigh = true; + } + + /* + || if we are above the target frequency, and we have + || never raised the frequence, we can lower the + || frequency + */ + if (! fHaveSeenLow) + { + if (Calib == 0) + break; + + --Calib; + if (fCalibrateMSI) + { + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(Calib); + } + else + { + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(Calib); + } + delay(500); + } + } + } while ((Calib != CalibNew) && + (! fHaveSeenLow || !fHaveSeenHigh)); + + // + // We are going to take higher calibration value first and + // it allows us not to call LMIC_setClockError(). + // + if (fHaveSeenHigh) + { + mSecondNew = mSecondHigh; + CalibNew = CalibHigh; + } + else if (fHaveSeenLow) + { + mSecondNew = mSecondLow; + CalibNew = CalibLow; + } + else + { + // Use original value + Serial.println( + "?CalibrateSystemClock: can't calibrate" + ); + } + + if (CalibNew != Calib) + { + Serial.print(CalibNew < Calib ? '+' : '-'); + if (fCalibrateMSI) + { + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(CalibNew); + } + else + { + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(CalibNew); + } + delay(500); + } + + Serial.print(" 0x"); + Serial.println(CalibNew, HEX); + return CalibNew; + } + +uint32_t +MeasureMicrosPerRtcSecond( + void + ) + { + uint32_t second; + uint32_t now; + uint32_t start; + uint32_t end; + + /* get the starting time */ + second = RTC->TR & (RTC_TR_ST | RTC_TR_SU); + + /* wait for a new second to start, and capture millis() in start */ + do { + now = RTC->TR & (RTC_TR_ST | RTC_TR_SU); + start = micros(); + } while (second == now); + + /* update our second of interest */ + second = now; + + /* no point in watching the register until we get close */ + delay(500); + + /* wait for the next second to start, and capture millis() */ + do { + now = RTC->TR & (RTC_TR_ST | RTC_TR_SU); + end = micros(); + } while (second == now); + + /* return the delta */ + return end - start; + } +#endif // NEED_STM32_ClockCalibration + +#if SUPPORT_STM32_ClockCalibration +static void rtccount_begin() + { + // enable clock to LPTIM1 + __HAL_RCC_LPTIM1_CLK_ENABLE(); + auto const pLptim = LPTIM1; + + // set LPTIM1 clock to LSE clock. + __HAL_RCC_LPTIM1_CONFIG(RCC_LPTIM1CLKSOURCE_LSE); + + // disable everything so we can tweak the CFGR + pLptim->CR = 0; + + // disable interrupts (needs to be done while disabled globally) + pLptim->IER = 0; + + // upcount from selected internal clock (which is LSE) + auto rCfg = pLptim->CFGR & ~0x01FEEEDF; + rCfg |= 0; + pLptim->CFGR = rCfg; + + // enable the counter but don't start it + pLptim->CR = LPTIM_CR_ENABLE; + delayMicroseconds(100); + + // set ARR to max value so we can count from 0 to 0xFFFF. + // must be done after enabling. + pLptim->ARR = 0xFFFF; + + // start in continuous mode. + pLptim->CR = LPTIM_CR_ENABLE | LPTIM_CR_CNTSTRT; + } + +static uint16_t rtccount_read() + { + auto const pLptim = LPTIM1; + uint32_t v1, v2; + + for (v1 = pLptim->CNT & 0xFFFF; (v2 = pLptim->CNT & 0xFFFF) != v1; v1 = v2) + /* loop */; + + return (uint16_t) v1; + } + +#else +static void rtccount_begin() + { + // nothing + } + +static uint16_t rtccount_read() + { + return 0; + } +#endif diff --git a/libraries/arduino-lmic-master/examples/header_test/header_test.ino b/libraries/arduino-lmic-master/examples/header_test/header_test.ino new file mode 100644 index 0000000..e81cffa --- /dev/null +++ b/libraries/arduino-lmic-master/examples/header_test/header_test.ino @@ -0,0 +1,36 @@ +/* + +Module: header_test.ino + +Function: + Simple hello-world (and compile-test) app + +Copyright notice and License: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation April 2018 + +*/ + +#include + +# define STATIC_ASSERT(e) \ + void STATIC_ASSERT__(int MCCIADK_C_ASSERT_x[(e) ? 1: -1]) + +STATIC_ASSERT(ARDUINO_LMIC_VERSION >= ARDUINO_LMIC_VERSION_CALC(2,1,5,0)); + +STATIC_ASSERT(ARDUINO_LMIC_VERSION_CALC(1,2,3,4) == 0x01020304); + +STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 1); +STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 2); +STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 3); +STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 4); + +void setup() + { + } + +void loop() + { + } diff --git a/libraries/arduino-lmic-master/examples/helium-us915/helium-us915.ino b/libraries/arduino-lmic-master/examples/helium-us915/helium-us915.ino new file mode 100644 index 0000000..f18622c --- /dev/null +++ b/libraries/arduino-lmic-master/examples/helium-us915/helium-us915.ino @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example sends a valid LoRaWAN packet with payload "Hello, + * world!", using frequency and encryption settings matching those of + * the The Things Network. It's pre-configured for the Adafruit + * Feather M0 LoRa. + * + * This uses OTAA (Over-the-air activation), where where a DevEUI and + * application key is configured, which are used in an over-the-air + * activation procedure where a DevAddr and session keys are + * assigned/generated for use with all further communication. + * + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in + * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably + * violated by this sketch when left running for longer)! + + * To use this sketch, first register your application and device with + * the things network, to set or generate an AppEUI, DevEUI and AppKey. + * Multiple devices can use the same AppEUI, but each device has its own + * DevEUI and AppKey. + * + * Do not forget to define the radio type correctly in + * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * + *******************************************************************************/ + +#include +#include +#include + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8]= { FILLMEIN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]= { FILLMEIN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from the TTN console can be copied as-is. +static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +// +// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +// m0 defs ADAFRUIT_FEATHER_M0 +// +#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) +// Pin mapping for Adafruit Feather M0 LoRa, etc. +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB + .spi_freq = 8000000, +}; +#elif defined(ARDUINO_AVR_FEATHER32U4) +// Pin mapping for Adafruit Feather 32u4 LoRa, etc. +// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only +// because MCCI doesn't have a test board; probably higher frequencies +// will work. +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {7, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather 32U4 LoRa, in dB + .spi_freq = 1000000, +}; +#elif defined(ARDUINO_CATENA_4551) +// Pin mapping for Murata module / Catena 4551 +const lmic_pinmap lmic_pins = { + .nss = 7, + .rxtx = 29, + .rst = 8, + .dio = { 25, // DIO0 (IRQ) is D25 + 26, // DIO1 is D26 + 27, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000 // 8MHz +}; +#elif defined(MCCI_CATENA_4610) +#include "arduino_lmic_hal_boards.h" +const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Catena4610(); +#elif defined(ARDUINO_DISCO_L072CZ_LRWAN1) +#include "arduino_lmic_hal_boards.h" +// Pin mapping Discovery +const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Disco_L072cz_Lrwan1(); +#else +# error "Unknown target" +#endif + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("AppSKey: "); + for (size_t i=0; i +#include +#include + +#include +#include + +// we formerly would check this configuration; but now there is a flag, +// in the LMIC, LMIC.noRXIQinversion; +// if we set that during init, we get the same effect. If +// DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is +// treated as always set. +// +// #if !defined(DISABLE_INVERT_IQ_ON_RX) +// #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ +// lmic_project_config.h in arduino-lmic/project_config to set it. +// #endif + +// How often to send a packet. Note that this sketch bypasses the normal +// LMIC duty cycle limiting, so when you change anything in this sketch +// (payload length, frequency, spreading factor), be sure to check if +// this interval should not also be increased. +// See this spreadsheet for an easy airtime and duty cycle calculator: +// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc + +#define TX_INTERVAL 2000 // milliseconds +#define RX_RSSI_INTERVAL 100 // milliseconds + +// Pin mapping for Adafruit Feather M0 LoRa, etc. +// +// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +// m0 defs ADAFRUIT_FEATHER_M0 +// +#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB + .spi_freq = 8000000, +}; +#elif defined(ARDUINO_AVR_FEATHER32U4) +// Pin mapping for Adafruit Feather 32u4 LoRa, etc. +// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only +// because MCCI doesn't have a test board; probably higher frequencies +// will work. +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {7, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather 32U4 LoRa, in dB + .spi_freq = 1000000, +}; +#elif defined(ARDUINO_CATENA_4551) +const lmic_pinmap lmic_pins = { + .nss = 7, + .rxtx = 29, + .rst = 8, + .dio = { 25, // DIO0 (IRQ) is D25 + 26, // DIO1 is D26 + 27, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000 // 8MHz +}; +#else +# error "Unknown target" +#endif + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h, +// otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +// this gets callled by the library but we choose not to display any info; +// and no action is required. +void onEvent (ev_t ev) { +} + +extern "C" { +void lmic_printf(const char *fmt, ...); +}; + +void lmic_printf(const char *fmt, ...) { + if (! Serial.dtr()) + return; + + char buf[256]; + va_list ap; + + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + // in case we overflowed: + buf[sizeof(buf) - 1] = '\0'; + if (Serial.dtr()) Serial.print(buf); +} + +osjob_t txjob; +osjob_t timeoutjob; +static void tx_func (osjob_t* job); + +// Transmit the given string and call the given function afterwards +void tx(const char *str, osjobcb_t func) { + // the radio is probably in RX mode; stop it. + os_radio(RADIO_RST); + // wait a bit so the radio can come out of RX mode + delay(1); + + // prepare data + LMIC.dataLen = 0; + while (*str) + LMIC.frame[LMIC.dataLen++] = *str++; + + // set completion function. + LMIC.osjob.func = func; + + // start the transmission + os_radio(RADIO_TX); + Serial.println("TX"); +} + +// Enable rx mode and call func when a packet is received +void rx(osjobcb_t func) { + LMIC.osjob.func = func; + LMIC.rxtime = os_getTime(); // RX _now_ + // Enable "continuous" RX (e.g. without a timeout, still stops after + // receiving a packet) + os_radio(RADIO_RXON); + Serial.println("RX"); +} + +static void rxtimeout_func(osjob_t *job) { + digitalWrite(LED_BUILTIN, LOW); // off +} + +static void rx_func (osjob_t* job) { + // Blink once to confirm reception and then keep the led on + digitalWrite(LED_BUILTIN, LOW); // off + delay(10); + digitalWrite(LED_BUILTIN, HIGH); // on + + // Timeout RX (i.e. update led status) after 3 periods without RX + os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); + + // Reschedule TX so that it should not collide with the other side's + // next TX + os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); + + Serial.print("Got "); + Serial.print(LMIC.dataLen); + Serial.println(" bytes"); + Serial.write(LMIC.frame, LMIC.dataLen); + Serial.println(); + + // Restart RX + rx(rx_func); +} + +static void txdone_func (osjob_t* job) { + rx(rx_func); +} + +// log text to USART and toggle LED +static void tx_func (osjob_t* job) { + // say hello + tx("Hello, world!", txdone_func); + // reschedule job every TX_INTERVAL (plus a bit of random to prevent + // systematic collisions), unless packets are received, then rx_func + // will reschedule at half this time. + os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); +} + +// application entry point +void setup() { + // delay(3000) makes recovery from botched images much easier, as it + // gives the host time to break in to start a download. Without it, + // you get to the crash before the host can break in. + delay(3000); + + // even after the delay, we wait for the host to open the port. operator + // bool(Serial) just checks dtr(), and it tosses in a 10ms delay. + while(! Serial.dtr()) + /* wait for the PC */; + + Serial.begin(115200); + Serial.println("Starting"); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + pinMode(LED_BUILTIN, OUTPUT); + + // initialize runtime env + os_init(); + + // Set up these settings once, and use them for both TX and RX +#ifdef ARDUINO_ARCH_STM32 + LMIC_setClockError(10*65536/100); +#endif + +#if defined(CFG_eu868) + // Use a frequency in the g3 which allows 10% duty cycling. + LMIC.freq = 869525000; + // Use a medium spread factor. This can be increased up to SF12 for + // better range, but then, the interval should be (significantly) + // raised to comply with duty cycle limits as well. + LMIC.datarate = DR_SF9; + // Maximum TX power + LMIC.txpow = 27; +#elif defined(CFG_us915) + // make it easier for test, by pull the parameters up to the top of the + // block. Ideally, we'd use the serial port to drive this; or have + // a voting protocol where one side is elected the controller and + // guides the responder through all the channels, powers, ramps + // the transmit power from min to max, and measures the RSSI and SNR. + // Even more amazing would be a scheme where the controller could + // handle multiple nodes; in that case we'd have a way to do + // production test and qualification. However, using an RWC5020A + // is a much better use of development time. + + // set fDownlink true to use a downlink channel; false + // to use an uplink channel. Generally speaking, uplink + // is more interesting, because you can prove that gateways + // *should* be able to hear you. + const static bool fDownlink = false; + + // the downlink channel to be used. + const static uint8_t kDownlinkChannel = 3; + + // the uplink channel to be used. + const static uint8_t kUplinkChannel = 8 + 3; + + // this is automatically set to the proper bandwidth in kHz, + // based on the selected channel. + uint32_t uBandwidth; + + if (! fDownlink) + { + if (kUplinkChannel < 64) + { + LMIC.freq = US915_125kHz_UPFBASE + + kUplinkChannel * US915_125kHz_UPFSTEP; + uBandwidth = 125; + } + else + { + LMIC.freq = US915_500kHz_UPFBASE + + (kUplinkChannel - 64) * US915_500kHz_UPFSTEP; + uBandwidth = 500; + } + } + else + { + // downlink channel + LMIC.freq = US915_500kHz_DNFBASE + + kDownlinkChannel * US915_500kHz_DNFSTEP; + uBandwidth = 500; + } + + // Use a suitable spreading factor + if (uBandwidth < 500) + LMIC.datarate = US915_DR_SF7; // DR4 + else + LMIC.datarate = US915_DR_SF12CR; // DR8 + + // default tx power for US: 21 dBm + LMIC.txpow = 21; +#elif defined(CFG_au915) + // make it easier for test, by pull the parameters up to the top of the + // block. Ideally, we'd use the serial port to drive this; or have + // a voting protocol where one side is elected the controller and + // guides the responder through all the channels, powers, ramps + // the transmit power from min to max, and measures the RSSI and SNR. + // Even more amazing would be a scheme where the controller could + // handle multiple nodes; in that case we'd have a way to do + // production test and qualification. However, using an RWC5020A + // is a much better use of development time. + + // set fDownlink true to use a downlink channel; false + // to use an uplink channel. Generally speaking, uplink + // is more interesting, because you can prove that gateways + // *should* be able to hear you. + const static bool fDownlink = false; + + // the downlink channel to be used. + const static uint8_t kDownlinkChannel = 3; + + // the uplink channel to be used. + const static uint8_t kUplinkChannel = 8 + 3; + + // this is automatically set to the proper bandwidth in kHz, + // based on the selected channel. + uint32_t uBandwidth; + + if (! fDownlink) + { + if (kUplinkChannel < 64) + { + LMIC.freq = AU915_125kHz_UPFBASE + + kUplinkChannel * AU915_125kHz_UPFSTEP; + uBandwidth = 125; + } + else + { + LMIC.freq = AU915_500kHz_UPFBASE + + (kUplinkChannel - 64) * AU915_500kHz_UPFSTEP; + uBandwidth = 500; + } + } + else + { + // downlink channel + LMIC.freq = AU915_500kHz_DNFBASE + + kDownlinkChannel * AU915_500kHz_DNFSTEP; + uBandwidth = 500; + } + + // Use a suitable spreading factor + if (uBandwidth < 500) + LMIC.datarate = AU915_DR_SF7; // DR4 + else + LMIC.datarate = AU915_DR_SF12CR; // DR8 + + // default tx power for AU: 30 dBm + LMIC.txpow = 30; +#elif defined(CFG_as923) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = AS923_F1 + kChannel * 200000; + uBandwidth = 125; + + // Use a suitable spreading factor + if (uBandwidth == 125) + LMIC.datarate = AS923_DR_SF7; // DR7 + else + LMIC.datarate = AS923_DR_SF7B; // DR8 + + // default tx power for AS: 21 dBm + LMIC.txpow = 16; + + if (LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP) + { + LMIC.lbt_ticks = us2osticks(AS923JP_LBT_US); + LMIC.lbt_dbmax = AS923JP_LBT_DB_MAX; + } +#elif defined(CFG_kr920) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = KR920_F1 + kChannel * 200000; + uBandwidth = 125; + + LMIC.datarate = KR920_DR_SF7; // DR7 + // default tx power for KR: 14 dBm + LMIC.txpow = KR920_TX_EIRP_MAX_DBM; + if (LMIC.freq < KR920_F14DBM) + LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW; + + LMIC.lbt_ticks = us2osticks(KR920_LBT_US); + LMIC.lbt_dbmax = KR920_LBT_DB_MAX; +#elif defined(CFG_in866) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = IN866_F1 + kChannel * 200000; + uBandwidth = 125; + + LMIC.datarate = IN866_DR_SF7; // DR7 + // default tx power for IN: 30 dBm + LMIC.txpow = IN866_TX_EIRP_MAX_DBM; +#else +# error Unsupported LMIC regional configuration. +#endif + + + // disable RX IQ inversion + LMIC.noRXIQinversion = true; + + // This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250) + LMIC.rps = updr2rps(LMIC.datarate); + + Serial.print("Frequency: "); Serial.print(LMIC.freq / 1000000); + Serial.print("."); Serial.print((LMIC.freq / 100000) % 10); + Serial.print("MHz"); + Serial.print(" LMIC.datarate: "); Serial.print(LMIC.datarate); + Serial.print(" LMIC.txpow: "); Serial.println(LMIC.txpow); + Serial.println("Started"); + Serial.flush(); + + // setup initial job + os_setCallback(&txjob, tx_func); +} + +void loop() { + // execute scheduled jobs and events + os_runloop_once(); +} diff --git a/libraries/arduino-lmic-master/examples/raw-halconfig/raw-halconfig.ino b/libraries/arduino-lmic-master/examples/raw-halconfig/raw-halconfig.ino new file mode 100644 index 0000000..009cfe2 --- /dev/null +++ b/libraries/arduino-lmic-master/examples/raw-halconfig/raw-halconfig.ino @@ -0,0 +1,435 @@ +/* + +Module: raw-halconfig.ino + +Function: + Auto-configured raw test example, for Adafruit Feather M0 LoRa + +Copyright notice and License: + See LICENSE file accompanying this project. + +Author: + Matthijs Kooijman 2015 + Terry Moore, MCCI Corporation 2018 + +*/ + +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example transmits data on hardcoded channel and receives data + * when not transmitting. Running this sketch on two nodes should allow + * them to communicate. + *******************************************************************************/ + +#include +#include +#include + +#include + +#include +#include + +// we formerly would check this configuration; but now there is a flag, +// in the LMIC, LMIC.noRXIQinversion; +// if we set that during init, we get the same effect. If +// DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is +// treated as always set. +// +// #if !defined(DISABLE_INVERT_IQ_ON_RX) +// #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ +// lmic_project_config.h in arduino-lmic/project_config to set it. +// #endif + +// How often to send a packet. Note that this sketch bypasses the normal +// LMIC duty cycle limiting, so when you change anything in this sketch +// (payload length, frequency, spreading factor), be sure to check if +// this interval should not also be increased. +// See this spreadsheet for an easy airtime and duty cycle calculator: +// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc + +#define TX_INTERVAL 2000 // milliseconds +#define RX_RSSI_INTERVAL 100 // milliseconds + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h, +// otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +// this gets callled by the library but we choose not to display any info; +// and no action is required. +void onEvent (ev_t ev) { +} + +extern "C" { +void lmic_printf(const char *fmt, ...); +}; + +void lmic_printf(const char *fmt, ...) { + if (! Serial.dtr()) + return; + + char buf[256]; + va_list ap; + + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + // in case we overflowed: + buf[sizeof(buf) - 1] = '\0'; + if (Serial.dtr()) Serial.print(buf); +} + +osjob_t txjob; +osjob_t timeoutjob; +static void tx_func (osjob_t* job); + +// Transmit the given string and call the given function afterwards +void tx(const char *str, osjobcb_t func) { + // the radio is probably in RX mode; stop it. + os_radio(RADIO_RST); + // wait a bit so the radio can come out of RX mode + delay(1); + + // prepare data + LMIC.dataLen = 0; + while (*str) + LMIC.frame[LMIC.dataLen++] = *str++; + + // set completion function. + LMIC.osjob.func = func; + + // start the transmission + os_radio(RADIO_TX); + Serial.println("TX"); +} + +// Enable rx mode and call func when a packet is received +void rx(osjobcb_t func) { + LMIC.osjob.func = func; + LMIC.rxtime = os_getTime(); // RX _now_ + // Enable "continuous" RX (e.g. without a timeout, still stops after + // receiving a packet) + os_radio(RADIO_RXON); + Serial.println("RX"); +} + +static void rxtimeout_func(osjob_t *job) { + digitalWrite(LED_BUILTIN, LOW); // off +} + +static void rx_func (osjob_t* job) { + // Blink once to confirm reception and then keep the led on + digitalWrite(LED_BUILTIN, LOW); // off + delay(10); + digitalWrite(LED_BUILTIN, HIGH); // on + + // Timeout RX (i.e. update led status) after 3 periods without RX + os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); + + // Reschedule TX so that it should not collide with the other side's + // next TX + os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); + + Serial.print("Got "); + Serial.print(LMIC.dataLen); + Serial.println(" bytes"); + Serial.write(LMIC.frame, LMIC.dataLen); + Serial.println(); + + // Restart RX + rx(rx_func); +} + +static void txdone_func (osjob_t* job) { + rx(rx_func); +} + +// log text to USART and toggle LED +static void tx_func (osjob_t* job) { + // say hello + tx("Hello, world!", txdone_func); + // reschedule job every TX_INTERVAL (plus a bit of random to prevent + // systematic collisions), unless packets are received, then rx_func + // will reschedule at half this time. + os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); +} + +// application entry point +void setup() { + // delay(3000) makes recovery from botched images much easier, as it + // gives the host time to break in to start a download. Without it, + // you get to the crash before the host can break in. + delay(3000); + + // even after the delay, we wait for the host to open the port. operator + // bool(Serial) just checks dtr(), and it tosses in a 10ms delay. + while(! Serial.dtr()) + /* wait for the PC */; + + Serial.begin(115200); + Serial.println("Starting"); + + pinMode(LED_BUILTIN, OUTPUT); + + // initialize runtime env + // don't die mysteriously; die noisily. + const lmic_pinmap *pPinMap = Arduino_LMIC::GetPinmap_ThisBoard(); + + if (pPinMap == nullptr) { + pinMode(LED_BUILTIN, OUTPUT); + for (;;) { + // flash lights, sleep. + for (int i = 0; i < 5; ++i) { + digitalWrite(LED_BUILTIN, 1); + delay(100); + digitalWrite(LED_BUILTIN, 0); + delay(900); + } + Serial.println(F("board not known to library; add pinmap or update getconfig_thisboard.cpp")); + } + } + + os_init_ex(pPinMap); + + // Set up these settings once, and use them for both TX and RX +#ifdef ARDUINO_ARCH_STM32 + LMIC_setClockError(10*65536/100); +#endif + +#if defined(CFG_eu868) + // Use a frequency in the g3 which allows 10% duty cycling. + LMIC.freq = 869525000; + // Use a medium spread factor. This can be increased up to SF12 for + // better range, but then, the interval should be (significantly) + // raised to comply with duty cycle limits as well. + LMIC.datarate = DR_SF9; + // Maximum TX power + LMIC.txpow = 27; +#elif defined(CFG_us915) + // make it easier for test, by pull the parameters up to the top of the + // block. Ideally, we'd use the serial port to drive this; or have + // a voting protocol where one side is elected the controller and + // guides the responder through all the channels, powers, ramps + // the transmit power from min to max, and measures the RSSI and SNR. + // Even more amazing would be a scheme where the controller could + // handle multiple nodes; in that case we'd have a way to do + // production test and qualification. However, using an RWC5020A + // is a much better use of development time. + + // set fDownlink true to use a downlink channel; false + // to use an uplink channel. Generally speaking, uplink + // is more interesting, because you can prove that gateways + // *should* be able to hear you. + const static bool fDownlink = false; + + // the downlink channel to be used. + const static uint8_t kDownlinkChannel = 3; + + // the uplink channel to be used. + const static uint8_t kUplinkChannel = 8 + 3; + + // this is automatically set to the proper bandwidth in kHz, + // based on the selected channel. + uint32_t uBandwidth; + + if (! fDownlink) + { + if (kUplinkChannel < 64) + { + LMIC.freq = US915_125kHz_UPFBASE + + kUplinkChannel * US915_125kHz_UPFSTEP; + uBandwidth = 125; + } + else + { + LMIC.freq = US915_500kHz_UPFBASE + + (kUplinkChannel - 64) * US915_500kHz_UPFSTEP; + uBandwidth = 500; + } + } + else + { + // downlink channel + LMIC.freq = US915_500kHz_DNFBASE + + kDownlinkChannel * US915_500kHz_DNFSTEP; + uBandwidth = 500; + } + + // Use a suitable spreading factor + if (uBandwidth < 500) + LMIC.datarate = US915_DR_SF7; // DR4 + else + LMIC.datarate = US915_DR_SF12CR; // DR8 + + // default tx power for US: 21 dBm + LMIC.txpow = 21; +#elif defined(CFG_au915) + // make it easier for test, by pull the parameters up to the top of the + // block. Ideally, we'd use the serial port to drive this; or have + // a voting protocol where one side is elected the controller and + // guides the responder through all the channels, powers, ramps + // the transmit power from min to max, and measures the RSSI and SNR. + // Even more amazing would be a scheme where the controller could + // handle multiple nodes; in that case we'd have a way to do + // production test and qualification. However, using an RWC5020A + // is a much better use of development time. + + // set fDownlink true to use a downlink channel; false + // to use an uplink channel. Generally speaking, uplink + // is more interesting, because you can prove that gateways + // *should* be able to hear you. + const static bool fDownlink = false; + + // the downlink channel to be used. + const static uint8_t kDownlinkChannel = 3; + + // the uplink channel to be used. + const static uint8_t kUplinkChannel = 8 + 3; + + // this is automatically set to the proper bandwidth in kHz, + // based on the selected channel. + uint32_t uBandwidth; + + if (! fDownlink) + { + if (kUplinkChannel < 64) + { + LMIC.freq = AU915_125kHz_UPFBASE + + kUplinkChannel * AU915_125kHz_UPFSTEP; + uBandwidth = 125; + } + else + { + LMIC.freq = AU915_500kHz_UPFBASE + + (kUplinkChannel - 64) * AU915_500kHz_UPFSTEP; + uBandwidth = 500; + } + } + else + { + // downlink channel + LMIC.freq = AU915_500kHz_DNFBASE + + kDownlinkChannel * AU915_500kHz_DNFSTEP; + uBandwidth = 500; + } + + // Use a suitable spreading factor + if (uBandwidth < 500) + LMIC.datarate = AU915_DR_SF7; // DR4 + else + LMIC.datarate = AU915_DR_SF12CR; // DR8 + + // default tx power for AU: 30 dBm + LMIC.txpow = 30; +#elif defined(CFG_as923) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = AS923_F1 + kChannel * 200000; + uBandwidth = 125; + + // Use a suitable spreading factor + if (uBandwidth == 125) + LMIC.datarate = AS923_DR_SF7; // DR7 + else + LMIC.datarate = AS923_DR_SF7B; // DR8 + + // default tx power for AS: 21 dBm + LMIC.txpow = 16; + + if (LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP) + { + LMIC.lbt_ticks = us2osticks(AS923JP_LBT_US); + LMIC.lbt_dbmax = AS923JP_LBT_DB_MAX; + } +#elif defined(CFG_kr920) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = KR920_F1 + kChannel * 200000; + uBandwidth = 125; + + LMIC.datarate = KR920_DR_SF7; // DR7 + // default tx power for KR: 14 dBm + LMIC.txpow = KR920_TX_EIRP_MAX_DBM; + if (LMIC.freq < KR920_F14DBM) + LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW; + + LMIC.lbt_ticks = us2osticks(KR920_LBT_US); + LMIC.lbt_dbmax = KR920_LBT_DB_MAX; +#elif defined(CFG_in866) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = IN866_F1 + kChannel * 200000; + uBandwidth = 125; + + LMIC.datarate = IN866_DR_SF7; // DR7 + // default tx power for IN: 30 dBm + LMIC.txpow = IN866_TX_EIRP_MAX_DBM; +#else +# error Unsupported LMIC regional configuration. +#endif + + + // disable RX IQ inversion + LMIC.noRXIQinversion = true; + + // This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250) + LMIC.rps = updr2rps(LMIC.datarate); + + Serial.print("Frequency: "); Serial.print(LMIC.freq / 1000000); + Serial.print("."); Serial.print((LMIC.freq / 100000) % 10); + Serial.print("MHz"); + Serial.print(" LMIC.datarate: "); Serial.print(LMIC.datarate); + Serial.print(" LMIC.txpow: "); Serial.println(LMIC.txpow); + Serial.println("Started"); + Serial.flush(); + + // setup initial job + os_setCallback(&txjob, tx_func); +} + +void loop() { + // execute scheduled jobs and events + os_runloop_once(); +} diff --git a/libraries/arduino-lmic-master/examples/raw/raw.ino b/libraries/arduino-lmic-master/examples/raw/raw.ino new file mode 100644 index 0000000..6fe8189 --- /dev/null +++ b/libraries/arduino-lmic-master/examples/raw/raw.ino @@ -0,0 +1,373 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI Corporation + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example transmits data on hardcoded channel and receives data + * when not transmitting. Running this sketch on two nodes should allow + * them to communicate. + *******************************************************************************/ + +#include +#include +#include + +// we formerly would check this configuration; but now there is a flag, +// in the LMIC, LMIC.noRXIQinversion; +// if we set that during init, we get the same effect. If +// DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is +// treated as always set. +// +// #if !defined(DISABLE_INVERT_IQ_ON_RX) +// #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ +// lmic_project_config.h in arduino-lmic/project_config to set it. +// #endif + +// How often to send a packet. Note that this sketch bypasses the normal +// LMIC duty cycle limiting, so when you change anything in this sketch +// (payload length, frequency, spreading factor), be sure to check if +// this interval should not also be increased. +// See this spreadsheet for an easy airtime and duty cycle calculator: +// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc +#define TX_INTERVAL 2000 + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, +}; + + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h, +// otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +void onEvent (ev_t ev) { +} + +osjob_t txjob; +osjob_t timeoutjob; +static void tx_func (osjob_t* job); + +// Transmit the given string and call the given function afterwards +void tx(const char *str, osjobcb_t func) { + os_radio(RADIO_RST); // Stop RX first + delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet + LMIC.dataLen = 0; + while (*str) + LMIC.frame[LMIC.dataLen++] = *str++; + LMIC.osjob.func = func; + os_radio(RADIO_TX); + Serial.println("TX"); +} + +// Enable rx mode and call func when a packet is received +void rx(osjobcb_t func) { + LMIC.osjob.func = func; + LMIC.rxtime = os_getTime(); // RX _now_ + // Enable "continuous" RX (e.g. without a timeout, still stops after + // receiving a packet) + os_radio(RADIO_RXON); + Serial.println("RX"); +} + +static void rxtimeout_func(osjob_t *job) { + digitalWrite(LED_BUILTIN, LOW); // off +} + +static void rx_func (osjob_t* job) { + // Blink once to confirm reception and then keep the led on + digitalWrite(LED_BUILTIN, LOW); // off + delay(10); + digitalWrite(LED_BUILTIN, HIGH); // on + + // Timeout RX (i.e. update led status) after 3 periods without RX + os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); + + // Reschedule TX so that it should not collide with the other side's + // next TX + os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); + + Serial.print("Got "); + Serial.print(LMIC.dataLen); + Serial.println(" bytes"); + Serial.write(LMIC.frame, LMIC.dataLen); + Serial.println(); + + // Restart RX + rx(rx_func); +} + +static void txdone_func (osjob_t* job) { + rx(rx_func); +} + +// log text to USART and toggle LED +static void tx_func (osjob_t* job) { + // say hello + tx("Hello, world!", txdone_func); + // reschedule job every TX_INTERVAL (plus a bit of random to prevent + // systematic collisions), unless packets are received, then rx_func + // will reschedule at half this time. + os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); +} + +// application entry point +void setup() { + Serial.begin(115200); + Serial.println("Starting"); + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + pinMode(LED_BUILTIN, OUTPUT); + + // initialize runtime env + os_init(); + +#if defined(CFG_eu868) + // Use a frequency in the g3 which allows 10% duty cycling. + LMIC.freq = 869525000; + // Use a medium spread factor. This can be increased up to SF12 for + // better range, but then, the interval should be (significantly) + // raised to comply with duty cycle limits as well. + LMIC.datarate = DR_SF9; + // Maximum TX power + LMIC.txpow = 27; +#elif defined(CFG_us915) + // make it easier for test, by pull the parameters up to the top of the + // block. Ideally, we'd use the serial port to drive this; or have + // a voting protocol where one side is elected the controller and + // guides the responder through all the channels, powers, ramps + // the transmit power from min to max, and measures the RSSI and SNR. + // Even more amazing would be a scheme where the controller could + // handle multiple nodes; in that case we'd have a way to do + // production test and qualification. However, using an RWC5020A + // is a much better use of development time. + + // set fDownlink true to use a downlink channel; false + // to use an uplink channel. Generally speaking, uplink + // is more interesting, because you can prove that gateways + // *should* be able to hear you. + const static bool fDownlink = false; + + // the downlink channel to be used. + const static uint8_t kDownlinkChannel = 3; + + // the uplink channel to be used. + const static uint8_t kUplinkChannel = 8 + 3; + + // this is automatically set to the proper bandwidth in kHz, + // based on the selected channel. + uint32_t uBandwidth; + + if (! fDownlink) + { + if (kUplinkChannel < 64) + { + LMIC.freq = US915_125kHz_UPFBASE + + kUplinkChannel * US915_125kHz_UPFSTEP; + uBandwidth = 125; + } + else + { + LMIC.freq = US915_500kHz_UPFBASE + + (kUplinkChannel - 64) * US915_500kHz_UPFSTEP; + uBandwidth = 500; + } + } + else + { + // downlink channel + LMIC.freq = US915_500kHz_DNFBASE + + kDownlinkChannel * US915_500kHz_DNFSTEP; + uBandwidth = 500; + } + + // Use a suitable spreading factor + if (uBandwidth < 500) + LMIC.datarate = US915_DR_SF7; // DR4 + else + LMIC.datarate = US915_DR_SF12CR; // DR8 + + // default tx power for US: 21 dBm + LMIC.txpow = 21; +#elif defined(CFG_au915) + // make it easier for test, by pull the parameters up to the top of the + // block. Ideally, we'd use the serial port to drive this; or have + // a voting protocol where one side is elected the controller and + // guides the responder through all the channels, powers, ramps + // the transmit power from min to max, and measures the RSSI and SNR. + // Even more amazing would be a scheme where the controller could + // handle multiple nodes; in that case we'd have a way to do + // production test and qualification. However, using an RWC5020A + // is a much better use of development time. + + // set fDownlink true to use a downlink channel; false + // to use an uplink channel. Generally speaking, uplink + // is more interesting, because you can prove that gateways + // *should* be able to hear you. + const static bool fDownlink = false; + + // the downlink channel to be used. + const static uint8_t kDownlinkChannel = 3; + + // the uplink channel to be used. + const static uint8_t kUplinkChannel = 8 + 3; + + // this is automatically set to the proper bandwidth in kHz, + // based on the selected channel. + uint32_t uBandwidth; + + if (! fDownlink) + { + if (kUplinkChannel < 64) + { + LMIC.freq = AU915_125kHz_UPFBASE + + kUplinkChannel * AU915_125kHz_UPFSTEP; + uBandwidth = 125; + } + else + { + LMIC.freq = AU915_500kHz_UPFBASE + + (kUplinkChannel - 64) * AU915_500kHz_UPFSTEP; + uBandwidth = 500; + } + } + else + { + // downlink channel + LMIC.freq = AU915_500kHz_DNFBASE + + kDownlinkChannel * AU915_500kHz_DNFSTEP; + uBandwidth = 500; + } + + // Use a suitable spreading factor + if (uBandwidth < 500) + LMIC.datarate = AU915_DR_SF7; // DR4 + else + LMIC.datarate = AU915_DR_SF12CR; // DR8 + + // default tx power for AU: 30 dBm + LMIC.txpow = 30; +#elif defined(CFG_as923) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = AS923_F1 + kChannel * 200000; + uBandwidth = 125; + + // Use a suitable spreading factor + if (uBandwidth == 125) + LMIC.datarate = AS923_DR_SF7; // DR7 + else + LMIC.datarate = AS923_DR_SF7B; // DR8 + + // default tx power for AS: 21 dBm + LMIC.txpow = 16; + + if (LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP) + { + LMIC.lbt_ticks = us2osticks(AS923JP_LBT_US); + LMIC.lbt_dbmax = AS923JP_LBT_DB_MAX; + } +#elif defined(CFG_kr920) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = KR920_F1 + kChannel * 200000; + uBandwidth = 125; + + LMIC.datarate = KR920_DR_SF7; // DR7 + // default tx power for KR: 14 dBm + LMIC.txpow = KR920_TX_EIRP_MAX_DBM; + if (LMIC.freq < KR920_F14DBM) + LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW; + + LMIC.lbt_ticks = us2osticks(KR920_LBT_US); + LMIC.lbt_dbmax = KR920_LBT_DB_MAX; +#elif defined(CFG_in866) +// make it easier for test, by pull the parameters up to the top of the +// block. Ideally, we'd use the serial port to drive this; or have +// a voting protocol where one side is elected the controller and +// guides the responder through all the channels, powers, ramps +// the transmit power from min to max, and measures the RSSI and SNR. +// Even more amazing would be a scheme where the controller could +// handle multiple nodes; in that case we'd have a way to do +// production test and qualification. However, using an RWC5020A +// is a much better use of development time. + const static uint8_t kChannel = 0; + uint32_t uBandwidth; + + LMIC.freq = IN866_F1 + kChannel * 200000; + uBandwidth = 125; + + LMIC.datarate = IN866_DR_SF7; // DR7 + // default tx power for IN: 30 dBm + LMIC.txpow = IN866_TX_EIRP_MAX_DBM; +#else +# error Unsupported LMIC regional configuration. +#endif + + + // disable RX IQ inversion + LMIC.noRXIQinversion = true; + + // This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250) + LMIC.rps = updr2rps(LMIC.datarate); + + Serial.print("Frequency: "); Serial.print(LMIC.freq / 1000000); + Serial.print("."); Serial.print((LMIC.freq / 100000) % 10); + Serial.print("MHz"); + Serial.print(" LMIC.datarate: "); Serial.print(LMIC.datarate); + Serial.print(" LMIC.txpow: "); Serial.println(LMIC.txpow); + + // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) + LMIC.rps = updr2rps(LMIC.datarate); + + // disable RX IQ inversion + LMIC.noRXIQinversion = true; + + Serial.println("Started"); + Serial.flush(); + + // setup initial job + os_setCallback(&txjob, tx_func); +} + +void loop() { + // execute scheduled jobs and events + os_runloop_once(); +} diff --git a/libraries/arduino-lmic-master/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino b/libraries/arduino-lmic-master/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino new file mode 100644 index 0000000..f70ac6c --- /dev/null +++ b/libraries/arduino-lmic-master/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino @@ -0,0 +1,275 @@ +/******************************************************************************* + * The Things Network - ABP Feather + * + * Example of using an Adafruit Feather M0 and DHT22 with a + * single-channel TheThingsNetwork gateway. + * + * This uses ABP (Activation by Personalization), where session keys for + * communication would be assigned/generated by TTN and hard-coded on the device. + * + * Learn Guide: https://learn.adafruit.com/lora-pi + * + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI + * Copyright (c) 2018 Brent Rubell, Adafruit Industries + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + *******************************************************************************/ +#include +#include +#include + +// include the DHT22 Sensor Library +#include "DHT.h" + +// DHT digital pin and sensor type +#define DHTPIN 10 +#define DHTTYPE DHT22 + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// LoRaWAN NwkSKey, network session key +static const PROGMEM u1_t NWKSKEY[16] = { FILLMEIN }; + +// LoRaWAN AppSKey, application session key +static const u1_t PROGMEM APPSKEY[16] = { FILLMEIN }; + +// LoRaWAN end-device address (DevAddr) +// See http://thethingsnetwork.org/wiki/AddressSpace +// The library converts the address to network byte order as needed. +#ifndef COMPILE_REGRESSION_TEST +static const u4_t DEVADDR = 0xFILLMEIN; +#else +static const u4_t DEVADDR = 0; +#endif + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_config.h, +// otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +// payload to send to TTN gateway +static uint8_t payload[5]; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 30; + +// Pin mapping for Adafruit Feather M0 LoRa +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB + .spi_freq = 8000000, +}; + +// init. DHT +DHT dht(DHTPIN, DHTTYPE); + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + break; + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_RFU1: + || Serial.println(F("EV_RFU1")); + || break; + */ + case EV_JOIN_FAILED: + Serial.println(F("EV_JOIN_FAILED")); + break; + case EV_REJOIN_FAILED: + Serial.println(F("EV_REJOIN_FAILED")); + break; + case EV_TXCOMPLETE: + Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + if (LMIC.txrxFlags & TXRX_ACK) + Serial.println(F("Received ack")); + if (LMIC.dataLen) { + Serial.println(F("Received ")); + Serial.println(LMIC.dataLen); + Serial.println(F(" bytes of payload")); + } + // Schedule next transmission + os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); + break; + case EV_LOST_TSYNC: + Serial.println(F("EV_LOST_TSYNC")); + break; + case EV_RESET: + Serial.println(F("EV_RESET")); + break; + case EV_RXCOMPLETE: + // data received in ping slot + Serial.println(F("EV_RXCOMPLETE")); + break; + case EV_LINK_DEAD: + Serial.println(F("EV_LINK_DEAD")); + break; + case EV_LINK_ALIVE: + Serial.println(F("EV_LINK_ALIVE")); + break; + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_SCAN_FOUND: + || Serial.println(F("EV_SCAN_FOUND")); + || break; + */ + case EV_TXSTART: + Serial.println(F("EV_TXSTART")); + break; + case EV_TXCANCELED: + Serial.println(F("EV_TXCANCELED")); + break; + case EV_RXSTART: + /* do not print anything -- it wrecks timing */ + break; + case EV_JOIN_TXCOMPLETE: + Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept")); + break; + default: + Serial.print(F("Unknown event: ")); + Serial.println((unsigned) ev); + break; + } +} + +void do_send(osjob_t* j){ + // Check if there is not a current TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND, not sending")); + } else { + // read the temperature from the DHT22 + float temperature = dht.readTemperature(); + Serial.print("Temperature: "); Serial.print(temperature); + Serial.println(" *C"); + // adjust for the f2sflt16 range (-1 to 1) + temperature = temperature / 100; + + // read the humidity from the DHT22 + float rHumidity = dht.readHumidity(); + Serial.print("%RH "); + Serial.println(rHumidity); + // adjust for the f2sflt16 range (-1 to 1) + rHumidity = rHumidity / 100; + + // float -> int + // note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16) + uint16_t payloadTemp = LMIC_f2sflt16(temperature); + // int -> bytes + byte tempLow = lowByte(payloadTemp); + byte tempHigh = highByte(payloadTemp); + // place the bytes into the payload + payload[0] = tempLow; + payload[1] = tempHigh; + + // float -> int + uint16_t payloadHumid = LMIC_f2sflt16(rHumidity); + // int -> bytes + byte humidLow = lowByte(payloadHumid); + byte humidHigh = highByte(payloadHumid); + payload[2] = humidLow; + payload[3] = humidHigh; + + // prepare upstream data transmission at the next possible time. + // transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved). + // don't request an ack (the last parameter, if not zero, requests an ack from the network). + // Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it. + LMIC_setTxData2(1, payload, sizeof(payload)-1, 0); + } + // Next TX is scheduled after TX_COMPLETE event. +} + +void setup() { + delay(5000); + while (!Serial); + Serial.begin(115200); + delay(100); + Serial.println(F("Starting")); + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // Set static session parameters. Instead of dynamically establishing a session + // by joining the network, precomputed session parameters are be provided. + // On AVR, these values are stored in flash and only copied to RAM + // once. Copy them to a temporary buffer here, LMIC_setSession will + // copy them into a buffer of its own again. + uint8_t appskey[sizeof(APPSKEY)]; + uint8_t nwkskey[sizeof(NWKSKEY)]; + memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); + + // We'll disable all 72 channels used by TTN + for (int c = 0; c < 72; c++){ + LMIC_disableChannel(c); + } + + // We'll only enable Channel 16 (905.5Mhz) since we're transmitting on a single-channel + LMIC_enableChannel(16); + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // TTN uses SF9 for its RX2 window. + LMIC.dn2Dr = DR_SF9; + + // Set data rate and transmit power for uplink + LMIC_setDrTxpow(DR_SF7,14); + + // Start job + do_send(&sendjob); +} + +void loop() { + os_runloop_once(); +} diff --git a/libraries/arduino-lmic-master/examples/ttn-abp/ttn-abp.ino b/libraries/arduino-lmic-master/examples/ttn-abp/ttn-abp.ino new file mode 100644 index 0000000..2f2ee66 --- /dev/null +++ b/libraries/arduino-lmic-master/examples/ttn-abp/ttn-abp.ino @@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example sends a valid LoRaWAN packet with payload "Hello, + * world!", using frequency and encryption settings matching those of + * the The Things Network. + * + * This uses ABP (Activation-by-personalisation), where a DevAddr and + * Session keys are preconfigured (unlike OTAA, where a DevEUI and + * application key is configured, while the DevAddr and session keys are + * assigned/generated in the over-the-air-activation procedure). + * + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in + * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably + * violated by this sketch when left running for longer)! + * + * To use this sketch, first register your application and device with + * the things network, to set or generate a DevAddr, NwkSKey and + * AppSKey. Each device should have their own unique values for these + * fields. + * + * Do not forget to define the radio type correctly in + * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * + *******************************************************************************/ + + // References: + // [feather] adafruit-feather-m0-radio-with-lora-module.pdf + +#include +#include +#include + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// LoRaWAN NwkSKey, network session key +// This should be in big-endian (aka msb). +static const PROGMEM u1_t NWKSKEY[16] = { FILLMEIN }; + +// LoRaWAN AppSKey, application session key +// This should also be in big-endian (aka msb). +static const u1_t PROGMEM APPSKEY[16] = { FILLMEIN }; + +// LoRaWAN end-device address (DevAddr) +// See http://thethingsnetwork.org/wiki/AddressSpace +// The library converts the address to network byte order as needed, so this should be in big-endian (aka msb) too. +static const u4_t DEVADDR = FILLMEIN ; // <-- Change this address for every node! + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_config.h, +// otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +// Adapted for Feather M0 per p.10 of [feather] +const lmic_pinmap lmic_pins = { + .nss = 8, // chip select on feather (rf95module) CS + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, // reset pin + .dio = {6, 5, LMIC_UNUSED_PIN}, // assumes external jumpers [feather_lora_jumper] + // DIO1 is on JP1-1: is io1 - we connect to GPO6 + // DIO1 is on JP5-3: is D2 - we connect to GPO5 +}; + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + break; + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_RFU1: + || Serial.println(F("EV_RFU1")); + || break; + */ + case EV_JOIN_FAILED: + Serial.println(F("EV_JOIN_FAILED")); + break; + case EV_REJOIN_FAILED: + Serial.println(F("EV_REJOIN_FAILED")); + break; + case EV_TXCOMPLETE: + Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + if (LMIC.txrxFlags & TXRX_ACK) + Serial.println(F("Received ack")); + if (LMIC.dataLen) { + Serial.println(F("Received ")); + Serial.println(LMIC.dataLen); + Serial.println(F(" bytes of payload")); + } + // Schedule next transmission + os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); + break; + case EV_LOST_TSYNC: + Serial.println(F("EV_LOST_TSYNC")); + break; + case EV_RESET: + Serial.println(F("EV_RESET")); + break; + case EV_RXCOMPLETE: + // data received in ping slot + Serial.println(F("EV_RXCOMPLETE")); + break; + case EV_LINK_DEAD: + Serial.println(F("EV_LINK_DEAD")); + break; + case EV_LINK_ALIVE: + Serial.println(F("EV_LINK_ALIVE")); + break; + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_SCAN_FOUND: + || Serial.println(F("EV_SCAN_FOUND")); + || break; + */ + case EV_TXSTART: + Serial.println(F("EV_TXSTART")); + break; + case EV_TXCANCELED: + Serial.println(F("EV_TXCANCELED")); + break; + case EV_RXSTART: + /* do not print anything -- it wrecks timing */ + break; + case EV_JOIN_TXCOMPLETE: + Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept")); + break; + default: + Serial.print(F("Unknown event: ")); + Serial.println((unsigned) ev); + break; + } +} + +void do_send(osjob_t* j){ + // Check if there is not a current TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND, not sending")); + } else { + // Prepare upstream data transmission at the next possible time. + LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + Serial.println(F("Packet queued")); + } + // Next TX is scheduled after TX_COMPLETE event. +} + +void setup() { +// pinMode(13, OUTPUT); + while (!Serial); // wait for Serial to be initialized + Serial.begin(115200); + delay(100); // per sample code on RF_95 test + Serial.println(F("Starting")); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // Set static session parameters. Instead of dynamically establishing a session + // by joining the network, precomputed session parameters are be provided. + #ifdef PROGMEM + // On AVR, these values are stored in flash and only copied to RAM + // once. Copy them to a temporary buffer here, LMIC_setSession will + // copy them into a buffer of its own again. + uint8_t appskey[sizeof(APPSKEY)]; + uint8_t nwkskey[sizeof(NWKSKEY)]; + memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); + #else + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); + #endif + + #if defined(CFG_eu868) + // Set up the channels used by the Things Network, which corresponds + // to the defaults of most gateways. Without this, only three base + // channels from the LoRaWAN specification are used, which certainly + // works, so it is good for debugging, but can overload those + // frequencies, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. The LMIC doesn't let you change + // the three basic settings, but we show them here. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + #elif defined(CFG_us915) || defined(CFG_au915) + // NA-US and AU channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #elif defined(CFG_as923) + // Set up the channels used in your country. Only two are defined by default, + // and they cannot be changed. Use BAND_CENTI to indicate 1% duty cycle. + // LMIC_setupChannel(0, 923200000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); + // LMIC_setupChannel(1, 923400000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); + + // ... extra definitions for channels 2..n here + #elif defined(CFG_kr920) + // Set up the channels used in your country. Three are defined by default, + // and they cannot be changed. Duty cycle doesn't matter, but is conventionally + // BAND_MILLI. + // LMIC_setupChannel(0, 922100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); + // LMIC_setupChannel(1, 922300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); + // LMIC_setupChannel(2, 922500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); + + // ... extra definitions for channels 3..n here. + #elif defined(CFG_in866) + // Set up the channels used in your country. Three are defined by default, + // and they cannot be changed. Duty cycle doesn't matter, but is conventionally + // BAND_MILLI. + // LMIC_setupChannel(0, 865062500, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); + // LMIC_setupChannel(1, 865402500, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); + // LMIC_setupChannel(2, 865985000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); + + // ... extra definitions for channels 3..n here. + #else + # error Region not supported + #endif + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // TTN uses SF9 for its RX2 window. + LMIC.dn2Dr = DR_SF9; + + // Set data rate and transmit power for uplink + LMIC_setDrTxpow(DR_SF7,14); + + // Start job + do_send(&sendjob); +} + +void loop() { + unsigned long now; + now = millis(); + if ((now & 512) != 0) { + digitalWrite(13, HIGH); + } + else { + digitalWrite(13, LOW); + } + + os_runloop_once(); + +} diff --git a/libraries/arduino-lmic-master/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino b/libraries/arduino-lmic-master/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino new file mode 100644 index 0000000..1acfabc --- /dev/null +++ b/libraries/arduino-lmic-master/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino @@ -0,0 +1,291 @@ +/******************************************************************************* + * The Things Network - Sensor Data Example + * + * Example of sending a valid LoRaWAN packet with DHT22 temperature and + * humidity data to The Things Networ using a Feather M0 LoRa. + * + * Learn Guide: https://learn.adafruit.com/the-things-network-for-feather + * + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI + * Copyright (c) 2018 Brent Rubell, Adafruit Industries + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + *******************************************************************************/ +#include +#include +#include + +// include the DHT22 Sensor Library +#include "DHT.h" + +// DHT digital pin and sensor type +#define DHTPIN 10 +#define DHTTYPE DHT22 + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +#define FILLMEIN 0 +#else +#warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +#define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8] = { FILLMEIN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8] = { FILLMEIN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from the TTN console can be copied as-is. +static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +// payload to send to TTN gateway +static uint8_t payload[5]; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 30; + +// Pin mapping for Adafruit Feather M0 LoRa +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB + .spi_freq = 8000000, +}; + +// init. DHT +DHT dht(DHTPIN, DHTTYPE); + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("AppSKey: "); + for (size_t i=0; i int + // note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16) + uint16_t payloadTemp = LMIC_f2sflt16(temperature); + // int -> bytes + byte tempLow = lowByte(payloadTemp); + byte tempHigh = highByte(payloadTemp); + // place the bytes into the payload + payload[0] = tempLow; + payload[1] = tempHigh; + + // float -> int + uint16_t payloadHumid = LMIC_f2sflt16(rHumidity); + // int -> bytes + byte humidLow = lowByte(payloadHumid); + byte humidHigh = highByte(payloadHumid); + payload[2] = humidLow; + payload[3] = humidHigh; + + // prepare upstream data transmission at the next possible time. + // transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved). + // don't request an ack (the last parameter, if not zero, requests an ack from the network). + // Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it. + LMIC_setTxData2(1, payload, sizeof(payload)-1, 0); + } + // Next TX is scheduled after TX_COMPLETE event. +} + +void setup() { + delay(5000); + while (! Serial); + Serial.begin(9600); + Serial.println(F("Starting")); + + dht.begin(); + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + // Disable link-check mode and ADR, because ADR tends to complicate testing. + LMIC_setLinkCheckMode(0); + // Set the data rate to Spreading Factor 7. This is the fastest supported rate for 125 kHz channels, and it + // minimizes air time and battery power. Set the transmission power to 14 dBi (25 mW). + LMIC_setDrTxpow(DR_SF7,14); + // in the US, with TTN, it saves join time if we start on subband 1 (channels 8-15). This will + // get overridden after the join by parameters from the network. If working with other + // networks or in other regions, this will need to be changed. + LMIC_selectSubBand(1); + + // Start job (sending automatically starts OTAA too) + do_send(&sendjob); +} + +void loop() { + // we call the LMIC's runloop processor. This will cause things to happen based on events and time. One + // of the things that will happen is callbacks for transmission complete or received messages. We also + // use this loop to queue periodic data transmissions. You can put other things here in the `loop()` routine, + // but beware that LoRaWAN timing is pretty tight, so if you do more than a few milliseconds of work, you + // will want to call `os_runloop_once()` every so often, to keep the radio running. + os_runloop_once(); +} diff --git a/libraries/arduino-lmic-master/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino b/libraries/arduino-lmic-master/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino new file mode 100644 index 0000000..9690639 --- /dev/null +++ b/libraries/arduino-lmic-master/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino @@ -0,0 +1,295 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example sends a valid LoRaWAN packet with payload "Hello, + * world!", using frequency and encryption settings matching those of + * the The Things Network. It's pre-configured for the Adafruit + * Feather M0 LoRa. + * + * This uses OTAA (Over-the-air activation), where where a DevEUI and + * application key is configured, which are used in an over-the-air + * activation procedure where a DevAddr and session keys are + * assigned/generated for use with all further communication. + * + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in + * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably + * violated by this sketch when left running for longer)! + + * To use this sketch, first register your application and device with + * the things network, to set or generate an AppEUI, DevEUI and AppKey. + * Multiple devices can use the same AppEUI, but each device has its own + * DevEUI and AppKey. + * + * Do not forget to define the radio type correctly in + * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * + *******************************************************************************/ + +#include +#include +#include + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8]= { FILLMEIN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]= { FILLMEIN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from the TTN console can be copied as-is. +static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +// +// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +// m0 defs ADAFRUIT_FEATHER_M0 +// +#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) +// Pin mapping for Adafruit Feather M0 LoRa, etc. +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB + .spi_freq = 8000000, +}; +#elif defined(ARDUINO_AVR_FEATHER32U4) +// Pin mapping for Adafruit Feather 32u4 LoRa, etc. +// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only +// because MCCI doesn't have a test board; probably higher frequencies +// will work. +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {7, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather 32U4 LoRa, in dB + .spi_freq = 1000000, +}; +#elif defined(ARDUINO_CATENA_4551) +// Pin mapping for Murata module / Catena 4551 +const lmic_pinmap lmic_pins = { + .nss = 7, + .rxtx = 29, + .rst = 8, + .dio = { 25, // DIO0 (IRQ) is D25 + 26, // DIO1 is D26 + 27, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000 // 8MHz +}; +#else +# error "Unknown target" +#endif + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("AppSKey: "); + for (size_t i=0; i +#include +#include +#include + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8]= { FILLMEIN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]= { FILLMEIN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from the TTN console can be copied as-is. +static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("AppSKey: "); + for (size_t i=0; i +#include +#include +#include + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8]={ FILLMEIN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]={ FILLMEIN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from ttnctl can be copied as-is. +static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, +}; + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("AppSKey: "); + for (size_t i=0; i +#include +#include + +// +// For normal use, we require that you edit the sketch to replace FILLMEIN +// with values assigned by the TTN console. However, for regression tests, +// we want to be able to compile these scripts. The regression tests define +// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- +// working but innocuous value. +// +#ifdef COMPILE_REGRESSION_TEST +# define FILLMEIN 0 +#else +# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" +# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) +#endif + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8]={ FILLMEIN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]={ FILLMEIN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from ttnctl can be copied as-is. +static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, +}; + +void printHex2(unsigned v) { + v &= 0xff; + if (v < 16) + Serial.print('0'); + Serial.print(v, HEX); +} + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("AppSKey: "); + for (size_t i=0; i +sentence=Arduino port of the LMIC (LoraWAN-MAC-in-C) framework provided by IBM. +paragraph=Supports LoRaWAN 1.0.2/1.0.3 Class A devices implemented using the Semtech SX1272/SX1276 (including HopeRF RFM92/RFM95 and Murata modules). Support for EU868, US, AU, AS923, KR and IN regional plans. Untested support for Class B and FSK operation. Various enhancements and bug fixes from MCCI and The Things Network New York. Original IBM URL http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html. +category=Communication +url=https://github.com/mcci-catena/arduino-lmic +architectures=* diff --git a/libraries/arduino-lmic-master/project_config/lmic_project_config.h b/libraries/arduino-lmic-master/project_config/lmic_project_config.h new file mode 100644 index 0000000..3f86e17 --- /dev/null +++ b/libraries/arduino-lmic-master/project_config/lmic_project_config.h @@ -0,0 +1,10 @@ +// project-specific definitions +//#define CFG_eu868 1 +#define CFG_us915 1 +//#define CFG_au915 1 +//#define CFG_as923 1 +// #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP */ +//#define CFG_kr920 1 +//#define CFG_in866 1 +#define CFG_sx1276_radio 1 +//#define LMIC_USE_INTERRUPTS diff --git a/libraries/arduino-lmic-master/src/aes/ideetron/AES-128_V10.cpp b/libraries/arduino-lmic-master/src/aes/ideetron/AES-128_V10.cpp new file mode 100644 index 0000000..d52623d --- /dev/null +++ b/libraries/arduino-lmic-master/src/aes/ideetron/AES-128_V10.cpp @@ -0,0 +1,348 @@ +/****************************************************************************************** +#if defined(USE_IDEETRON_AES) +* Copyright 2015, 2016 Ideetron B.V. +* +* This program 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 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +******************************************************************************************/ +/****************************************************************************************** +* +* File: AES-128_V10.cpp +* Author: Gerben den Hartog +* Compagny: Ideetron B.V. +* Website: http://www.ideetron.nl/LoRa +* E-mail: info@ideetron.nl +******************************************************************************************/ +/**************************************************************************************** +* +* Created on: 20-10-2015 +* Supported Hardware: ID150119-02 Nexus board with RFM95 +* +* Firmware Version 1.0 +* First version +****************************************************************************************/ + +// This file was taken from +// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for +// use with LMIC. It was only cosmetically modified: +// - AES_Encrypt was renamed to lmic_aes_encrypt. +// - All other functions and variables were made static +// - Tabs were converted to 2 spaces +// - An #include and #if guard was added +// - S_Table is now stored in PROGMEM + +#include "../../lmic/oslmic.h" + +#if defined(USE_IDEETRON_AES) + +/* +******************************************************************************************** +* Global Variables +******************************************************************************************** +*/ + +static unsigned char State[4][4]; + +static CONST_TABLE(unsigned char, S_Table)[16][16] = { + {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, + {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, + {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, + {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, + {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, + {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, + {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, + {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, + {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, + {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, + {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, + {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, + {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, + {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, + {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, + {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} +}; + +#ifdef __cplusplus +extern "C" { +#endif + void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); +#ifdef __cplusplus +} +#endif + +static void AES_Add_Round_Key(unsigned char *Round_Key); +static unsigned char AES_Sub_Byte(unsigned char Byte); +static void AES_Shift_Rows(); +static void AES_Mix_Collums(); +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); + +/* +***************************************************************************************** +* Description : Function for encrypting data using AES-128 +* +* Arguments : *Data Data to encrypt is a 16 byte long arry +* *Key Key to encrypt data with is a 16 byte long arry +***************************************************************************************** +*/ +void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) +{ + unsigned char i; + unsigned char Row,Collum; + unsigned char Round = 0x00; + unsigned char Round_Key[16]; + + //Copy input to State arry + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = Data[Row + (4*Collum)]; + } + } + + //Copy key to round key + for(i = 0; i < 16; i++) + { + Round_Key[i] = Key[i]; + } + + //Add round key + AES_Add_Round_Key(Round_Key); + + //Preform 9 full rounds + for(Round = 1; Round < 10; Round++) + { + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Preform Row Shift + AES_Shift_Rows(); + + //Mix Collums + AES_Mix_Collums(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round key + AES_Add_Round_Key(Round_Key); + } + + //Last round whitout mix collums + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Shift rows + AES_Shift_Rows(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round Key + AES_Add_Round_Key(Round_Key); + + //Copy the State into the data array + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + Data[Row + (4*Collum)] = State[Row][Collum]; + } + } + +} + +/* +***************************************************************************************** +* Description : Function that add's the round key for the current round +* +* Arguments : *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Add_Round_Key(unsigned char *Round_Key) +{ + unsigned char Row,Collum; + + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; + } + } +} + +/* +***************************************************************************************** +* Description : Function that substitutes a byte with a byte from the S_Table +* +* Arguments : Byte The byte that will be substituted +* +* Return : The return is the found byte in the S_Table +***************************************************************************************** +*/ +static unsigned char AES_Sub_Byte(unsigned char Byte) +{ + unsigned char S_Row,S_Collum; + unsigned char S_Byte; + + //Split byte up in Row and Collum + S_Row = ((Byte >> 4) & 0x0F); + S_Collum = (Byte & 0x0F); + + //Find the correct byte in the S_Table + S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum); + + return S_Byte; +} + +/* +***************************************************************************************** +* Description : Function that preforms the shift row operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Shift_Rows() +{ + unsigned char Buffer; + + //Row 0 doesn't change + + //Shift Row 1 one left + //Store firt byte in buffer + Buffer = State[1][0]; + //Shift all bytes + State[1][0] = State[1][1]; + State[1][1] = State[1][2]; + State[1][2] = State[1][3]; + State[1][3] = Buffer; + + //Shift row 2 two left + Buffer = State[2][0]; + State[2][0] = State[2][2]; + State[2][2] = Buffer; + Buffer = State[2][1]; + State[2][1] = State[2][3]; + State[2][3] = Buffer; + + //Shift row 3 three left + Buffer = State[3][3]; + State[3][3] = State[3][2]; + State[3][2] = State[3][1]; + State[3][1] = State[3][0]; + State[3][0] = Buffer; +} + +/* +***************************************************************************************** +* Description : Function that preforms the Mix Collums operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Mix_Collums() +{ + unsigned char Row,Collum; + unsigned char a[4], b[4]; + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + a[Row] = State[Row][Collum]; + b[Row] = (State[Row][Collum] << 1); + + if((State[Row][Collum] & 0x80) == 0x80) + { + b[Row] = b[Row] ^ 0x1B; + } + } + State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; + State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; + State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; + State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; + } +} + +/* +***************************************************************************************** +* Description : Function that calculaties the round key for the current round +* +* Arguments : Round Number of current Round +* *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) +{ + unsigned char i,j; + unsigned char b; + unsigned char Temp[4]; + unsigned char Buffer; + unsigned char Rcon; + + //Calculate first Temp + //Copy laste byte from previous key + for(i = 0; i < 4; i++) + { + Temp[i] = Round_Key[i+12]; + } + + //Rotate Temp + Buffer = Temp[0]; + Temp[0] = Temp[1]; + Temp[1] = Temp[2]; + Temp[2] = Temp[3]; + Temp[3] = Buffer; + + //Substitute Temp + for(i = 0; i < 4; i++) + { + Temp[i] = AES_Sub_Byte(Temp[i]); + } + + //Calculate Rcon + Rcon = 0x01; + while(Round != 1) + { + b = Rcon & 0x80; + Rcon = Rcon << 1; + if(b == 0x80) + { + Rcon = Rcon ^ 0x1b; + } + Round--; + } + + //XOR Rcon + Temp[0] = Temp[0] ^ Rcon; + + //Calculate new key + for(i = 0; i < 4; i++) + { + for(j = 0; j < 4; j++) + { + Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; + Temp[j] = Round_Key[j + (4*i)]; + } + } +} + +#endif // defined(USE_IDEETRON_AES) diff --git a/libraries/arduino-lmic-master/src/aes/lmic.c b/libraries/arduino-lmic-master/src/aes/lmic.c new file mode 100644 index 0000000..2a5bca3 --- /dev/null +++ b/libraries/arduino-lmic-master/src/aes/lmic.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../lmic/oslmic.h" + +#if defined(USE_ORIGINAL_AES) + +#define AES_MICSUB 0x30 // internal use only + +static CONST_TABLE(u4_t, AES_RCON)[10] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 +}; + +static CONST_TABLE(u1_t, AES_S)[256] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +}; + +static CONST_TABLE(u4_t, AES_E1)[256] = { + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, + 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, + 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, + 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, + 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, + 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, + 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, + 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, + 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, + 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, + 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, + 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, + 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, + 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, + 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, + 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, + 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, + 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, + 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, + 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, + 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, + 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A, +}; + +static CONST_TABLE(u4_t, AES_E2)[256] = { + 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, + 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, + 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, + 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, + 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, + 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, + 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, + 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, + 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, + 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, + 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, + 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, + 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, + 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, + 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, + 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, + 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, + 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, + 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, + 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, + 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, + 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, + 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, + 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, + 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, + 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, + 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, + 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, + 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, + 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, + 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, + 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616, +}; + +static CONST_TABLE(u4_t, AES_E3)[256] = { + 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, + 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, + 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, + 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, + 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, + 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, + 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, + 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, + 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, + 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, + 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, + 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, + 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, + 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, + 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, + 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, + 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, + 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, + 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, + 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, + 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, + 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, + 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, + 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, + 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, + 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, + 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, + 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, + 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, + 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, + 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, + 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16, +}; + +static CONST_TABLE(u4_t, AES_E4)[256] = { + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C, +}; + +#define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3]) +#define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v) +#define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) ) + +#define u1(v) ((u1_t)(v)) + +#define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \ + r2 = ki[i+2]; \ + r3 = ki[i+3]; \ + r0 = ki[i] + +#define AES_expr4(r1,r2,r3,r0,i) r1 ^= TABLE_GET_U4(AES_E4, u1(i)); \ + r2 ^= TABLE_GET_U4(AES_E3, u1(i>>8)); \ + r3 ^= TABLE_GET_U4(AES_E2, u1(i>>16)); \ + r0 ^= TABLE_GET_U4(AES_E1, (i>>24)) + +#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, r0>>24 )<<24); \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r1>>16))<<16); \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r2>> 8))<< 8); \ + a ^= (u4_t)TABLE_GET_U1(AES_S, u1(r3) ) + +// global area for passing parameters (aux, key) and for storing round keys +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[11*16/sizeof(u4_t)]; + +// generate 1+10 roundkeys for encryption with 128-bit key +// read 128-bit key from AESKEY in MSBF, generate roundkey words in place +static void aesroundkeys () { + int i; + u4_t b; + + for( i=0; i<4; i++) { + AESKEY[i] = swapmsbf(AESKEY[i]); + } + + b = AESKEY[3]; + for( ; i<44; i++ ) { + if( i%4==0 ) { + // b = SubWord(RotWord(b)) xor Rcon[i/4] + b = ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 16)) << 24) ^ + ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 8)) << 16) ^ + ((u4_t)TABLE_GET_U1(AES_S, u1(b) ) << 8) ^ + ((u4_t)TABLE_GET_U1(AES_S, b >> 24 ) ) ^ + TABLE_GET_U4(AES_RCON, (i-4)/4); + } + AESKEY[i] = b ^= AESKEY[i-4]; + } +} + +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { + + aesroundkeys(); + + if( mode & AES_MICNOAUX ) { + AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0; + } else { + AESAUX[0] = swapmsbf(AESAUX[0]); + AESAUX[1] = swapmsbf(AESAUX[1]); + AESAUX[2] = swapmsbf(AESAUX[2]); + AESAUX[3] = swapmsbf(AESAUX[3]); + } + + while( (signed char)len > 0 ) { + u4_t a0, a1, a2, a3; + u4_t t0, t1, t2, t3; + u4_t *ki, *ke; + + // load input block + if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block + a0 = AESAUX[0]; + a1 = AESAUX[1]; + a2 = AESAUX[2]; + a3 = AESAUX[3]; + } + else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block + a0 = a1 = a2 = a3 = 0; // load null block + mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2 + } else + LOADDATA: { // load data block (partially) + for(t0=0; t0<16; t0++) { + t1 = (t1<<8) | ((t0> 4) != 0 ) { // last block + do { + // compute CMAC subkey K1 and K2 + t0 = a0 >> 31; // save MSB + a0 = (a0 << 1) | (a1 >> 31); + a1 = (a1 << 1) | (a2 >> 31); + a2 = (a2 << 1) | (a3 >> 31); + a3 = (a3 << 1); + if( t0 ) a3 ^= 0x87; + } while( --t1 ); + + AESAUX[0] ^= a0; + AESAUX[1] ^= a1; + AESAUX[2] ^= a2; + AESAUX[3] ^= a3; + mode &= ~AES_MICSUB; + goto LOADDATA; + } else { + // save cipher block as new iv + AESAUX[0] = a0; + AESAUX[1] = a1; + AESAUX[2] = a2; + AESAUX[3] = a3; + } + } else { // CIPHER + if( mode & AES_CTR ) { // xor block (partially) + t0 = (len > 16) ? 16: len; + for(t1=0; t1>24); + a0 <<= 8; + if((t1&3)==3) { + a0 = a1; + a1 = a2; + a2 = a3; + } + } + // update counter + AESAUX[3]++; + } else { // ECB + // store block + msbf4_write(buf+0, a0); + msbf4_write(buf+4, a1); + msbf4_write(buf+8, a2); + msbf4_write(buf+12, a3); + } + } + + // update block state + if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) { + buf += 16; + len -= 16; + } + mode |= AES_MICNOAUX; + } + return AESAUX[0]; +} + +#endif diff --git a/libraries/arduino-lmic-master/src/aes/other.c b/libraries/arduino-lmic-master/src/aes/other.c new file mode 100644 index 0000000..7093fb4 --- /dev/null +++ b/libraries/arduino-lmic-master/src/aes/other.c @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2016 Matthijs Kooijman + * + * LICENSE + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and + * redistribution. + * + * NO WARRANTY OF ANY KIND IS PROVIDED. + *******************************************************************************/ + +/* + * The original LMIC AES implementation integrates raw AES encryption + * with CMAC and AES-CTR in a single piece of code. Most other AES + * implementations (only) offer raw single block AES encryption, so this + * file contains an implementation of CMAC and AES-CTR, and offers the + * same API through the os_aes() function as the original AES + * implementation. This file assumes that there is an encryption + * function available with this signature: + * + * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); + * + * That takes a single 16-byte buffer and encrypts it wit the given + * 16-byte key. + */ + +#include "../lmic/oslmic.h" + +#if !defined(USE_ORIGINAL_AES) + +// This should be defined elsewhere +void lmic_aes_encrypt(u1_t *data, u1_t *key); + +// global area for passing parameters (aux, key) +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[16/sizeof(u4_t)]; + +// Shift the given buffer left one bit +static void shift_left(xref2u1_t buf, u1_t len) { + while (len--) { + u1_t next = len ? buf[1] : 0; + + u1_t val = (*buf << 1); + if (next & 0x80) + val |= 1; + *buf++ = val; + } +} + +// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, +// AESAUX is prepended to the message. AESAUX is used as working memory +// in any case. The CMAC result is returned in AESAUX as well. +static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) { + if (prepend_aux) + lmic_aes_encrypt(AESaux, AESkey); + else + memset (AESaux, 0, 16); + + while (len > 0) { + u1_t need_padding = 0; + for (u1_t i = 0; i < 16; ++i, ++buf, --len) { + if (len == 0) { + // The message is padded with 0x80 and then zeroes. + // Since zeroes are no-op for xor, we can just skip them + // and leave AESAUX unchanged for them. + AESaux[i] ^= 0x80; + need_padding = 1; + break; + } + AESaux[i] ^= *buf; + } + + if (len == 0) { + // Final block, xor with K1 or K2. K1 and K2 are calculated + // by encrypting the all-zeroes block and then applying some + // shifts and xor on that. + u1_t final_key[16]; + memset(final_key, 0, sizeof(final_key)); + lmic_aes_encrypt(final_key, AESkey); + + // Calculate K1 + u1_t msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + + // If the final block was not complete, calculate K2 from K1 + if (need_padding) { + msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + } + + // Xor with K1 or K2 + for (u1_t i = 0; i < sizeof(final_key); ++i) + AESaux[i] ^= final_key[i]; + } + + lmic_aes_encrypt(AESaux, AESkey); + } +} + +// Run AES-CTR using the key in AESKEY and using AESAUX as the +// counter block. The last byte of the counter block will be incremented +// for every block. The given buffer will be encrypted in place. +static void os_aes_ctr (xref2u1_t buf, u2_t len) { + u1_t ctr[16]; + while (len) { + // Encrypt the counter block with the selected key + memcpy(ctr, AESaux, sizeof(ctr)); + lmic_aes_encrypt(ctr, AESkey); + + // Xor the payload with the resulting ciphertext + for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) + *buf ^= ctr[i]; + + // Increment the block index byte + AESaux[15]++; + } +} + +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { + switch (mode & ~AES_MICNOAUX) { + case AES_MIC: + os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); + return os_rmsbf4(AESaux); + + case AES_ENC: + // TODO: Check / handle when len is not a multiple of 16 + for (u1_t i = 0; i < len; i += 16) + lmic_aes_encrypt(buf+i, AESkey); + break; + + case AES_CTR: + os_aes_ctr(buf, len); + break; + } + return 0; +} + +#endif // !defined(USE_ORIGINAL_AES) diff --git a/libraries/arduino-lmic-master/src/arduino_lmic.h b/libraries/arduino-lmic-master/src/arduino_lmic.h new file mode 100644 index 0000000..4e8d953 --- /dev/null +++ b/libraries/arduino-lmic-master/src/arduino_lmic.h @@ -0,0 +1,34 @@ +/* + +Module: arduino_lmic.h + +Function: + Arduino-LMIC C++ top-level include file + +Copyright & License: + See accompanying LICENSE file. + +Author: + Matthijs Kooijman 2015 + Terry Moore, MCCI November 2018 + +*/ + +#pragma once + +#ifndef _ARDUINO_LMIC_H_ +# define _ARDUINO_LMIC_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "lmic/lmic.h" +#include "lmic/lmic_bandplan.h" +#include "lmic/lmic_util.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _ARDUINO_LMIC_H_ */ diff --git a/libraries/arduino-lmic-master/src/arduino_lmic_hal_boards.h b/libraries/arduino-lmic-master/src/arduino_lmic_hal_boards.h new file mode 100644 index 0000000..4fc94c6 --- /dev/null +++ b/libraries/arduino-lmic-master/src/arduino_lmic_hal_boards.h @@ -0,0 +1,47 @@ +/* + +Module: arduino_lmic_hal_boards.h + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#pragma once + +#ifndef _arduino_lmic_hal_boards_h_ +# define _arduino_lmic_hal_boards_h_ + +#include "arduino_lmic_hal_configuration.h" + +namespace Arduino_LMIC { + +const HalPinmap_t *GetPinmap_FeatherM0LoRa(); +const HalPinmap_t *GetPinmap_Feather32U4LoRa(); + +const HalPinmap_t *GetPinmap_Catena4420(); +const HalPinmap_t *GetPinmap_Catena4551(); +const HalPinmap_t *GetPinmap_Catena4610(); +const HalPinmap_t *GetPinmap_Catena4610(); +const HalPinmap_t *GetPinmap_Catena4611(); +const HalPinmap_t *GetPinmap_Catena4612(); +const HalPinmap_t *GetPinmap_Catena4617(); +const HalPinmap_t *GetPinmap_Catena4618(); +const HalPinmap_t *GetPinmap_Catena4630(); +const HalPinmap_t *GetPinmap_Catena4801(); +const HalPinmap_t *GetPinmap_Catena4802(); +const HalPinmap_t* GetPinmap_ttgo_lora32_v1(); +const HalPinmap_t* GetPinmap_heltec_lora32(); +const HalPinmap_t* GetPinmap_Disco_L072cz_Lrwan1(); + +const HalPinmap_t *GetPinmap_ThisBoard(); + +}; /* namespace Arduino_LIMC */ + +#endif /* _arduino_lmic_hal_boards_h_ */ diff --git a/libraries/arduino-lmic-master/src/arduino_lmic_hal_configuration.h b/libraries/arduino-lmic-master/src/arduino_lmic_hal_configuration.h new file mode 100644 index 0000000..2350f4a --- /dev/null +++ b/libraries/arduino-lmic-master/src/arduino_lmic_hal_configuration.h @@ -0,0 +1,117 @@ +/* + +Module: arduino_lmic_hal_configuration.h + +Function: + Arduino-LMIC C++ HAL configuration APIs + +Copyright & License: + See accompanying LICENSE file. + +Author: + Matthijs Kooijman 2015 + Terry Moore, MCCI November 2018 + +*/ +#pragma once + +#ifndef _arduino_lmic_hal_configuration_h_ +# define _arduino_lmic_hal_configuration_h_ + +#include +#include "lmic/lmic_env.h" + +namespace Arduino_LMIC { + +/* these types should match the types used by the LMIC */ +typedef int32_t ostime_t; + +// this type is used when we need to represent a threee-state signal +enum class ThreeState_t : uint8_t { + Off = 0, + On = 1, + HiZ = 2 +}; + +// forward reference +class HalConfiguration_t; + +// +// for legacy reasons, we need a plain-old-data C-like +// structure that defines the "pin mapping" for the +// common pins. Many clients initialize an instance of +// this structure using named-field initialization. +// +// Be careful of alignment below. +struct HalPinmap_t { + // Use this for any unused pins. + static constexpr uint8_t UNUSED_PIN = 0xff; + static constexpr int NUM_DIO = 3; + // for backward compatibility... + static constexpr uint8_t LMIC_UNUSED_PIN = UNUSED_PIN; + + /* the contents */ + uint8_t nss; // byte 0: pin for select + uint8_t rxtx; // byte 1: pin for rx/tx control + uint8_t rst; // byte 2: pin for reset + uint8_t dio[NUM_DIO]; // bytes 3..5: pins for DIO0, DOI1, DIO2 + // true if we must set rxtx for rx_active, false for tx_active + uint8_t rxtx_rx_active; // byte 6: polarity of rxtx active + int8_t rssi_cal; // byte 7: cal in dB -- added to RSSI + // measured prior to decision. + // Must include noise guardband! + uint32_t spi_freq; // bytes 8..11: SPI freq in Hz. + + // optional pointer to configuration object (bytes 12..15) + HalConfiguration_t *pConfig; + }; + +class HalConfiguration_t + { +public: + HalConfiguration_t() {}; + + // these must match the constants in radio.c + enum class TxPowerPolicy_t : uint8_t + { + RFO, + PA_BOOST, + PA_BOOST_20dBm + }; + + virtual ostime_t setModuleActive(bool state) { + LMIC_API_PARAMETER(state); + + // by default, if not overridden, do nothing + // and return 0 to indicate that the caller + // need not delay. + return 0; + } + + virtual void begin(void) {} + virtual void end(void) {} + virtual bool queryUsingTcxo(void) { return false; } + + // compute desired transmit power policy. HopeRF needs + // (and previous versions of this library always chose) + // PA_BOOST mode. So that's our default. Override this + // for the Murata module. + virtual TxPowerPolicy_t getTxPowerPolicy( + TxPowerPolicy_t policy, + int8_t requestedPower, + uint32_t frequency + ) + { + LMIC_API_PARAMETER(policy); + LMIC_API_PARAMETER(requestedPower); + LMIC_API_PARAMETER(frequency); + // default: use PA_BOOST exclusively + return TxPowerPolicy_t::PA_BOOST; + } + }; + +bool hal_init_with_pinmap(const HalPinmap_t *pPinmap); + +}; // end namespace Arduino_LMIC + +#endif diff --git a/libraries/arduino-lmic-master/src/arduino_lmic_lorawan_compliance.h b/libraries/arduino-lmic-master/src/arduino_lmic_lorawan_compliance.h new file mode 100644 index 0000000..ceedc1b --- /dev/null +++ b/libraries/arduino-lmic-master/src/arduino_lmic_lorawan_compliance.h @@ -0,0 +1,31 @@ +/* + +Module: arduino_lmic_lorawan_compliance.h + +Function: + Arduino-LMIC C++ include file for LoRaWAN compliance + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI March 2019 + +*/ + +#pragma once + +#ifndef _ARDUINO_LMIC_LORAWAN_COMPLIANCE_H_ +# define _ARDUINO_LMIC_LORAWAN_COMPLIANCE_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "lmic/lorawan_spec_compliance.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _ARDUINO_LMIC_LORAWAN_COMPLIANCE_H_ */ diff --git a/libraries/arduino-lmic-master/src/arduino_lmic_user_configuration.h b/libraries/arduino-lmic-master/src/arduino_lmic_user_configuration.h new file mode 100644 index 0000000..c7c9576 --- /dev/null +++ b/libraries/arduino-lmic-master/src/arduino_lmic_user_configuration.h @@ -0,0 +1,30 @@ +/* + +Module: arduino_lmic_user_configuration.h + +Function: + Get the Arduino-LMIC configuration into scope + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ +#pragma once + +#ifndef _arduino_lmic_user_configuration_h_ +# define _arduino_lmic_user_configuration_h_ + +# ifdef __cplusplus +extern "C" { +# endif + +# include "lmic/lmic_config_preconditions.h" + +# ifdef __cplusplus +} +# endif + +#endif /* _arduino_lmic_user_configuration_h_ */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4420.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4420.cpp new file mode 100644 index 0000000..ae833b0 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4420.cpp @@ -0,0 +1,74 @@ +/* + +Module: getconfig_catena4420.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for MCCI Catena 4420 + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4420_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 6, + PIN_SX1276_NRESET = 5, + PIN_SX1276_DIO0 = 12, // pin assignment for DIO0 (aka IRQ) + PIN_SX1276_DIO1 = 11, + PIN_SX1276_DIO2 = 10, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_Catena4420_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4420_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4420_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4420_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4420_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4420_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4420_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4420(void) + { + return &myPinmap; + } + +} // namespace Arduino_LMIC; \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4551.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4551.cpp new file mode 100644 index 0000000..817dc77 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4551.cpp @@ -0,0 +1,84 @@ +/* + +Module: getconfig_catena4551.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4551) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4551) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4551_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // On the 4551, we can't control the TCXO; it's always on. + // So we use the default. + // virtual ostime_t setModuleActive(bool state) override + virtual bool queryUsingTcxo(void) override { return true; }; + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4551_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4551_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4551_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4551_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4551_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4551_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4551_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig, + }; + +const HalPinmap_t *GetPinmap_Catena4551(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4551) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4610.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4610.cpp new file mode 100644 index 0000000..54e2a56 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4610.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4610.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for the Catena 4610 + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4610) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4610_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4610_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4610_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4610_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4610_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4610_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4610_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4610_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4610(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4610 */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4611.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4611.cpp new file mode 100644 index 0000000..fbab281 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4611.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4611.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4611) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4611_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4611_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4611_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4611_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4611_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4611_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4611_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4611_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4611(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4611) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4612.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4612.cpp new file mode 100644 index 0000000..9fdb3bd --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4612.cpp @@ -0,0 +1,103 @@ +/* + +Module: getconfig_catena4612.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4612) || \ + /* legacy name */ \ + defined(ARDUINO_CATENA_4612) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4612_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4612_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4612_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4612_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4612_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4612_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4612_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4612_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4612(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4612) || defined(ARDUINO_CATENA_4612) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4617.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4617.cpp new file mode 100644 index 0000000..e3857a2 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4617.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4617.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Lakshmi Priya Natarajan, MCCI June 2019 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4617) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4617_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4617_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4617_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4617_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4617_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4617_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4617_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4617_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4617(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4617) || defined(ARDUINO_CATENA_4617) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4618.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4618.cpp new file mode 100644 index 0000000..9e0c449 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4618.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4618.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Lakshmi Priya Natarajan, MCCI June 2019 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4618) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4618_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4618_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4618_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4618_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4618_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4618_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4618_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4618_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4618(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4618) || defined(ARDUINO_CATENA_4618) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4630.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4630.cpp new file mode 100644 index 0000000..0ee0b27 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4630.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4630.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Dhinesh Kumar Pitchai, MCCI June 2019 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4630) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4630_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4630_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4630_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4630_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4630_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4630_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4630_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4630_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4630(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4630) || defined(ARDUINO_CATENA_4630) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4801.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4801.cpp new file mode 100644 index 0000000..eeeb59b --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4801.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4801.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4801) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4801) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4801_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(3); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4801_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4801_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4801_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4801_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4801_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4801_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4801_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4801(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_CATENA_4611) || defined(ARDUINO_CATENA_4801) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_catena4802.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4802.cpp new file mode 100644 index 0000000..ef9d8d6 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_catena4802.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4802.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Dhinesh Kumar Pitchai, MCCI November 2020 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4802) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4802) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4802_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(3); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4802_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4802_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4802_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4802_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4802_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4802_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4802_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4802(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_CATENA_4611) || defined(ARDUINO_CATENA_4802) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_disco_l072cs_lrwan1.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_disco_l072cs_lrwan1.cpp new file mode 100644 index 0000000..576b915 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_disco_l072cs_lrwan1.cpp @@ -0,0 +1,66 @@ +/* + +Module: getpinmap_disco_l072cz_lrwan1.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for the Discovery L072CZ LRWAN1 + +Copyright & License: + See accompanying LICENSE file. + +Author: + Helium February 2020 + +*/ + +#if defined(ARDUINO_DISCO_L072CZ_LRWAN1) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + + class HalConfiguration_Disco_L072cz_Lrwan1_t : public HalConfiguration_t + { + public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 37, + PIN_SX1276_NRESET = 33, + PIN_SX1276_DIO0 = 38, + PIN_SX1276_DIO1 = 39, + PIN_SX1276_DIO2 = 40, + PIN_SX1276_RXTX = 21, + }; + + virtual bool queryUsingTcxo(void) override { return false; }; + }; + // save some typing by bringing the pin numbers into scope + static HalConfiguration_Disco_L072cz_Lrwan1_t myConfig; + + static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NSS, + .rxtx = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_RXTX, + .rst = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NRESET, + + .dio = {HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO0, + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO1, + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO2, + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + + const HalPinmap_t *GetPinmap_Disco_L072cz_Lrwan1(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_DISCO_L072CZ_LRWAN1) */ diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_feather32u4lora.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_feather32u4lora.cpp new file mode 100644 index 0000000..c852eab --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_feather32u4lora.cpp @@ -0,0 +1,79 @@ +/* + +Module: getconfig_feather32u4lora.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for Adafruit Feather 32U4 LoRa + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Feather32U4LoRa_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 8, + PIN_SX1276_NRESET = 4, + PIN_SX1276_DIO0 = 7, + PIN_SX1276_DIO1 = 6, + PIN_SX1276_DIO2 = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_Feather32U4LoRa_t myConfig; + +// Pin mapping for Adafruit Feather 32u4 LoRa, etc. +// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only +// because MCCI doesn't have a test board; probably higher frequencies +// will work. + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 8, + .spi_freq = 1000000, /* 1MHz */ + .pConfig = &myConfig, + }; + +const HalPinmap_t *GetPinmap_Feather32U4LoRa(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_featherm0lora.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_featherm0lora.cpp new file mode 100644 index 0000000..7d221dc --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_featherm0lora.cpp @@ -0,0 +1,74 @@ +/* + +Module: getconfig_featherm0lora.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for Adafruit Feather M0 LoRa + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_FeatherM0LoRa_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 8, + PIN_SX1276_NRESET = 4, + PIN_SX1276_DIO0 = 3, + PIN_SX1276_DIO1 = 6, + PIN_SX1276_DIO2 = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_FeatherM0LoRa_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_FeatherM0LoRa(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_heltec_lora32.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_heltec_lora32.cpp new file mode 100644 index 0000000..897907a --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_heltec_lora32.cpp @@ -0,0 +1,80 @@ +/* + +Module: getpinmap_heltec_lora32.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for Heltec WiFi LoRa 32 (V1 & V2) and Heltec Wireless Stick + +Copyright & License: + See accompanying LICENSE file. + +Author: + Manuel Bleichenbacher, manuel.bleichenbacher@gmail.com October 2019 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +// Note: The pin constants SS, RST_LoRa and DIOx are defined in pins_arduino.h +// (board-specific variant in Arduino core). Even if it won't be used, this +// file needs to compile for all other variants as well. +#if !defined(ARDUINO_HELTEC_WIFI_LORA_32) && !defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) && !defined(ARDUINO_HELTEC_WIRELESS_STICK) +#undef SS +#undef RST_LoRa +#undef DIO0 +#undef DIO1 +#undef DIO2 +#define SS HalPinmap_t::UNUSED_PIN +#define RST_LoRa HalPinmap_t::UNUSED_PIN +#define DIO0 HalPinmap_t::UNUSED_PIN +#define DIO1 HalPinmap_t::UNUSED_PIN +#define DIO2 HalPinmap_t::UNUSED_PIN +#endif + + +namespace Arduino_LMIC +{ + +class HalConfiguration_heltec_lora32 : public HalConfiguration_t +{ +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = SS, + PIN_SX1276_NRESET = RST_LoRa, + PIN_SX1276_DIO0 = DIO0, + PIN_SX1276_DIO1 = DIO1, + PIN_SX1276_DIO2 = DIO2, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; +}; + +static HalConfiguration_heltec_lora32 myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_heltec_lora32::PIN_SX1276_NSS, + .rxtx = HalConfiguration_heltec_lora32::PIN_SX1276_ANT_SWITCH_RX, + .rst = HalConfiguration_heltec_lora32::PIN_SX1276_NRESET, + .dio = { + HalConfiguration_heltec_lora32::PIN_SX1276_DIO0, + HalConfiguration_heltec_lora32::PIN_SX1276_DIO1, + HalConfiguration_heltec_lora32::PIN_SX1276_DIO2, + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig}; + +const HalPinmap_t *GetPinmap_heltec_lora32(void) +{ + return &myPinmap; +} + +}; // namespace Arduino_LMIC diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_thisboard.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_thisboard.cpp new file mode 100644 index 0000000..77f7d01 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_thisboard.cpp @@ -0,0 +1,75 @@ +/* + +Module: getconfig_thisboard.cpp + +Function: + Return a suitable LMIC config for this board. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include + +namespace Arduino_LMIC { + +const HalPinmap_t *GetPinmap_ThisBoard(void) + { +/* +|| Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +|| m0 defs ADAFRUIT_FEATHER_M0 +*/ +#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) +# if defined(ARDUINO_MCCI_CATENA_4420) + // this uses a radiowing and an odd configuration + return GetPinmap_Catena4420(); +# else + // others use Feather M0 LoRa + return GetPinmap_FeatherM0LoRa(); +# endif +#elif defined(ARDUINO_AVR_FEATHER32U4) + return GetPinmap_Feather32U4LoRa(); +#elif defined(ARDUINO_MCCI_CATENA_4551) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4551) + return GetPinmap_Catena4551(); +#elif defined(ARDUINO_MCCI_CATENA_4610) + return GetPinmap_Catena4610(); +#elif defined(ARDUINO_MCCI_CATENA_4611) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4611) + return GetPinmap_Catena4611(); +#elif defined(ARDUINO_MCCI_CATENA_4612) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4612) + return GetPinmap_Catena4612(); +#elif defined(ARDUINO_MCCI_CATENA_4617) + return GetPinmap_Catena4617(); +#elif defined(ARDUINO_MCCI_CATENA_4618) + return GetPinmap_Catena4618(); +#elif defined(ARDUINO_MCCI_CATENA_4630) + return GetPinmap_Catena4630(); +#elif defined(ARDUINO_MCCI_CATENA_4801) + return GetPinmap_Catena4801(); +#elif defined(ARDUINO_MCCI_CATENA_4802) + return GetPinmap_Catena4802(); +#elif defined(ARDUINO_DISCO_L072CZ_LRWAN1) + return GetPinmap_Disco_L072cz_Lrwan1(); +#elif defined(PINNOCHIO_SCOUT) + return GetPinmap_PinnochioScount(); +#elif defined(ARDUINO_TTGO_LoRa32_V1) + return GetPinmap_ttgo_lora32_v1(); +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) || defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_HELTEC_WIRELESS_STICK) + return GetPinmap_heltec_lora32(); +#else + #pragma message("Board not supported -- use an explicit pinmap") + return nullptr; +#endif + } + +}; // namespace Arduino_LMIC + diff --git a/libraries/arduino-lmic-master/src/hal/getpinmap_ttgo_lora32_v1.cpp b/libraries/arduino-lmic-master/src/hal/getpinmap_ttgo_lora32_v1.cpp new file mode 100644 index 0000000..71db3eb --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/getpinmap_ttgo_lora32_v1.cpp @@ -0,0 +1,78 @@ +/* + +Module: getconfig_ttgo_lora32_v1.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for TTGO ESP32 OLED V1 + +Copyright & License: + See accompanying LICENSE file. + +Author: + German Martin, gmag11@gmail.com June 2019 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +#define LORA_DIO0 26 +#define LORA_DIO1 33 +#define LORA_DIO2 32 + +namespace Arduino_LMIC { + +class HalConfiguration_ttgo_lora32_v1 : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 18, + PIN_SX1276_NRESET = 14, + PIN_SX1276_DIO0 = LORA_DIO0, + PIN_SX1276_DIO1 = LORA_DIO1, + PIN_SX1276_DIO2 = LORA_DIO2, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_ttgo_lora32_v1 myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_ttgo_lora32_v1::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_ttgo_lora32_v1::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_ttgo_lora32_v1::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_ttgo_lora32_v1::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_ttgo_lora32_v1::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_ttgo_lora32_v1::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t * GetPinmap_ttgo_lora32_v1 (void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC diff --git a/libraries/arduino-lmic-master/src/hal/hal.cpp b/libraries/arduino-lmic-master/src/hal/hal.cpp new file mode 100644 index 0000000..6e72a58 --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/hal.cpp @@ -0,0 +1,512 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * Copyright (c) 2018-2019 MCCI Corporation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ + +#include +#include +// include all the lmic header files, including ../lmic/hal.h +#include "../lmic.h" +// include the C++ hal.h +#include "hal.h" +// we may need some things from stdio. +#include + +// ----------------------------------------------------------------------------- +// I/O + +static const Arduino_LMIC::HalPinmap_t *plmic_pins; +static Arduino_LMIC::HalConfiguration_t *pHalConfig; +static Arduino_LMIC::HalConfiguration_t nullHalConig; +static hal_failure_handler_t* custom_hal_failure_handler = NULL; + +static void hal_interrupt_init(); // Fwd declaration + +static void hal_io_init () { + // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK + ASSERT(plmic_pins->nss != LMIC_UNUSED_PIN); + ASSERT(plmic_pins->dio[0] != LMIC_UNUSED_PIN); + ASSERT(plmic_pins->dio[1] != LMIC_UNUSED_PIN || plmic_pins->dio[2] != LMIC_UNUSED_PIN); + +// Serial.print("nss: "); Serial.println(plmic_pins->nss); +// Serial.print("rst: "); Serial.println(plmic_pins->rst); +// Serial.print("dio[0]: "); Serial.println(plmic_pins->dio[0]); +// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]); +// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]); + + // initialize SPI chip select to high (it's active low) + digitalWrite(plmic_pins->nss, HIGH); + pinMode(plmic_pins->nss, OUTPUT); + + if (plmic_pins->rxtx != LMIC_UNUSED_PIN) { + // initialize to RX + digitalWrite(plmic_pins->rxtx, LOW != plmic_pins->rxtx_rx_active); + pinMode(plmic_pins->rxtx, OUTPUT); + } + if (plmic_pins->rst != LMIC_UNUSED_PIN) { + // initialize RST to floating + pinMode(plmic_pins->rst, INPUT); + } + + hal_interrupt_init(); +} + +// val == 1 => tx +void hal_pin_rxtx (u1_t val) { + if (plmic_pins->rxtx != LMIC_UNUSED_PIN) + digitalWrite(plmic_pins->rxtx, val != plmic_pins->rxtx_rx_active); +} + +// set radio RST pin to given value (or keep floating!) +void hal_pin_rst (u1_t val) { + if (plmic_pins->rst == LMIC_UNUSED_PIN) + return; + + if(val == 0 || val == 1) { // drive pin + digitalWrite(plmic_pins->rst, val); + pinMode(plmic_pins->rst, OUTPUT); + } else { // keep pin floating + pinMode(plmic_pins->rst, INPUT); + } +} + +s1_t hal_getRssiCal (void) { + return plmic_pins->rssi_cal; +} + +//-------------------- +// Interrupt handling +//-------------------- +static constexpr unsigned NUM_DIO_INTERRUPT = 3; +static_assert(NUM_DIO_INTERRUPT <= NUM_DIO, "Number of interrupt-sensitive lines must be less than number of GPIOs"); +static ostime_t interrupt_time[NUM_DIO_INTERRUPT] = {0}; + +#if !defined(LMIC_USE_INTERRUPTS) +static void hal_interrupt_init() { + pinMode(plmic_pins->dio[0], INPUT); + if (plmic_pins->dio[1] != LMIC_UNUSED_PIN) + pinMode(plmic_pins->dio[1], INPUT); + if (plmic_pins->dio[2] != LMIC_UNUSED_PIN) + pinMode(plmic_pins->dio[2], INPUT); + static_assert(NUM_DIO_INTERRUPT == 3, "Number of interrupt lines must be set to 3"); +} + +static bool dio_states[NUM_DIO_INTERRUPT] = {0}; +void hal_pollPendingIRQs_helper() { + uint8_t i; + for (i = 0; i < NUM_DIO_INTERRUPT; ++i) { + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + if (dio_states[i] != digitalRead(plmic_pins->dio[i])) { + dio_states[i] = !dio_states[i]; + if (dio_states[i] && interrupt_time[i] == 0) { + ostime_t const now = os_getTime(); + interrupt_time[i] = now ? now : 1; + } + } + } +} + +#else +// Interrupt handlers + +static void hal_isrPin0() { + if (interrupt_time[0] == 0) { + ostime_t now = os_getTime(); + interrupt_time[0] = now ? now : 1; + } +} +static void hal_isrPin1() { + if (interrupt_time[1] == 0) { + ostime_t now = os_getTime(); + interrupt_time[1] = now ? now : 1; + } +} +static void hal_isrPin2() { + if (interrupt_time[2] == 0) { + ostime_t now = os_getTime(); + interrupt_time[2] = now ? now : 1; + } +} + +typedef void (*isr_t)(); +static const isr_t interrupt_fns[NUM_DIO_INTERRUPT] = {hal_isrPin0, hal_isrPin1, hal_isrPin2}; +static_assert(NUM_DIO_INTERRUPT == 3, "number of interrupts must be 3 for initializing interrupt_fns[]"); + +static void hal_interrupt_init() { + for (uint8_t i = 0; i < NUM_DIO_INTERRUPT; ++i) { + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + pinMode(plmic_pins->dio[i], INPUT); + attachInterrupt(digitalPinToInterrupt(plmic_pins->dio[i]), interrupt_fns[i], RISING); + } +} +#endif // LMIC_USE_INTERRUPTS + +void hal_processPendingIRQs() { + uint8_t i; + for (i = 0; i < NUM_DIO_INTERRUPT; ++i) { + ostime_t iTime; + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + // NOTE(tmm@mcci.com): if using interrupts, this next step + // assumes uniprocessor and fairly strict memory ordering + // semantics relative to ISRs. It would be better to use + // interlocked-exchange, but that's really far beyond + // Arduino semantics. Because our ISRs use "first time + // stamp" semantics, we don't have a value-race. But if + // we were to disable ints here, we might observe a second + // edge that we'll otherwise miss. Not a problem in this + // use case, as the radio won't release IRQs until we + // explicitly clear them. + iTime = interrupt_time[i]; + if (iTime) { + interrupt_time[i] = 0; + radio_irq_handler_v2(i, iTime); + } + } +} + +// ----------------------------------------------------------------------------- +// SPI + +static void hal_spi_init () { + SPI.begin(); +} + +static void hal_spi_trx(u1_t cmd, u1_t* buf, size_t len, bit_t is_read) { + uint32_t spi_freq; + u1_t nss = plmic_pins->nss; + + if ((spi_freq = plmic_pins->spi_freq) == 0) + spi_freq = LMIC_SPI_FREQ; + + SPISettings settings(spi_freq, MSBFIRST, SPI_MODE0); + SPI.beginTransaction(settings); + digitalWrite(nss, 0); + + SPI.transfer(cmd); + + for (; len > 0; --len, ++buf) { + u1_t data = is_read ? 0x00 : *buf; + data = SPI.transfer(data); + if (is_read) + *buf = data; + } + + digitalWrite(nss, 1); + SPI.endTransaction(); +} + +void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len) { + hal_spi_trx(cmd, (u1_t*)buf, len, 0); +} + +void hal_spi_read(u1_t cmd, u1_t* buf, size_t len) { + hal_spi_trx(cmd, buf, len, 1); +} + +// ----------------------------------------------------------------------------- +// TIME + +static void hal_time_init () { + // Nothing to do +} + +u4_t hal_ticks () { + // Because micros() is scaled down in this function, micros() will + // overflow before the tick timer should, causing the tick timer to + // miss a significant part of its values if not corrected. To fix + // this, the "overflow" serves as an overflow area for the micros() + // counter. It consists of three parts: + // - The US_PER_OSTICK upper bits are effectively an extension for + // the micros() counter and are added to the result of this + // function. + // - The next bit overlaps with the most significant bit of + // micros(). This is used to detect micros() overflows. + // - The remaining bits are always zero. + // + // By comparing the overlapping bit with the corresponding bit in + // the micros() return value, overflows can be detected and the + // upper bits are incremented. This is done using some clever + // bitwise operations, to remove the need for comparisons and a + // jumps, which should result in efficient code. By avoiding shifts + // other than by multiples of 8 as much as possible, this is also + // efficient on AVR (which only has 1-bit shifts). + static uint8_t overflow = 0; + + // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, + // the others will be the lower bits of our return value. + uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; + // Most significant byte of scaled + uint8_t msb = scaled >> 24; + // Mask pointing to the overlapping bit in msb and overflow. + const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); + // Update overflow. If the overlapping bit is different + // between overflow and msb, it is added to the stored value, + // so the overlapping bit becomes equal again and, if it changed + // from 1 to 0, the upper bits are incremented. + overflow += (msb ^ overflow) & mask; + + // Return the scaled value with the upper bits of stored added. The + // overlapping bit will be equal and the lower bits will be 0, so + // bitwise or is a no-op for them. + return scaled | ((uint32_t)overflow << 24); + + // 0 leads to correct, but overly complex code (it could just return + // micros() unmodified), 8 leaves no room for the overlapping bit. + static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); +} + +// Returns the number of ticks until time. Negative values indicate that +// time has already passed. +static s4_t delta_time(u4_t time) { + return (s4_t)(time - hal_ticks()); +} + +// deal with boards that are stressed by no-interrupt delays #529, etc. +#if defined(ARDUINO_DISCO_L072CZ_LRWAN1) +# define HAL_WAITUNTIL_DOWNCOUNT_MS 16 // on this board, 16 ms works better +# define HAL_WAITUNTIL_DOWNCOUNT_THRESH ms2osticks(16) // as does this threashold. +#else +# define HAL_WAITUNTIL_DOWNCOUNT_MS 8 // on most boards, delay for 8 ms +# define HAL_WAITUNTIL_DOWNCOUNT_THRESH ms2osticks(9) // but try to leave a little slack for final timing. +#endif + +u4_t hal_waitUntil (u4_t time) { + s4_t delta = delta_time(time); + // check for already too late. + if (delta < 0) + return -delta; + + // From delayMicroseconds docs: Currently, the largest value that + // will produce an accurate delay is 16383. Also, STM32 does a better + // job with delay is less than 10,000 us; so reduce in steps. + // It's nice to use delay() for the longer times. + while (delta > HAL_WAITUNTIL_DOWNCOUNT_THRESH) { + // deliberately delay 8ms rather than 9ms, so we + // will exit loop with delta typically positive. + // Depends on BSP keeping time accurately even if interrupts + // are disabled. + delay(HAL_WAITUNTIL_DOWNCOUNT_MS); + // re-synchronize. + delta = delta_time(time); + } + + // The radio driver runs with interrupt disabled, and this can + // mess up timing APIs on some platforms. If we know the BSP feature + // set, we can decide whether to use delta_time() [more exact, + // but not always possible with interrupts off], or fall back to + // delay_microseconds() [less exact, but more universal] + +#if defined(_mcci_arduino_version) + // unluckily, delayMicroseconds() isn't very accurate. + // but delta_time() works with interrupts disabled. + // so spin using delta_time(). + while (delta_time(time) > 0) + /* loop */; +#else // ! defined(_mcci_arduino_version) + // on other BSPs, we need to stick with the older way, + // until we fix the radio driver to run with interrupts + // enabled. + if (delta > 0) + delayMicroseconds(delta * US_PER_OSTICK); +#endif // ! defined(_mcci_arduino_version) + + // we aren't "late". Callers are interested in gross delays, not + // necessarily delays due to poor timekeeping here. + return 0; +} + +// check and rewind for target time +u1_t hal_checkTimer (u4_t time) { + // No need to schedule wakeup, since we're not sleeping + return delta_time(time) <= 0; +} + +static uint8_t irqlevel = 0; + +void hal_disableIRQs () { + noInterrupts(); + irqlevel++; +} + +void hal_enableIRQs () { + if(--irqlevel == 0) { + interrupts(); + +#if !defined(LMIC_USE_INTERRUPTS) + // Instead of using proper interrupts (which are a bit tricky + // and/or not available on all pins on AVR), just poll the pin + // values. Since os_runloop disables and re-enables interrupts, + // putting this here makes sure we check at least once every + // loop. + // + // As an additional bonus, this prevents the can of worms that + // we would otherwise get for running SPI transfers inside ISRs. + // We merely collect the edges and timestamps here; we wait for + // a call to hal_processPendingIRQs() before dispatching. + hal_pollPendingIRQs_helper(); +#endif /* !defined(LMIC_USE_INTERRUPTS) */ + } +} + +uint8_t hal_getIrqLevel(void) { + return irqlevel; +} + +void hal_sleep () { + // Not implemented +} + +// ----------------------------------------------------------------------------- + +#if defined(LMIC_PRINTF_TO) +#if !defined(__AVR) +static ssize_t uart_putchar (void *, const char *buf, size_t len) { + return LMIC_PRINTF_TO.write((const uint8_t *)buf, len); +} + +static cookie_io_functions_t functions = + { + .read = NULL, + .write = uart_putchar, + .seek = NULL, + .close = NULL + }; + +void hal_printf_init() { + stdout = fopencookie(NULL, "w", functions); + if (stdout != nullptr) { + setvbuf(stdout, NULL, _IONBF, 0); + } +} +#else // defined(__AVR) +static int uart_putchar (char c, FILE *) +{ + LMIC_PRINTF_TO.write(c) ; + return 0 ; +} + +void hal_printf_init() { + // create a FILE structure to reference our UART output function + static FILE uartout; + memset(&uartout, 0, sizeof(uartout)); + + // fill in the UART file descriptor with pointer to writer. + fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); + + // The uart is the standard output device STDOUT. + stdout = &uartout ; +} + +#endif // !defined(ESP8266) || defined(ESP31B) || defined(ESP32) +#endif // defined(LMIC_PRINTF_TO) + +void hal_init (void) { + // use the global constant + Arduino_LMIC::hal_init_with_pinmap(&lmic_pins); +} + +// hal_init_ex is a C API routine, written in C++, and it's called +// with a pointer to an lmic_pinmap. +void hal_init_ex (const void *pContext) { + const lmic_pinmap * const pHalPinmap = (const lmic_pinmap *) pContext; + if (! Arduino_LMIC::hal_init_with_pinmap(pHalPinmap)) { + hal_failed(__FILE__, __LINE__); + } +} + +// C++ API: initialize the HAL properly with a configuration object +namespace Arduino_LMIC { +bool hal_init_with_pinmap(const HalPinmap_t *pPinmap) + { + if (pPinmap == nullptr) + return false; + + // set the static pinmap pointer. + plmic_pins = pPinmap; + + // set the static HalConfiguration pointer. + HalConfiguration_t * const pThisHalConfig = pPinmap->pConfig; + + if (pThisHalConfig != nullptr) + pHalConfig = pThisHalConfig; + else + pHalConfig = &nullHalConig; + + pHalConfig->begin(); + + // configure radio I/O and interrupt handler + hal_io_init(); + // configure radio SPI + hal_spi_init(); + // configure timer and interrupt handler + hal_time_init(); +#if defined(LMIC_PRINTF_TO) + // printf support + hal_printf_init(); +#endif + // declare success + return true; + } +}; // namespace Arduino_LMIC + + +void hal_failed (const char *file, u2_t line) { + if (custom_hal_failure_handler != NULL) { + (*custom_hal_failure_handler)(file, line); + } + +#if defined(LMIC_FAILURE_TO) + LMIC_FAILURE_TO.println("FAILURE "); + LMIC_FAILURE_TO.print(file); + LMIC_FAILURE_TO.print(':'); + LMIC_FAILURE_TO.println(line); + LMIC_FAILURE_TO.flush(); +#endif + + hal_disableIRQs(); + + // Infinite loop + while (1) { + ; + } +} + +void hal_set_failure_handler(const hal_failure_handler_t* const handler) { + custom_hal_failure_handler = handler; +} + +ostime_t hal_setModuleActive (bit_t val) { + // setModuleActive() takes a c++ bool, so + // it effectively says "val != 0". We + // don't have to. + return pHalConfig->setModuleActive(val); +} + +bit_t hal_queryUsingTcxo(void) { + return pHalConfig->queryUsingTcxo(); +} + +uint8_t hal_getTxPowerPolicy( + u1_t inputPolicy, + s1_t requestedPower, + u4_t frequency + ) { + return (uint8_t) pHalConfig->getTxPowerPolicy( + Arduino_LMIC::HalConfiguration_t::TxPowerPolicy_t(inputPolicy), + requestedPower, + frequency + ); +} diff --git a/libraries/arduino-lmic-master/src/hal/hal.h b/libraries/arduino-lmic-master/src/hal/hal.h new file mode 100644 index 0000000..f831c1e --- /dev/null +++ b/libraries/arduino-lmic-master/src/hal/hal.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2015-2016 Matthijs Kooijman + * Copyright (c) 2016-2018 MCCI Corporation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ +#ifndef _hal_hal_h_ +#define _hal_hal_h_ + +#include "arduino_lmic_hal_configuration.h" + +// for compatbility reasons, we need to disclose the configuration +// structure as global type lmic_pinmap. +using lmic_pinmap = Arduino_LMIC::HalPinmap_t; + +// similarly, we need to disclose NUM_DIO and LMIC_UNUSED_PIN +static const int NUM_DIO = lmic_pinmap::NUM_DIO; + +// Use this for any unused pins. +const u1_t LMIC_UNUSED_PIN = lmic_pinmap::UNUSED_PIN; + +// Declared here, to be defined and initialized by the application. +// Use os_init_ex() if you want not to use a const table, or if +// you need to define a derived type (so you can override methods). +extern const lmic_pinmap lmic_pins; + +#endif // _hal_hal_h_ diff --git a/libraries/arduino-lmic-master/src/lmic.h b/libraries/arduino-lmic-master/src/lmic.h new file mode 100644 index 0000000..d0423a3 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic.h @@ -0,0 +1,30 @@ +/* + +Module: lmic.h + +Function: + Deprecated C++ top-level include file (use instead). + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +Note: + This header file is deprecated and is included for + transitional purposes. It's deprecated because it's + confusing to have src/lmic.h (this file) and src/lmic/lmic.h + (the API file for the C library). We can't take it out + yet, because it would inconvenience the world, but + we can hope that someday it will wither away (on a major + version bump). + + Please don't add any new functionality in this file; + it is just a wrapper for arduino_lmic.h. + +*/ + +#include "arduino_lmic.h" + +/* end of file */ diff --git a/libraries/arduino-lmic-master/src/lmic/config.h b/libraries/arduino-lmic-master/src/lmic/config.h new file mode 100644 index 0000000..abd0538 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/config.h @@ -0,0 +1,223 @@ +#ifndef _lmic_config_h_ +#define _lmic_config_h_ + +// In the original LMIC code, these config values were defined on the +// gcc commandline. Since Arduino does not allow easily modifying the +// compiler commandline unless you modify the BSP, you have two choices: +// +// - edit {libraries}/arduino-lmic/project_config/lmic_project_config.h; +// - use a BSP like the MCCI Arduino BSPs, which get the configuration +// from the boards.txt file through a menu option. +// +// You definitely should not edit this file. + +// set up preconditions, and load configuration if needed. +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +// check post-conditions. + +// make sure that we have exactly one target region defined. +#if CFG_LMIC_REGION_MASK == 0 +# define CFG_eu868 1 +#elif (CFG_LMIC_REGION_MASK & (-CFG_LMIC_REGION_MASK)) != CFG_LMIC_REGION_MASK +# error You can define at most one of CFG_... variables +#elif (CFG_LMIC_REGION_MASK & LMIC_REGIONS_SUPPORTED) == 0 +# error The selected CFG_... region is not supported yet. +#endif + +// make sure that LMIC_COUNTRY_CODE is defined. +#ifndef LMIC_COUNTRY_CODE +# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_NONE +#endif + +// if the country code is Japan, then the region must be AS923 +#if LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP && CFG_region != LMIC_REGION_as923 +# error "If country code is JP, then region must be AS923" +#endif + +// check for internal consistency +#if !(CFG_LMIC_EU_like || CFG_LMIC_US_like) +# error "Internal error: Neither EU-like nor US-like!" +#endif + +// This is the SX1272/SX1273 radio, which is also used on the HopeRF +// RFM92 boards. +//#define CFG_sx1272_radio 1 +// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on +// the HopeRF RFM95 boards. +//#define CFG_sx1276_radio 1 + +// ensure that a radio is defined. +#if ! (defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio)) +# warning Target radio not defined, assuming CFG_sx1276_radio +#define CFG_sx1276_radio 1 +#elif defined(CFG_sx1272_radio) && defined(CFG_sx1276_radio) +# error You can define at most one of CFG_sx1272_radio and CF_sx1276_radio +#endif + +// LMIC requires ticks to be 15.5μs - 100 μs long +#ifndef OSTICKS_PER_SEC +// 16 μs per tick +# ifndef US_PER_OSTICK_EXPONENT +# define US_PER_OSTICK_EXPONENT 4 +# endif +# define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +# define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) +#endif /* OSTICKS_PER_SEC */ + +#if ! (10000 <= OSTICKS_PER_SEC && OSTICKS_PER_SEC < 64516) +# error LMIC requires ticks to be 15.5 us to 100 us long +#endif + +// Change the SPI clock speed if you encounter errors +// communicating with the radio. +// The standard range is 125kHz-8MHz, but some boards can go faster. +#ifndef LMIC_SPI_FREQ +#define LMIC_SPI_FREQ 1E6 +#endif + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +#ifndef LMIC_DEBUG_LEVEL +#define LMIC_DEBUG_LEVEL 0 +#endif + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Enable this to use interrupt handler routines listening for RISING signals. +// Otherwise, the library polls digital input lines for changes. +//#define LMIC_USE_INTERRUPTS + +// If DISABLE_LMIC_FAILURE_TO is defined, runtime assertion failures +// silently halt execution. Otherwise, LMIC_FAILURE_TO should be defined +// as the name of an object derived from Print, which will be used for +// displaying runtime assertion failures. If you say nothing in your +// lmic_project_config.h, runtime assertion failures are displayed +// using the Serial object. +#if ! defined(DISABLE_LMIC_FAILURE_TO) && ! defined(LMIC_FAILURE_TO) +#define LMIC_FAILURE_TO Serial +#endif + +// define this in lmic_project_config.h to disable all code related to joining +//#define DISABLE_JOIN +// define this in lmic_project_config.h to disable all code related to ping +//#define DISABLE_PING +// define this in lmic_project_config.h to disable all code related to beacon tracking. +// Requires ping to be disabled too +//#define DISABLE_BEACONS + +// define these in lmic_project_config.h to disable the corresponding MAC commands. +// Class A +//#define DISABLE_MCMD_DutyCycleReq // duty cycle cap +//#define DISABLE_MCMD_RXParamSetupReq // 2nd DN window param +//#define DISABLE_MCMD_NewChannelReq // set new channel +//#define DISABLE_MCMD_DlChannelReq // set downlink channel for RX1 for given uplink channel. +//#define DISABLE_MCMD_RXTimingSetupReq // delay between TX and RX +// Class B +//#define DISABLE_MCMD_PingSlotChannelReq // set ping freq, automatically disabled by DISABLE_PING +//#define ENABLE_MCMD_BeaconTimingAns // next beacon start, DEPRECATED, normally disabled by DISABLE_BEACON + +// DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this. +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +// #define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +// #define USE_IDEETRON_AES + +#if ! (defined(USE_ORIGINAL_AES) || defined(USE_IDEETRON_AES)) +# define USE_IDEETRON_AES +#endif + +#if defined(USE_ORIGINAL_AES) && defined(USE_IDEETRON_AES) +# error "You may define at most one of USE_ORIGINAL_AES and USE_IDEETRON_AES" +#endif + +// LMIC_DISABLE_DR_LEGACY +// turn off legacy DR_* symbols that vary by bandplan. +// Older code uses these for configuration. EU868_DR_*, US915_DR_* +// etc symbols are prefered, but breaking older code is inconvenient for +// everybody. We don't want to use DR_* in the LMIC itself, so we provide +// this #define to allow them to be removed. +#if !defined(LMIC_DR_LEGACY) +# if !defined(LMIC_DISABLE_DR_LEGACY) +# define LMIC_DR_LEGACY 1 +# else // defined(LMIC_DISABLE_DR_LEGACY) +# define LMIC_DR_LEGACY 0 +# endif // defined(LMIC_DISABLE_DR_LEGACY) +#endif // LMIC_DR_LEGACY + +// LMIC_ENABLE_DeviceTimeReq +// enable support for MCMD_DeviceTimeReq and MCMD_DeviceTimeAns +// this is always defined, and non-zero to enable it. +#if !defined(LMIC_ENABLE_DeviceTimeReq) +# define LMIC_ENABLE_DeviceTimeReq 0 +#endif + +// LMIC_ENABLE_user_events +// Enable/disable support for programmable callbacks for events, rx, and tx. +// This is always defined, and non-zero to enable. Default is enabled. +#if !defined(LMIC_ENABLE_user_events) +# define LMIC_ENABLE_user_events 1 +#endif + +// LMIC_ENABLE_onEvent +// Enable/disable support for out-call to user-supplied `onEvent()` function. +// This is always defined, and non-zero to enable. Default is enabled. +#if !defined(LMIC_ENABLE_onEvent) +# define LMIC_ENABLE_onEvent 1 +#endif + +// LMIC_ENABLE_long_messages +// LMIC certification requires full-length 255 frames, but to save RAM, +// a shorter maximum can be set. This controls both RX and TX buffers, +// so reducing this by 1 saves 2 bytes of RAM. +#if defined(LMIC_ENABLE_long_messages) && defined(LMIC_MAX_FRAME_LENGTH) +#error "Use only one of LMIC_ENABLE_long_messages or LMIC_MAX_FRAME_LENGTH" +#elif defined(LMIC_ENABLE_long_messages) && LMIC_ENABLE_long_messages == 0 +# define LMIC_MAX_FRAME_LENGTH 64 +#elif !defined(LMIC_MAX_FRAME_LENGTH) +# define LMIC_MAX_FRAME_LENGTH 255 +#elif LMIC_MAX_FRAME_LENGTH > 255 +#error "LMIC_MAX_FRAME_LENGTH cannot be larger than 255" +#endif + +// LMIC_ENABLE_event_logging +// LMIC debugging for certification tests requires this, because debug prints affect +// timing too dramatically. But normal operation doesn't need this. +#if !defined(LMIC_ENABLE_event_logging) +# define LMIC_ENABLE_event_logging 0 /* PARAM */ +#endif + +// LMIC_LORAWAN_SPEC_VERSION +#if !defined(LMIC_LORAWAN_SPEC_VERSION) +# define LMIC_LORAWAN_SPEC_VERSION LMIC_LORAWAN_SPEC_VERSION_1_0_3 +#endif + +// LMIC_ENABLE_arbitrary_clock_error +// We normally don't want to allow users to set wide clock errors, because +// we assume reasonably-disciplined os_getTime() values. But... there might +// be platforms that require wider errors. +#if !defined(LMIC_ENABLE_arbitrary_clock_error) +# define LMIC_ENABLE_arbitrary_clock_error 0 /* PARAM */ +#endif + +#endif // _lmic_config_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/hal.h b/libraries/arduino-lmic-master/src/lmic/hal.h new file mode 100644 index 0000000..f232480 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/hal.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016, 2018-2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _lmic_hal_h_ +#define _lmic_hal_h_ + +#ifndef _oslmic_types_h_ +# include "oslmic_types.h" +#endif + +#ifndef _lmic_env_h_ +# include "lmic_env.h" +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + +// The type of an optional user-defined failure handler routine +typedef void LMIC_ABI_STD hal_failure_handler_t(const char* const file, const uint16_t line); + +/* + * initialize hardware (IO, SPI, TIMER, IRQ). + * This API is deprecated as it uses the const global lmic_pins, + * which the platform can't control or change. + */ +void hal_init (void); + +/* + * Initialize hardware, passing in platform-specific context + * The pointer is to a HalPinmap_t. + */ +void hal_init_ex (const void *pContext); + +/* + * drive radio RX/TX pins (0=rx, 1=tx). Actual polarity + * is determined by the value of HalPinmap_t::rxtx_rx_active. + */ +void hal_pin_rxtx (u1_t val); + +/* + * control radio RST pin (0=low, 1=high, 2=floating) + */ +void hal_pin_rst (u1_t val); + +/* + * Perform SPI write transaction with radio chip + * - write the command byte 'cmd' + * - write 'len' bytes out of 'buf' + */ +void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len); + +/* + * Perform SPI read transaction with radio chip + * - write the command byte 'cmd' + * - read 'len' bytes into 'buf' + */ +void hal_spi_read(u1_t cmd, u1_t* buf, size_t len); + +/* + * disable all CPU interrupts. + * - might be invoked nested + * - will be followed by matching call to hal_enableIRQs() + */ +void hal_disableIRQs (void); + +/* + * enable CPU interrupts. + */ +void hal_enableIRQs (void); + +/* + * return CPU interrupt nesting count + */ +uint8_t hal_getIrqLevel (void); + +/* + * put system and CPU in low-power mode, sleep until interrupt. + */ +void hal_sleep (void); + +/* + * return 32-bit system time in ticks. + */ +u4_t hal_ticks (void); + +/* + * busy-wait until specified timestamp (in ticks) is reached. If on-time, return 0, + * otherwise return the number of ticks we were late. + */ +u4_t hal_waitUntil (u4_t time); + +/* + * check and rewind timer for target time. + * - return 1 if target time is close + * - otherwise rewind timer for target time or full period and return 0 + */ +u1_t hal_checkTimer (u4_t targettime); + +/* + * perform fatal failure action. + * - called by assertions + * - action could be HALT or reboot + */ +void hal_failed (const char *file, u2_t line); + +/* + * set a custom hal failure handler routine. The default behaviour, defined in + * hal_failed(), is to halt by looping infintely. + */ +void hal_set_failure_handler(const hal_failure_handler_t* const); + +/* + * get the calibration value for radio_rssi + */ +s1_t hal_getRssiCal (void); + +/* + * control the radio state + * - if val == 0, turn tcxo off and otherwise prepare for sleep + * - if val == 1, turn tcxo on and otherwise prep for activity + * - return the number of ticks that we need to wait + */ +ostime_t hal_setModuleActive (bit_t val); + +/* find out if we're using Tcxo */ +bit_t hal_queryUsingTcxo(void); + +/* represent the various radio TX power policy */ +enum { + LMICHAL_radio_tx_power_policy_rfo = 0, + LMICHAL_radio_tx_power_policy_paboost = 1, + LMICHAL_radio_tx_power_policy_20dBm = 2, +}; + +/* + * query the configuration as to the Tx Power Policy + * to be used on this board, given our desires and + * requested power. + */ +uint8_t hal_getTxPowerPolicy( + u1_t inputPolicy, + s1_t requestedPower, + u4_t freq + ); + +void hal_pollPendingIRQs_helper(); +void hal_processPendingIRQs(void); + +/// \brief check for any pending interrupts: stub if interrupts are enabled. +static void inline hal_pollPendingIRQs(void) + { +#if !defined(LMIC_USE_INTERRUPTS) + hal_pollPendingIRQs_helper(); +#endif /* !defined(LMIC_USE_INTERRUPTS) */ + } + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _lmic_hal_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic.c b/libraries/arduino-lmic-master/src/lmic/lmic.c new file mode 100644 index 0000000..957d622 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic.c @@ -0,0 +1,3103 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Copyright (c) 2016-2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file +#define LMIC_DR_LEGACY 0 +#include "lmic_bandplan.h" + +#if defined(DISABLE_BEACONS) && !defined(DISABLE_PING) +#error Ping needs beacon tracking +#endif + +DEFINE_LMIC; + +// Fwd decls. +static void reportEventNoUpdate(ev_t); +static void reportEventAndUpdate(ev_t); +static void engineUpdate(void); +static bit_t processJoinAccept_badframe(void); +static bit_t processJoinAccept_nojoinframe(void); + + +#if !defined(DISABLE_BEACONS) +static void startScan (void); +#endif + +// set the txrxFlags, with debugging +static inline void initTxrxFlags(const char *func, u1_t mask) { + LMIC_DEBUG2_PARAMETER(func); + +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask); +#endif + LMIC.txrxFlags = mask; +} + +// or the txrxFlags, with debugging +static inline void orTxrxFlags(const char *func, u1_t mask) { + initTxrxFlags(func, LMIC.txrxFlags | mask); +} + + + +// ================================================================================ +// BEG OS - default implementations for certain OS suport functions + +#if !defined(HAS_os_calls) + +#if !defined(os_rlsbf2) +u2_t os_rlsbf2 (xref2cu1_t buf) { + return (u2_t)((u2_t)buf[0] | ((u2_t)buf[1]<<8)); +} +#endif + +#if !defined(os_rlsbf4) +u4_t os_rlsbf4 (xref2cu1_t buf) { + return (u4_t)((u4_t)buf[0] | ((u4_t)buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); +} +#endif + + +#if !defined(os_rmsbf4) +u4_t os_rmsbf4 (xref2cu1_t buf) { + return (u4_t)((u4_t)buf[3] | ((u4_t)buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24)); +} +#endif + + +#if !defined(os_wlsbf2) +void os_wlsbf2 (xref2u1_t buf, u2_t v) { + buf[0] = v; + buf[1] = v>>8; +} +#endif + +#if !defined(os_wlsbf4) +void os_wlsbf4 (xref2u1_t buf, u4_t v) { + buf[0] = v; + buf[1] = v>>8; + buf[2] = v>>16; + buf[3] = v>>24; +} +#endif + +#if !defined(os_wmsbf4) +void os_wmsbf4 (xref2u1_t buf, u4_t v) { + buf[3] = v; + buf[2] = v>>8; + buf[1] = v>>16; + buf[0] = v>>24; +} +#endif + +#if !defined(os_getBattLevel) +u1_t os_getBattLevel (void) { + return MCMD_DEVS_BATT_NOINFO; +} +#endif + +#if !defined(os_crc16) +// New CRC-16 CCITT(XMODEM) checksum for beacons: +u2_t os_crc16 (xref2cu1_t data, uint len) { + u2_t remainder = 0; + u2_t polynomial = 0x1021; + for( uint i = 0; i < len; i++ ) { + remainder ^= data[i] << 8; + for( u1_t bit = 8; bit > 0; bit--) { + if( (remainder & 0x8000) ) + remainder = (remainder << 1) ^ polynomial; + else + remainder <<= 1; + } + } + return remainder; +} +#endif + +#endif // !HAS_os_calls + +// END OS - default implementations for certain OS suport functions +// ================================================================================ + +// ================================================================================ +// BEG AES + +static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) { + os_clearMem(AESaux,16); + AESaux[0] = 0x49; + AESaux[5] = dndir?1:0; + AESaux[15] = len; + os_wlsbf4(AESaux+ 6,devaddr); + os_wlsbf4(AESaux+10,seqno); +} + + +static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { + micB0(devaddr, seqno, dndir, len); + os_copyMem(AESkey,key,16); + return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len); +} + + +static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { + micB0(devaddr, seqno, dndir, len); + os_copyMem(AESkey,key,16); + // MSB because of internal structure of AES + os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len)); +} + + +static void aes_appendMic0 (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES +} + + +static int aes_verifyMic0 (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len); +} + + +static void aes_encrypt (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + os_aes(AES_ENC, pdu, len); +} + + +static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) { + if( len <= 0 ) + return; + os_clearMem(AESaux, 16); + AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1 + AESaux[5] = dndir?1:0; + os_wlsbf4(AESaux+ 6,devaddr); + os_wlsbf4(AESaux+10,seqno); + os_copyMem(AESkey,key,16); + os_aes(AES_CTR, payload, len); +} + + +static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) { + os_clearMem(nwkkey, 16); + nwkkey[0] = 0x01; + os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID); + os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce); + os_copyMem(artkey, nwkkey, 16); + artkey[0] = 0x02; + + os_getDevKey(AESkey); + os_aes(AES_ENC, nwkkey, 16); + os_getDevKey(AESkey); + os_aes(AES_ENC, artkey, 16); +} + +// END AES +// ================================================================================ + + +// ================================================================================ +// BEG LORA + +static CONST_TABLE(u1_t, SENSITIVITY)[7][3] = { + // ------------bw---------- + // 125kHz 250kHz 500kHz + { 141-109, 141-109, 141-109 }, // FSK + { 141-127, 141-124, 141-121 }, // SF7 + { 141-129, 141-126, 141-123 }, // SF8 + { 141-132, 141-129, 141-126 }, // SF9 + { 141-135, 141-132, 141-129 }, // SF10 + { 141-138, 141-135, 141-132 }, // SF11 + { 141-141, 141-138, 141-135 } // SF12 +}; + +int getSensitivity (rps_t rps) { + return -141 + TABLE_GET_U1_TWODIM(SENSITIVITY, getSf(rps), getBw(rps)); +} + +ostime_t calcAirTime (rps_t rps, u1_t plen) { + u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz + u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12 + if( sf == FSK ) { + return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8 + * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000; + } + u1_t sfx = 4*(sf+(7-SF7)); + u1_t q = sfx - (sf >= SF11 ? 8 : 0); + int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0); + if( tmp > 0 ) { + tmp = (tmp + q - 1) / q; + tmp *= getCr(rps)+5; + tmp += 8; + } else { + tmp = 8; + } + tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */; + // bw = 125000 = 15625 * 2^3 + // 250000 = 15625 * 2^4 + // 500000 = 15625 * 2^5 + // sf = 7..12 + // + // osticks = tmp * OSTICKS_PER_SEC * 1< counter reduced divisor 125000/8 => 15625 + // 2 => counter 2 shift on tmp + sfx = sf+(7-SF7) - (3+2) - bw; + int div = 15625; + if( sfx > 4 ) { + // prevent 32bit signed int overflow in last step + div >>= sfx-4; + sfx = 4; + } + // Need 32bit arithmetic for this last step + return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div; +} + +// END LORA +// ================================================================================ + + +// Table below defines the size of one symbol as +// symtime = 256us * 2^T(sf,bw) +// 256us is called one symunit. +// SF: +// BW: |__7___8___9__10__11__12 +// 125kHz | 2 3 4 5 6 7 +// 250kHz | 1 2 3 4 5 6 +// 500kHz | 0 1 2 3 4 5 +// + +static void setRxsyms (ostime_t rxsyms) { + if (rxsyms >= (((ostime_t)1) << 10u)) { + LMIC.rxsyms = (1u << 10u) - 1; + } else if (rxsyms < 0) { + LMIC.rxsyms = 0; + } else { + LMIC.rxsyms = rxsyms; + } +} + +#if !defined(DISABLE_BEACONS) +static ostime_t calcRxWindow (u1_t secs, dr_t dr) { + ostime_t rxoff, err; + if( secs==0 ) { + // aka 128 secs (next becaon) + rxoff = LMIC.drift; + err = LMIC.lastDriftDiff; + } else { + // scheduled RX window within secs into current beacon period + rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp; + err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp; + } + rxsyms_t rxsyms = LMICbandplan_MINRX_SYMS_LoRa_ClassB; + err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns; + setRxsyms(LMICbandplan_MINRX_SYMS_LoRa_ClassB + (err / dr2hsym(dr))); + + return (rxsyms-LMICbandplan_PAMBL_SYMS) * dr2hsym(dr) + rxoff; +} + + +// Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2)) +static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) { + if( ini ) { + LMIC.drift = 0; + LMIC.maxDriftDiff = 0; + LMIC.missedBcns = 0; + LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF; + } + ostime_t hsym = dr2hsym(DR_BCN); + LMIC.bcnRxsyms = LMICbandplan_MINRX_SYMS_LoRa_ClassB + ms2osticksCeil(ms) / hsym; + LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-LMICbandplan_PAMBL_SYMS) * hsym; +} +#endif // !DISABLE_BEACONS + + +#if !defined(DISABLE_PING) +// Setup scheduled RX window (ping/multicast slot) +static void rxschedInit (xref2rxsched_t rxsched) { + os_clearMem(AESkey,16); + os_clearMem(LMIC.frame+8,8); + os_wlsbf4(LMIC.frame, LMIC.bcninfo.time); + os_wlsbf4(LMIC.frame+4, LMIC.devaddr); + os_aes(AES_ENC,LMIC.frame,16); + u1_t intvExp = rxsched->intvExp; + ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units) + rxsched->rxbase = (LMIC.bcninfo.txtime + + BCN_RESERVE_osticks + + ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks + rxsched->slot = 0; + rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<dr); + rxsched->rxsyms = LMIC.rxsyms; +} + + +static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) { + again: + if( rxsched->rxtime - cando >= 0 ) + return 1; + u1_t slot; + if( (slot=rxsched->slot) >= 128 ) + return 0; + u1_t intv = 1<intvExp; + if( (rxsched->slot = (slot += (intv))) >= 128 ) + return 0; + rxsched->rxtime = rxsched->rxbase + + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp) + - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr); + rxsched->rxsyms = LMIC.rxsyms; + goto again; +} +#endif // !DISABLE_PING) + + +ostime_t LMICcore_rndDelay (u1_t secSpan) { + u2_t r = os_getRndU2(); + ostime_t delay = r; + if( delay > OSTICKS_PER_SEC ) + delay = r % (u2_t)OSTICKS_PER_SEC; + if( secSpan > 0 ) + delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC; + return delay; +} + +// delay reftime ticks, plus a random interval in [0..secSpan). +static void txDelay (ostime_t reftime, u1_t secSpan) { + if (secSpan != 0) + reftime += LMICcore_rndDelay(secSpan); + if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) { + LMIC.globalDutyAvail = reftime; + LMIC.opmode |= OP_RNDTX; + } +} + + +void LMICcore_setDrJoin (u1_t reason, u1_t dr) { + LMIC_EV_PARAMETER(reason); + + EV(drChange, INFO, (e_.reason = reason, + e_.deveui = MAIN::CDEV->getEui(), + e_.dr = dr|DR_PAGE, + e_.txpow = LMIC.adrTxPow, + e_.prevdr = LMIC.datarate|DR_PAGE, + e_.prevtxpow = LMIC.adrTxPow)); + LMIC.datarate = dr; + DO_DEVDB(LMIC.datarate,datarate); +} + + +static bit_t setDrTxpow (u1_t reason, u1_t dr, s1_t pow) { + bit_t result = 0; + + LMIC_EV_PARAMETER(reason); + + EV(drChange, INFO, (e_.reason = reason, + e_.deveui = MAIN::CDEV->getEui(), + e_.dr = dr|DR_PAGE, + e_.txpow = pow, + e_.prevdr = LMIC.datarate|DR_PAGE, + e_.prevtxpow = LMIC.adrTxPow)); + + if( pow != KEEP_TXPOW && pow != LMIC.adrTxPow ) { + LMIC.adrTxPow = pow; + result = 1; + } + if( LMIC.datarate != dr ) { + LMIC.datarate = dr; + DO_DEVDB(LMIC.datarate,datarate); + LMIC.opmode |= OP_NEXTCHNL; + result = 1; + } + return result; +} + + +#if !defined(DISABLE_PING) +void LMIC_stopPingable (void) { + LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI); +} + + +void LMIC_setPingable (u1_t intvExp) { + // Change setting + LMIC.ping.intvExp = (intvExp & 0x7); + LMIC.opmode |= OP_PINGABLE; + // App may call LMIC_enableTracking() explicitely before + // Otherwise tracking is implicitly enabled here + if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 ) + LMIC_enableTracking(0); +} + +#endif // !DISABLE_PING + +static void runEngineUpdate (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + engineUpdate(); +} + +static void reportEventAndUpdate(ev_t ev) { + reportEventNoUpdate(ev); + engineUpdate(); +} + +static void reportEventNoUpdate (ev_t ev) { + uint32_t const evSet = UINT32_C(1) << ev; + EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, + e_.eui = MAIN::CDEV->getEui(), + e_.info = ev)); +#if LMIC_ENABLE_onEvent + void (*pOnEvent)(ev_t) = onEvent; + + // rxstart is critical timing; legacy onEvent handlers + // don't comprehend this; so don't report. + if (pOnEvent != NULL && (evSet & (UINT32_C(1)<> 8; // read as signed 24-bit + LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto + LMIC.bcninfo.info = d[OFF_BCN_INFO]; + LMIC.bcninfo.flags |= BCN_FULL; + return LMIC_BEACON_ERROR_SUCCESS_FULL; +} +#endif // !DISABLE_BEACONS + +// put a mac response to the current output buffer. Limit according to kind of +// mac data (piggyback vs port 0) +static bit_t put_mac_uplink_byte(uint8_t b) { + if (LMIC.pendMacPiggyback) { + // put in pendMacData + if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { + LMIC.pendMacData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } else { + // put in pendTxData + if (LMIC.pendMacLen < sizeof(LMIC.pendTxData)) { + LMIC.pendTxData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } +} + +static bit_t put_mac_uplink_byte2(uint8_t b1, uint8_t b2) { + u1_t outindex = LMIC.pendMacLen; + + if (put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2)) { + return 1; + } else { + LMIC.pendMacLen = outindex; + return 0; + } +} + +static bit_t put_mac_uplink_byte3(u1_t b1, u1_t b2, u1_t b3) { + u1_t outindex = LMIC.pendMacLen; + + if (put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2) && put_mac_uplink_byte(b3)) { + return 1; + } else { + LMIC.pendMacLen = outindex; + return 0; + } +} + +static CONST_TABLE(u1_t, macCmdSize)[] = { + /* 2: LinkCheckAns */ 3, + /* 3: LinkADRReq */ 5, + /* 4: DutyCycleReq */ 2, + /* 5: RXParamSetupReq */ 5, + /* 6: DevStatusReq */ 1, + /* 7: NewChannelReq */ 6, + /* 8: RXTimingSetupReq */ 2, + /* 9: TxParamSetupReq */ 2, + /* 0x0A: DlChannelReq */ 5, + /* B, C: RFU */ 0, 0, + /* 0x0D: DeviceTimeAns */ 6, + /* 0x0E, 0x0F */ 0, 0, + /* 0x10: PingSlotInfoAns */ 1, + /* 0x11: PingSlotChannelReq */ 5, + /* 0x12: BeaconTimingAns */ 4, + /* 0x13: BeaconFreqReq */ 4 +}; + +static u1_t getMacCmdSize(u1_t macCmd) { + if (macCmd >= 2) { + const unsigned macCmdMinus2 = macCmd - 2u; + if (macCmdMinus2 < LENOF_TABLE(macCmdSize)) { + // macCmd in table, fetch it's size. + return TABLE_GET_U1(macCmdSize, macCmdMinus2); + } + } + // macCmd too small or too large: return zero. Zero is + // never a legal command size, so it signals an error + // to the caller. + return 0; +} + +static bit_t +applyAdrRequests( + const uint8_t *opts, + int olen, + u1_t adrAns +) { + lmic_saved_adr_state_t initialState; + int const kAdrReqSize = 5; + int oidx; + u1_t p1 = 0; + u1_t p4 = 0; + bit_t response_fit = 1; + bit_t map_ok = 1; + + LMICbandplan_saveAdrState(&initialState); + + // compute the changes + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + for (oidx = 0; oidx < olen; oidx += kAdrReqSize) { + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; + } + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels + + p1 = opts[oidx+1]; // txpow + DR, in case last + p4 = opts[oidx+4]; // ChMaskCtl, NbTrans + u1_t chpage = p4 & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page + + map_ok = LMICbandplan_mapChannels(chpage, chmap); + LMICOS_logEventUint32("applyAdrRequests: mapChannels", ((u4_t)chpage << 16)|(chmap << 0)); + } + } + + if (! map_ok) { + adrAns &= ~MCMD_LinkADRAns_ChannelACK; + } + + // p1 now has txpow + DR. DR must be feasible. + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); + + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) && ! LMICbandplan_isDataRateFeasible(dr)) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + LMICOS_logEventUint32("applyAdrRequests: final DR not feasible", dr); + } + + if (adrAns != (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + LMICbandplan_restoreAdrState(&initialState); + } + + // now put all the options + for (oidx = 0; oidx < olen && response_fit; oidx += kAdrReqSize) { + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; + } + response_fit = put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); + } + + // all done scanning options + bit_t changes = LMICbandplan_compareAdrState(&initialState); + + // handle the final options + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + // handle uplink repeat count + u1_t uprpt = p4 & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count + if (LMIC.upRepeat != uprpt) { + LMIC.upRepeat = uprpt; + changes = 1; + } + + LMICOS_logEventUint32("applyAdrRequests: setDrTxPow", ((u4_t)adrAns << 16)|(dr << 8)|(p1 << 0)); + + // handle power changes here, too. + changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + } + + // Certification doesn't like this, but it makes the device happier with TTN. + // LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + + return response_fit; +} + +static int +scan_mac_cmds_link_adr( + const uint8_t *opts, + int olen, + bit_t *presponse_fit + ) + { + LMICOS_logEventUint32("scan_mac_cmds_link_adr", olen); + + if (olen == 0) + return 0; + + int oidx = 0; + int const kAdrReqSize = 5; + int lastOidx; + u1_t adrAns = MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK; + + // process the contiguous slots + for (;;) { + lastOidx = oidx; + + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end; but fail it. + adrAns = 0; + break; + } + u1_t p1 = opts[oidx+1]; // txpow + DR + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels + u1_t chpage = opts[oidx+4] & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page + // u1_t uprpt = opts[oidx+4] & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); + + if( !LMICbandplan_canMapChannels(chpage, chmap) ) { + adrAns &= ~MCMD_LinkADRAns_ChannelACK; + LMICOS_logEventUint32("scan_mac_cmds_link_adr: failed canMapChannels", ((u4_t)chpage << 16)|((u4_t)chmap << 0)); + } + + if( !validDR(dr) ) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + } + if (pow2dBm(p1) == -128) { + adrAns &= ~MCMD_LinkADRAns_PowerACK; + } + + oidx += kAdrReqSize; + if (opts[oidx] != MCMD_LinkADRReq) + break; + } + + // go back and apply the ADR changes, if any -- use the effective length, + // and process. + *presponse_fit = applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); + + return lastOidx; + } + +// scan mac commands starting at opts[] for olen, return count of bytes consumed. +// build response in pendMacData[], but limit length as needed; simply chop at last +// response that fits. +static int +scan_mac_cmds( + const uint8_t *opts, + int olen, + int port + ) { + int oidx = 0; + uint8_t cmd; + + LMIC.pendMacLen = 0; + if (port == 0) { + // port zero: mac data is in the normal payload, and there can't be + // piggyback mac data. + LMIC.pendMacPiggyback = 0; + } else { + // port is either -1 (no port) or non-zero (piggyback): treat as piggyback. + LMIC.pendMacPiggyback = 1; + } + + while( oidx < olen ) { + bit_t response_fit; + + response_fit = 1; + cmd = opts[oidx]; + + /* compute length, and exit for illegal commands */ + // cmdlen == 0 for error, or > 0 length of command. + int const cmdlen = getMacCmdSize(cmd); + if (cmdlen <= 0 || cmdlen > olen - oidx) { + // "the first unknown command terminates processing" + olen = oidx; + break; + } + + switch( cmd ) { + case MCMD_LinkCheckAns: { + // TODO(tmm@mcci.com) capture these, reliably.. + //int gwmargin = opts[oidx+1]; + //int ngws = opts[oidx+2]; + break; + } + // from 1.0.3 spec section 5.2: + // For the purpose of configuring the end-device channel mask, the end-device will + // process all contiguous LinkAdrReq messages, in the order present in the downlink message, + // as a single atomic block command. The end-device will accept or reject all Channel Mask + // controls in the contiguous block, and provide consistent Channel Mask ACK status + // indications for each command in the contiguous block in each LinkAdrAns message, + // reflecting the acceptance or rejection of this atomic channel mask setting. + // + // So we need to process all the contigious commands + case MCMD_LinkADRReq: { + // skip over all but the last command. + oidx += scan_mac_cmds_link_adr(opts + oidx, olen - oidx, &response_fit); + break; + } + + case MCMD_DevStatusReq: { + // LMIC.snr is SNR times 4, convert to real SNR; rounding towards zero. + const int snr = (LMIC.snr + 2) / 4; + // per [1.02] 5.5. the margin is the SNR. + LMIC.devAnsMargin = (u1_t)(0b00111111 & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr)); + + response_fit = put_mac_uplink_byte3(MCMD_DevStatusAns, os_getBattLevel(), LMIC.devAnsMargin); + break; + } + +#if !defined(DISABLE_MCMD_RXParamSetupReq) + case MCMD_RXParamSetupReq: { + dr_t dr = (dr_t)(opts[oidx+1] & 0x0F); + u1_t rx1DrOffset = (u1_t)((opts[oidx+1] & 0x70) >> 4); + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); + LMIC.dn2Ans = 0xC0; // answer pending, but send this one in order. + if( validDR(dr) ) + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX2DataRateACK; + if( freq != 0 ) + LMIC.dn2Ans |= MCMD_RXParamSetupAns_ChannelACK; + if (rx1DrOffset <= 3) + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX1DrOffsetAck; + + if( LMIC.dn2Ans == (0xC0|MCMD_RXParamSetupAns_RX2DataRateACK|MCMD_RXParamSetupAns_ChannelACK| MCMD_RXParamSetupAns_RX1DrOffsetAck) ) { + LMIC.dn2Dr = dr; + LMIC.dn2Freq = freq; + LMIC.rx1DrOffset = rx1DrOffset; + DO_DEVDB(LMIC.dn2Dr,dn2Dr); + DO_DEVDB(LMIC.dn2Freq,dn2Freq); + } + + /* put the first copy of the message */ + response_fit = put_mac_uplink_byte2(MCMD_RXParamSetupAns, LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU); + break; + } +#endif // !DISABLE_MCMD_RXParamSetupReq + +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + case MCMD_RXTimingSetupReq: { + u1_t delay = opts[oidx+1] & MCMD_RXTimingSetupReq_Delay; + if (delay == 0) + delay = 1; + + LMIC.rxDelay = delay; + LMIC.macRxTimingSetupAns = 2; + response_fit = put_mac_uplink_byte(MCMD_RXTimingSetupAns); + break; + } +#endif // !DISABLE_MCMD_RXTimingSetupReq + +#if !defined(DISABLE_MCMD_DutyCycleReq) + case MCMD_DutyCycleReq: { + u1_t cap = opts[oidx+1]; + LMIC.globalDutyRate = cap & 0xF; + LMIC.globalDutyAvail = os_getTime(); + DO_DEVDB(cap,dutyCap); + + response_fit = put_mac_uplink_byte(MCMD_DutyCycleAns); + break; + } +#endif // !DISABLE_MCMD_DutyCycleReq + +#if !defined(DISABLE_MCMD_NewChannelReq) && CFG_LMIC_EU_like + case MCMD_NewChannelReq: { + u1_t chidx = opts[oidx+1]; // channel + u4_t raw_f_not_zero = opts[oidx+2] | opts[oidx+3] | opts[oidx+4]; + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq + u1_t drs = opts[oidx+5]; // datarate span + u1_t ans = MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK; + + if (freq == 0 && raw_f_not_zero) { + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + u1_t MaxDR = drs >> 4; + u1_t MinDR = drs & 0xF; + if (MaxDR < MinDR || !validDR(MaxDR) || !validDR(MinDR)) { + ans &= ~MCMD_NewChannelAns_DataRateACK; + } + + if( ans == (MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK)) { + if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { + LMICOS_logEventUint32("NewChannelReq: setupChannel failed", ((u4_t)MaxDR << 24u) | ((u4_t)MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + } + + response_fit = put_mac_uplink_byte2(MCMD_NewChannelAns, ans); + break; + } +#endif // !DISABLE_MCMD_NewChannelReq + +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + case MCMD_DlChannelReq: { + u1_t chidx = opts[oidx+1]; // channel + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq + u1_t ans = MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK; + + if (freq == 0) { + ans &= ~MCMD_DlChannelAns_ChannelACK; + } + if (chidx > MAX_CHANNELS) { + // this is not defined by the 1.0.3 spec + ans = 0; + } else if ((LMIC.channelMap & (1 << chidx)) == 0) { + // the channel is not enabled for downlink. + ans &= ~MCMD_DlChannelAns_FreqACK; + } + + if( ans == (MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK)) { + LMIC.channelDlFreq[chidx] = freq; + } + + response_fit = put_mac_uplink_byte2(MCMD_DlChannelAns, ans); + // set sticky answer. + LMIC.macDlChannelAns = ans | 0xC0; + break; + } +#endif // !DISABLE_MCMD_DlChannelReq + +#if !defined(DISABLE_MCMD_PingSlotChannelReq) && !defined(DISABLE_PING) + case MCMD_PingSlotChannelReq: { + u4_t raw_f_not_zero = opts[oidx+1] | opts[oidx+2] | opts[oidx+3]; + u4_t freq = LMICbandplan_convFreq(&opts[oidx+1]); + u1_t dr = opts[oidx+4] & 0xF; + u1_t ans = MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK; + if (! raw_f_not_zero) { + freq = FREQ_PING; + } else if (freq == 0) { + ans &= ~MCMD_PingSlotFreqAns_ChannelACK; + } + if (! validDR(dr)) + ans &= ~MCMD_PingSlotFreqAns_DataRateACK; + + if (ans == (MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK)) { + LMIC.ping.freq = freq; + LMIC.ping.dr = dr; + DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); + DO_DEVDB(LMIC.ping.freq, pingFreq); + DO_DEVDB(LMIC.ping.dr, pingDr); + } + response_fit = put_mac_uplink_byte2(MCMD_PingSlotChannelAns, ans); + break; + } +#endif // !DISABLE_MCMD_PingSlotChannelReq && !DISABLE_PING + +#if defined(ENABLE_MCMD_BeaconTimingAns) && !defined(DISABLE_BEACONS) + case MCMD_BeaconTimingAns: { + // Ignore if tracking already enabled or bcninfoTries == 0 + if( (LMIC.opmode & OP_TRACK) == 0 && LMIC.bcninfoTries != 0) { + LMIC.bcnChnl = opts[oidx+3]; + // Enable tracking - bcninfoTries + LMIC.opmode |= OP_TRACK; + // LMIC.bcninfoTries is cleared later in txComplete handling - triggers EV_BEACON_FOUND + // Setup RX parameters + LMIC.bcninfo.txtime = (LMIC.rxtime + + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BeaconTimingAns_TUNIT) + + ms2osticksCeil(MCMD_BeaconTimingAns_TUNIT/2) + - BCN_INTV_osticks); + LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared) + calcBcnRxWindowFromMillis(MCMD_BeaconTimingAns_TUNIT,1); // error of +/-N ms + + EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BeaconTimingAns, + e_.eui = MAIN::CDEV->getEui(), + e_.lostmic = Base::lsbf4(&d[pend]), + e_.info = (LMIC.missedBcns | + (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks + - LMIC.bcnRxtime) << 8)), + e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks))); + } + break; + } /* end case */ +#endif // !ENABLE_MCMD_BeaconTimingAns && !DISABLE_BEACONS + +#if LMIC_ENABLE_TxParamSetupReq + case MCMD_TxParamSetupReq: { + uint8_t txParam; + txParam = opts[oidx+1]; + + // we don't allow unrecognized bits to get to txParam. + txParam &= (MCMD_TxParam_RxDWELL_MASK| + MCMD_TxParam_TxDWELL_MASK| + MCMD_TxParam_MaxEIRP_MASK); + LMIC.txParam = txParam; + response_fit = put_mac_uplink_byte(MCMD_TxParamSetupAns); + break; + } /* end case */ +#endif // LMIC_ENABLE_TxParamSetupReq + +#if LMIC_ENABLE_DeviceTimeReq + case MCMD_DeviceTimeAns: { + // don't process a spurious downlink. + if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_rx ) { + // remember that it's time to notify the client. + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_success; + + // the network time is linked to the time of the last TX. + LMIC.localDeviceTime = LMIC.txend; + + // save the network time. + // The first 4 bytes contain the seconds since the GPS epoch + // (i.e January the 6th 1980 at 00:00:00 UTC). + // Note: as per the LoRaWAN specs, the octet order for all + // multi-octet fields is little endian + // Note: the casts are necessary, because opts is an array of + // single byte values, and they might overflow when shifted + LMIC.netDeviceTime = ( (lmic_gpstime_t) opts[oidx + 1] ) | + (((lmic_gpstime_t) opts[oidx + 2]) << 8) | + (((lmic_gpstime_t) opts[oidx + 3]) << 16) | + (((lmic_gpstime_t) opts[oidx + 4]) << 24); + + // The 5th byte contains the fractional seconds in 2^-8 second steps + LMIC.netDeviceTimeFrac = opts[oidx + 5]; +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": MAC command DeviceTimeAns received: seconds_since_gps_epoch=%"PRIu32", fractional_seconds=%d\n", os_getTime(), LMIC.netDeviceTime, LMIC.netDeviceTimeFrac); +#endif + } + break; + } /* end case */ +#endif // LMIC_ENABLE_DeviceTimeReq + + default: { + // force olen to current oidx so we'll exit the while() + olen = oidx; + break; + } /* end case */ + } /* end switch */ + + /* if we're out of spce for responses, skip to end. */ + if (! response_fit) { + olen = oidx; + } else { + oidx += cmdlen; + } + } /* end while */ + + return oidx; +} + +// change the ADR ack request count, unless adr ack is diabled. +static void setAdrAckCount (s2_t count) { + if (LMIC.adrAckReq != LINK_CHECK_OFF) { + LMIC.adrAckReq = count; + } +} + +static bit_t decodeFrame (void) { + xref2u1_t d = LMIC.frame; + u1_t hdr = d[0]; + u1_t ftype = hdr & HDR_FTYPE; + int dlen = LMIC.dataLen; +#if LMIC_DEBUG_LEVEL > 0 + const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); +#endif + if (dlen > 0) + LMICOS_logEventUint32("decodeFrame", (dlen << 8) | (hdr << 0)); + + if( dlen < OFF_DAT_OPTS+4 || + (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || + (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) { + // Basic sanity checks failed + EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]), + e_.info2 = hdr + (dlen<<8))); + norx: +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Invalid downlink, window=%s\n", os_getTime(), window); +#endif + LMIC.dataLen = 0; + return 0; + } + // Validate exact frame length + // Note: device address was already read+evaluated in order to arrive here. + int fct = d[OFF_DAT_FCT]; + u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]); + u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]); + int olen = fct & FCT_OPTLEN; + int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame + int poff = OFF_DAT_OPTS+olen; + int pend = dlen-4; // MIC + + if( addr != LMIC.devaddr ) { + LMICOS_logEventUint32("decodeFrame: wrong address", addr); + + EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, + e_.eui = MAIN::CDEV->getEui(), + e_.info = addr, + e_.info2 = LMIC.devaddr)); + goto norx; + } + if( poff > pend ) { + LMICOS_logEventUint32("decodeFrame: corrupted frame", ((u4_t)dlen << 16) | (fct << 8) | (poff - pend)); + EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16))); + goto norx; + } + + int port = -1; + int replayConf = 0; + + if( pend > poff ) + port = d[poff++]; + + // compute the 32-bit sequence number based on the 16-bit sequence number received + // and the internal 32-bit number. Because the 32-bit number is used in the MIC + // calculation, this must be right. (And if you're curious why a 32-bit seqno matters, + // it's this calculation, plus its use in the MIC calculation.) + // + // we have to be careful to get the right value for replay of last message received. + u2_t seqnoDiff = (u2_t)(seqno - LMIC.seqnoDn); + if (seqnoDiff == 0xFFFFu) { + seqno = LMIC.seqnoDn - 1; + } else { + seqno = LMIC.seqnoDn + seqnoDiff; + } + + if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) { + LMICOS_logEventUint32("decodeFrame: bad MIC", os_rlsbf4(&d[pend])); + EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC, + e_.eui1 = MAIN::CDEV->getEui(), + e_.info1 = Base::lsbf4(&d[pend]), + e_.info2 = seqno, + e_.info3 = LMIC.devaddr)); + goto norx; + } + if( seqno < LMIC.seqnoDn ) { + if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + LMICOS_logEventUint32("decodeFrame: rollover discarded", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; + } + if( seqno != LMIC.seqnoDn-1 || !LMIC.lastDnConf || ftype != HDR_FTYPE_DCDN ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + LMICOS_logEventUint32("decodeFrame: Retransmit confimed discarded", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; + } + // Replay of previous sequence number allowed only if + // previous frame and repeated both requested confirmation + // but set a flag, so we don't actually process the message. + LMICOS_logEventUint32("decodeFrame: Retransmit confimed accepted", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + replayConf = 1; + LMIC.dnConf = FCT_ACK; + } + else { + if( seqnoDiff > LMICbandplan_MAX_FCNT_GAP) { + LMICOS_logEventUint32("decodeFrame: gap too big", ((u4_t)seqnoDiff << 16) | (seqno & 0xFFFFu)); + goto norx; + } + if( seqno > LMIC.seqnoDn ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + } + LMIC.seqnoDn = seqno+1; // next number to be expected + DO_DEVDB(LMIC.seqnoDn,seqnoDn); + // DN frame requested confirmation - provide ACK once with next UP frame + LMIC.dnConf = LMIC.lastDnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0); + if (LMIC.dnConf) + LMICOS_logEventUint32("decodeFrame: Confirmed downlink", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + } + + if (port == 0 && olen != 0 && pend > poff) { + // we have a port-zero message, and piggyback mac data. + // discard, section 4.3.1.6 line 544-546 +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": port==0 && FOptsLen=%#x: discard\n", os_getTime(), olen); +#endif + goto norx; + } + + if( LMIC.dnConf || (fct & FCT_MORE) ) + LMIC.opmode |= OP_POLL; + + // We heard from network + LMIC.adrChanged = LMIC.rejoinCnt = 0; + setAdrAckCount(LINK_CHECK_INIT); +#if !defined(DISABLE_MCMD_RXParamSetupReq) + // We heard from network "on a Class A downlink" + LMIC.dn2Ans = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + // We heard from network "on a Class A downlink" + LMIC.macRxTimingSetupAns = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + LMIC.macDlChannelAns = 0; +#endif + + int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps); + // for legacy reasons, LMIC.margin is set to the unsigned sensitivity. It can never be negative. + // it's only computed for legacy clients + LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m; + + // even if it's a replay confirmed, we process the mac options. + xref2u1_t opts = &d[OFF_DAT_OPTS]; + int oidx = scan_mac_cmds(opts, olen, port); + if( oidx != olen ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = 0x1000000 + (oidx) + (olen<<8))); + oidx = olen; + } + + if( !replayConf ) { + // Handle payload only if not a replay + // Decrypt payload - if any + if( port >= 0 && pend-poff > 0 ) { + aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff); + if (port == 0) { + // this is a mac command. scan the options. +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff); +#endif + int optendindex = scan_mac_cmds(d+poff, pend-poff, port); + if (optendindex != pend-poff) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF( + "%"LMIC_PRId_ostime_t": error processing mac commands for port 0 " + "(len=%#x, optendindex=%#x)\n", + os_getTime(), pend-poff, optendindex + ); +#endif + } + // wait to transmit until txcomplete: above. + } + } // end decrypt payload + EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.seqno = seqno, + e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN, + e_.mic = Base::lsbf4(&d[pend]), + e_.hdr = d[LORA::OFF_DAT_HDR], + e_.fct = d[LORA::OFF_DAT_FCT], + e_.port = port, + e_.plen = dlen, + e_.opts.length = olen, + memcpy(&e_.opts[0], opts, olen))); + } else { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY, + e_.eui = MAIN::CDEV->getEui(), + e_.info = Base::lsbf4(&d[pend]), + e_.info2 = seqno)); + // discard the data + LMICOS_logEventUint32("decodeFrame: discarding replay", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; + } + + if( // NWK acks but we don't have a frame pending + (ackup && LMIC.txCnt == 0) || + // We sent up confirmed and we got a response in DNW1/DNW2 + // BUT it did not carry an ACK - this should never happen + // Do not resend and assume frame was not ACKed. + (!ackup && LMIC.txCnt != 0) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK, + e_.eui = MAIN::CDEV->getEui(), + e_.info = seqno, + e_.info2 = ackup)); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt); +#endif + } + + if( LMIC.txCnt != 0 ) // we requested an ACK + orTxrxFlags(__func__, ackup ? TXRX_ACK : TXRX_NACK); + + if( port <= 0 ) { + orTxrxFlags(__func__, TXRX_NOPORT); + LMIC.dataBeg = poff; + LMIC.dataLen = 0; + } else { + orTxrxFlags(__func__, TXRX_PORT); + LMIC.dataBeg = poff; + LMIC.dataLen = pend-poff; + } +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags); +#endif + return 1; +} + + +// ================================================================================ +// TX/RX transaction support + +// start reception and log. +static void radioRx (void) { + reportEventNoUpdate(EV_RXSTART); + os_radio(RADIO_RX); +} + +// start RX in window 2. +static void setupRx2 (void) { + initTxrxFlags(__func__, TXRX_DNW2); + LMIC.rps = dndr2rps(LMIC.dn2Dr); + LMIC.freq = LMIC.dn2Freq; + LMIC.dataLen = 0; + radioRx(); +} + +//! \brief Adjust the delay (in ticks) of the target window-open time from nominal. +//! \param hsym the duration of one-half symbol in osticks. +//! \param rxsyms_in the nominal window length -- minimum length of time to delay. +//! \return Effective delay to use (positive for later, negative for earlier). +//! \post LMIC.rxsyms is set to the number of rxsymbols to be used for preamble timeout. +//! \bug For FSK, the radio driver ignores LMIC.rxsysms, and uses a fixed value of 4080 bits +//! (81 ms) +//! +//! \details The calculation of the RX Window opening time has to balance several things. +//! The system clock might be inaccurate. Generally, the LMIC assumes that the timebase +//! is accurage to 100 ppm, or 0.01%. 0.01% of a 6 second window is 600 microseconds. +//! For LoRa, the fastest data rates of interest is SF7 (1024 us/symbol); with an 8-byte +//! preamble, the shortest preamble is 8.092ms long. If using FSK, the symbol rate is +//! 20 microseconds, but the preamble is 8*5 bits long, so the preamble is 800 microseconds. +//! Unless LMIC_ENABLE_arbitrary_clock_error is true, we fold clock errors of > 0.4% back +//! to 0.4%. +ostime_t LMICcore_adjustForDrift (ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in) { + ostime_t rxoffset; + + // decide if we want to move left or right of the reference time. + rxoffset = -LMICbandplan_RX_EXTRA_MARGIN_osticks; + + u2_t clockerr = LMIC.client.clockError; + + // Most crystal oscillators are 100 ppm. If things are that tight, there's + // no point in specifying a drift, as 6 seconds at 100ppm is +/- 600 microseconds. + // We position the windows at the front, and there's some extra margin, so... + // don't bother setting values <= 100 ppm. + if (clockerr != 0) + { + // client has set clock error. Limit this to 0.1% unless there's + // a compile-time configuration. (In other words, assume that millis() + // clock is accurate to 0.1%.) You should never use clockerror to + // compensate for system-late problems. + u2_t const maxError = LMIC_kMaxClockError_ppm * MAX_CLOCK_ERROR / (1000 * 1000); + if (! LMIC_ENABLE_arbitrary_clock_error && clockerr > maxError) + { + clockerr = maxError; + } + } + + // If the clock is slow, the window needs to open earlier in our time + // in order to open at or before the specified time (in real world),. + // Don't bother to round, as this is very fine-grained. + ostime_t drift = (ostime_t)(((int64_t)delay * clockerr) / MAX_CLOCK_ERROR); + + // calculate the additional rxsyms needed to hit the window nominally. + ostime_t const tsym = 2 * hsym; + ostime_t driftwin; + driftwin = 2 * drift; + if (rxoffset < 0) + driftwin += -rxoffset; + // else we'll hit the window nominally. + + rxsyms_in += (driftwin + tsym - 1) / tsym; + + // reduce the rxoffset by the drift; this compensates for a slow clock; + // but it makes the rxtime too early by approximately `drift` if clock + // is fast. + rxoffset -= drift; + + setRxsyms(rxsyms_in); + + return delay + rxoffset; +} + +static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) { + ostime_t hsym = dr2hsym(dr); + + // Schedule the start of the receive window. os_getRadioRxRampup() is used to make sure we + // exit "sleep" well enough in advance of the receive window to be able to + // time things accurately. + // + // This also sets LMIC.rxsyms. This is NOT normally used for FSK; see LMICbandplan_txDoneFSK() + LMIC.rxtime = LMIC.txend + LMICcore_adjustForDrift(delay, hsym, LMICbandplan_MINRX_SYMS_LoRa_ClassA); + + LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": sched Rx12 %"LMIC_PRId_ostime_t"\n", os_getTime(), LMIC.rxtime - os_getRadioRxRampup()); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - os_getRadioRxRampup(), func); +} + +static void setupRx1 (osjobcb_t func) { + initTxrxFlags(__func__, TXRX_DNW1); + // Turn LMIC.rps from TX over to RX + LMIC.rps = setNocrc(LMIC.rps,1); + LMIC.dataLen = 0; + LMIC.osjob.func = func; + radioRx(); +} + + +// Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime +static void txDone (ostime_t delay, osjobcb_t func) { +#if !defined(DISABLE_PING) + if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) { + rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! + LMIC.opmode |= OP_PINGINI; + } +#endif // !DISABLE_PING + + // Change RX frequency (can happen even for EU-like if programmed by DlChannelReq) + // change params and rps (US only) before we increment txChnl + LMICbandplan_setRx1Params(); + + // LMIC.dndr carries the TX datarate (can be != LMIC.datarate [confirm retries etc.]) + // Setup receive -- either schedule FSK or schedule rx1 or rx2 window. + if( LMICbandplan_isFSK() ) { + LMICbandplan_txDoneFSK(delay, func); + } + else + { + schedRx12(delay, func, LMIC.dndr); + } +} + +// ======================================== Join frames + + +#if !defined(DISABLE_JOIN) +static void onJoinFailed (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + // Notify app - must call LMIC_reset() to stop joining + // otherwise join procedure continues. + reportEventAndUpdate(EV_JOIN_FAILED); +} + +// process join-accept message or deal with no join-accept in slot 2. +static bit_t processJoinAccept (void) { + if ((LMIC.txrxFlags & TXRX_DNW1) != 0 && LMIC.dataLen == 0) + return 0; + + // formerly we asserted. + if ((LMIC.opmode & OP_TXRXPEND) == 0) + // nothing we can do. + return 1; + + // formerly we asserted. + if ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) == 0) { + // we shouldn't be here. just drop the frame, but clean up txrxpend. + return processJoinAccept_badframe(); + } + + if( LMIC.dataLen == 0 ) { + // we didn't get any data and we're in slot 2. So... there's no join frame. + return processJoinAccept_nojoinframe(); + } + + u1_t hdr = LMIC.frame[0]; + u1_t dlen = LMIC.dataLen; + u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt! + LMIC_EV_VARIABLE(mic); // only used by EV(). + + if( (dlen != LEN_JA && dlen != LEN_JAEXT) + || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = dlen < 4 ? 0 : mic, + e_.info2 = hdr + (dlen<<8))); + return processJoinAccept_badframe(); + } + aes_encrypt(LMIC.frame+1, dlen-1); + if( !aes_verifyMic0(LMIC.frame, dlen-4) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC, + e_.info = mic)); + return processJoinAccept_badframe(); + } + + u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR); + LMIC.devaddr = addr; + LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF; + + // initDefaultChannels(0) for EU-like, nothing otherwise + LMICbandplan_joinAcceptChannelClear(); + + if (!LMICbandplan_hasJoinCFlist() && dlen > LEN_JA) { + // if no JoinCFList, we're supposed to continue + // the join per 2.2.5 of LoRaWAN regional 2.2.4 + // https://github.com/mcci-catena/arduino-lmic/issues/19 + } else if ( LMICbandplan_hasJoinCFlist() && dlen > LEN_JA ) { + dlen = OFF_CFLIST; + for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) { + u4_t freq = LMICbandplan_convFreq(&LMIC.frame[dlen]); + if( freq ) { + LMIC_setupChannel(chidx, freq, 0, -1); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel, idx=%d, freq=%"PRIu32"\n", os_getTime(), chidx, freq); +#endif + } + } + } + + // already incremented when JOIN REQ got sent off + aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey); + DO_DEVDB(LMIC.netid, netid); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.nwkKey, nwkkey); + DO_DEVDB(LMIC.artKey, artkey); + + EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(), + e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.oldaddr = oldaddr, + e_.nonce = LMIC.devNonce-1, + e_.mic = mic, + e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 + ? EV::joininfo_t::REJOIN_ACCEPT + : EV::joininfo_t::ACCEPT))); + + // + // XXX(tmm@mcci.com) OP_REJOIN confuses me, and I'm not sure why we're + // adjusting DRs here. We've just received a join accept, and the + // datarate therefore shouldn't be in play. In effect, we set the + // initial data rate based on the number of times we tried to rejoin. + // + if( (LMIC.opmode & OP_REJOIN) != 0 ) { +#if CFG_region != LMIC_REGION_as923 + // TODO(tmm@mcci.com) regionalize + // Lower DR every try below current UP DR + // need to check feasibility? join feasability is default. + LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt); +#else + // in the join of AS923 v1.1 or older, only DR2 (SF10) is used. + // TODO(tmm@mcci.com) if the rejoin logic is at all correct, we + // should be setting the uplink datarate based on the number of + // tries; this doesn't set the AS923 join data rate. + LMIC.datarate = AS923_DR_SF10; +#endif + } + LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI); + LMIC.opmode |= OP_NEXTCHNL; + LMIC.txCnt = 0; + stateJustJoined(); + // transition to the ADR_ACK initial state. + setAdrAckCount(LINK_CHECK_INIT); + + LMIC.dn2Dr = LMIC.frame[OFF_JA_DLSET] & 0x0F; + LMIC.rx1DrOffset = (LMIC.frame[OFF_JA_DLSET] >> 4) & 0x7; + LMIC.rxDelay = LMIC.frame[OFF_JA_RXDLY]; + if (LMIC.rxDelay == 0) LMIC.rxDelay = 1; + reportEventAndUpdate(EV_JOINED); + return 1; +} + +static bit_t processJoinAccept_badframe(void) { + if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) + // continue the join process: there's another window. + return 0; + else + // stop the join process + return processJoinAccept_nojoinframe(); +} + +static bit_t processJoinAccept_nojoinframe(void) { + // Valid states are JOINING (in which caise REJOIN is ignored) + // or ~JOINING and REJOIN. If it's a REJOIN, + // we need to turn off rejoin, signal an event, and increment + // the rejoin-sent count. Internal callers will turn on rejoin + // occasionally. + if( (LMIC.opmode & OP_JOINING) == 0) { + // formerly, we asserted ((LMIC.opmode & OP_REJOIN) != 0); + // but now we just return 1 if it's not asserted. + if ( (LMIC.opmode & OP_REJOIN) == 0) { + LMIC.opmode &= ~OP_TXRXPEND; + return 1; + } + LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND); + if( LMIC.rejoinCnt < 10 ) + LMIC.rejoinCnt++; + reportEventAndUpdate(EV_REJOIN_FAILED); + // stop the join process. + return 1; + } + // otherwise it's a normal join. At end of rx2, so we + // need to schedule something. + LMIC.opmode &= ~OP_TXRXPEND; + reportEventNoUpdate(EV_JOIN_TXCOMPLETE); + int failed = LMICbandplan_nextJoinState(); + EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.datarate|DR_PAGE, + e_.info2 = failed)); + // Build next JOIN REQUEST with next engineUpdate call + // Optionally, report join failed. + // Both after a random/chosen amount of ticks. That time + // is in LMIC.txend. The delay here is either zero or 1 + // tick; onJoinFailed()/runEngineUpdate() are responsible + // for honoring that. XXX(tmm@mcci.com) The IBM 1.6 code + // claimed to return a delay but really returns 0 or 1. + // Once we update as923 to return failed after dr2, we + // can take out this #if. + os_setTimedCallback(&LMIC.osjob, os_getTime()+failed, + failed + ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed + : FUNC_ADDR(runEngineUpdate)); // next step to be delayed + // stop this join process. + return 1; +} + +static void processRx2Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 ) { + initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot + } + // we're done with this join cycle anyway, so ignore the + // result of processJoinAccept() + (void) processJoinAccept(); +} + + +static void setupRx2Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processRx2Jacc); + setupRx2(); +} + + +static void processRx1Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 || !processJoinAccept() ) + schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr); +} + + +static void setupRx1Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + setupRx1(FUNC_ADDR(processRx1Jacc)); +} + + +static void jreqDone (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc)); +} + +#endif // !DISABLE_JOIN + +// ======================================== Data frames + +// Fwd decl. +static bit_t processDnData(void); + +static void processRx2DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 ) { + initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot + // It could be that the gateway *is* sending a reply, but we + // just didn't pick it up. To avoid TX'ing again while the + // gateay is not listening anyway, delay the next transmission + // until DNW2_SAFETY_ZONE from now, and add up to 2 seconds of + // extra randomization. + // BUG(tmm@mcci.com) this delay is not needed for some + // regions, e.g. US915 and AU915, which have non-overlapping + // uplink and downlink. + txDelay(os_getTime() + DNW2_SAFETY_ZONE, 2); + } + processDnData(); +} + + +static void setupRx2DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processRx2DnData); + setupRx2(); +} + + +static void processRx1DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 || !processDnData() ) + schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr); +} + + +static void setupRx1DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + setupRx1(FUNC_ADDR(processRx1DnData)); +} + + +static void updataDone (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData)); +} + +// ======================================== + +static bit_t sendAdrAckReq(void) { + if (LMIC.adrAckReq < LINK_CHECK_CONT) { + return 0; + } else if (LMIC.adrAckReq <= LINK_CHECK_DEAD) { + return 1; + } else if (LMIC.adrAckReq <= LINK_CHECK_DEAD + 32) { + // for compliance, though it's not clear why they care, we stop sending requests + // when we're right at the DEAD state + return 0; + } else if (LMIC.adrAckReq <= LINK_CHECK_UNJOIN - 32) { + return 0; + } else { + // otherwise, if our alternative is to unjoin and we have no other info, keep + // asking for a downlink. + return 1; + } +} + +static bit_t buildDataFrame (void) { + bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL); + u1_t dlen = txdata ? LMIC.pendTxLen : 0; + + // Piggyback MAC options + // Prioritize by importance + // highest importance are the ones in the pendMac buffer. + int end = OFF_DAT_OPTS; + + // Send piggyback data if: !txdata or txport != 0 + if ((! txdata || LMIC.pendTxPort != 0) && LMIC.pendMacPiggyback && LMIC.pendMacLen != 0) { + os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); + end += LMIC.pendMacLen; + } + LMIC.pendMacLen = 0; + LMIC.pendMacPiggyback = 0; + +#if !defined(DISABLE_MCMD_RXParamSetupReq) + // per 5.4, RxParamSetupAns is sticky. + if (LMIC.dn2Ans) { + if (LMIC.dn2Ans & 0x40) { + LMIC.dn2Ans ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_RXParamSetupAns; + LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU; + end += 2; + } + } +#endif // !DISABLE_MCMD_RXParamSetupReq +#if !defined(DISABLE_MCMD_DlChannelReq) + // per 5.4, DlChannelAns is sticky. + if (LMIC.macDlChannelAns) { + if (LMIC.macDlChannelAns & 0x40) { + LMIC.macDlChannelAns ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_DlChannelAns; + LMIC.frame[end + 1] = LMIC.macDlChannelAns & ~MCMD_DlChannelAns_RFU; + end += 2; + } + } +#endif // !DISABLE_MCMD_DlChannelReq +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + // per 5.7, RXTimingSetupAns is sticky + if (LMIC.macRxTimingSetupAns == 2) { + LMIC.macRxTimingSetupAns = 1; + } else if (LMIC.macRxTimingSetupAns) { + LMIC.frame[end++] = MCMD_RXTimingSetupAns; + } +#endif // !DISABLE_MCMD_RXTimingSetupReq) + +#if LMIC_ENABLE_DeviceTimeReq + if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) { + LMIC.frame[end+0] = MCMD_DeviceTimeReq; + end += 1; + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx; + } +#endif // LMIC_ENABLE_DeviceTimeReq +#if !defined(DISABLE_BEACONS) && defined(ENABLE_MCMD_BeaconTimingAns) + if ( LMIC.bcninfoTries > 0 ) { + LMIC.frame[end+0] = MCMD_BeaconInfoReq; + end += 1; + } +#endif + if (end > OFF_DAT_OPTS + 16) { + LMICOS_logEventUint32("piggyback mac opts too long", end); + return 0; + } + + if( LMIC.adrChanged ) { + // if ADR is enabled, and we were just counting down the + // transmits before starting an ADR, advance the timer so + // we'll do an ADR now. + if (LMIC.adrAckReq < LINK_CHECK_CONT) + setAdrAckCount(LINK_CHECK_CONT); + LMIC.adrChanged = 0; + } + + unsigned int flen = end + (txdata ? 5+dlen : 4); + if( flen > MAX_LEN_FRAME ) { + // Options and payload too big - delay payload + txdata = 0; + flen = end+4; + } + + u1_t maxFlen = LMICbandplan_maxFrameLen(LMIC.datarate); + + if (flen > maxFlen) { + LMICOS_logEventUint32("frame too long for this bandplan", ((u4_t)dlen << 16) | (flen << 8) | maxFlen); + return 0; + } + + LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1; + LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled + | (sendAdrAckReq() ? FCT_ADRACKReq : 0) + | (end-OFF_DAT_OPTS)); + os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr); + + if( LMIC.txCnt == 0 && LMIC.upRepeatCount == 0 ) { + LMIC.seqnoUp += 1; + DO_DEVDB(LMIC.seqnoUp,seqnoUp); + } else { + LMICOS_logEventUint32("retransmit", ((u4_t)LMIC.frame[OFF_DAT_FCT] << 24u) | ((u4_t)LMIC.txCnt << 16u) | (LMIC.upRepeatCount << 8u) | (LMIC.upRepeat<<0u)); + EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoUp-1, + e_.info2 = ((LMIC.txCnt+1) | + (LMIC.upRepeatCount << 8) | + ((LMIC.datarate|DR_PAGE)<<16)))); + } + os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1); + + // Clear pending DN confirmation + LMIC.dnConf = 0; + + if( txdata ) { + if( LMIC.pendTxConf ) { + // Confirmed only makes sense if we have a payload (or at least a port) + LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1; + if( LMIC.txCnt == 0 ) LMIC.txCnt = 1; + } else if (LMIC.upRepeat != 0) { + // we are repeating. So we need to count here. + if (LMIC.upRepeatCount == 0) { + LMIC.upRepeatCount = 1; + } + } + LMIC.frame[end] = LMIC.pendTxPort; + os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen); + aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey, + LMIC.devaddr, LMIC.seqnoUp-1, + /*up*/0, LMIC.frame+end+1, dlen); + } + aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4); + + EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.seqno = LMIC.seqnoUp-1, + e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP), + e_.mic = Base::lsbf4(&LMIC.frame[flen-4]), + e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR], + e_.fct = LMIC.frame[LORA::OFF_DAT_FCT], + e_.port = LMIC.pendTxPort, + e_.plen = txdata ? dlen : 0, + e_.opts.length = end-LORA::OFF_DAT_OPTS, + memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS))); + LMIC.dataLen = flen; + return 1; +} + + +#if !defined(DISABLE_BEACONS) +// Callback from HAL during scan mode or when job timer expires. +static void onBcnRx (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + // If we arrive via job timer make sure to put radio to rest. + os_radio(RADIO_RST); + os_clearCallback(&LMIC.osjob); + if( LMIC.dataLen == 0 ) { + // Nothing received - timeout + LMIC.opmode &= ~(OP_SCAN | OP_TRACK); + reportEventAndUpdate(EV_SCAN_TIMEOUT); + return; + } + if( ! LMIC_BEACON_SUCCESSFUL(decodeBeacon()) ) { + // Something is wrong with the beacon - continue scan + LMIC.dataLen = 0; + os_radio(RADIO_RXON); + os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx)); + return; + } + // Found our 1st beacon + // We don't have a previous beacon to calc some drift - assume + // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm + calcBcnRxWindowFromMillis(13,1); + LMIC.opmode &= ~OP_SCAN; // turn SCAN off + LMIC.opmode |= OP_TRACK; // auto enable tracking + reportEventAndUpdate(EV_BEACON_FOUND); // can be disabled in callback +} + + +// Enable receiver to listen to incoming beacons +// netid defines when scan stops (any or specific beacon) +// This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON +// Implicitely cancels any pending TX/RX transaction. +// Also cancels an onpoing joining procedure. +static void startScan (void) { + // formerly, we asserted. + if (LMIC.devaddr == 0 || (LMIC.opmode & OP_JOINING) != 0) + return; + if( (LMIC.opmode & OP_SHUTDOWN) != 0 ) + return; + // Cancel onging TX/RX transaction + LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0; + LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND); + LMICbandplan_setBcnRxParams(); + LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx)); + os_radio(RADIO_RXON); +} + + +bit_t LMIC_enableTracking (u1_t tryBcnInfo) { + if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 ) + return 0; // already in progress or failed to enable + // If BCN info requested from NWK then app has to take are + // of sending data up so that MCMD_BeaconInfoReq can be attached. + if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) + startScan(); + return 1; // enabled +} + + +void LMIC_disableTracking (void) { + LMIC.opmode &= ~(OP_SCAN|OP_TRACK); + LMIC.bcninfoTries = 0; + engineUpdate(); +} +#endif // !DISABLE_BEACONS + + + + + + + + + + + + + + + + + + + + + + + + + + + +// ================================================================================ +// +// Join stuff +// +// ================================================================================ + +#if !defined(DISABLE_JOIN) +static void buildJoinRequest (u1_t ftype) { + // Do not use pendTxData since we might have a pending + // user level frame in there. Use RX holding area instead. + xref2u1_t d = LMIC.frame; + d[OFF_JR_HDR] = ftype; + os_getArtEui(d + OFF_JR_ARTEUI); + os_getDevEui(d + OFF_JR_DEVEUI); + os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce); + aes_appendMic0(d, OFF_JR_MIC); + + EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(), + e_.arteui = MAIN::CDEV->getArtEui(), + e_.nonce = LMIC.devNonce, + e_.oldaddr = LMIC.devaddr, + e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]), + e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 + ? EV::joininfo_t::REJOIN_REQUEST + : EV::joininfo_t::REQUEST))); + LMIC.dataLen = LEN_JR; + LMIC.devNonce++; + DO_DEVDB(LMIC.devNonce,devNonce); +} + +static void startJoining (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + // see issue #244: for backwards compatibility + // don't override what the user does after os_init(). + if (LMIC.initBandplanAfterReset) + LMICbandplan_resetDefaultChannels(); + else + LMIC.initBandplanAfterReset = 1; + + // let the client know that now's the time to update + // network settings. + reportEventAndUpdate(EV_JOINING); +} + +// reset the joined-to-network state (and clean up) +void LMIC_unjoin(void) { + // reset any joining flags + LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_UNJOIN); + + // put us in unjoined state: + LMIC.devaddr = 0; + + // clear transmit. + LMIC_clrTxData(); +} + +// Start join procedure if not already joined. +bit_t LMIC_startJoining (void) { + if( LMIC.devaddr == 0 ) { + // There should be no TX/RX going on + // ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0); + LMIC.opmode &= ~OP_POLL; + // Lift any previous duty limitation + LMIC.globalDutyRate = 0; + // Cancel scanning + LMIC.opmode &= ~(OP_SCAN|OP_UNJOIN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL); + // Setup state + LMIC.rejoinCnt = LMIC.txCnt = 0; + resetJoinParams(); + LMICbandplan_initJoinLoop(); + LMIC.opmode |= OP_JOINING; + // reportEventAndUpdate will call engineUpdate which then starts sending JOIN REQUESTS + os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining)); + return 1; + } + return 0; // already joined +} + +static void unjoinAndRejoin(xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + LMIC_unjoin(); + LMIC_startJoining(); +} + +// do a deferred unjoin and rejoin, so not in engineupdate. +void LMIC_unjoinAndRejoin(void) { + os_setCallback(&LMIC.osjob, FUNC_ADDR(unjoinAndRejoin)); +} + +#endif // !DISABLE_JOIN + + +// ================================================================================ +// +// +// +// ================================================================================ + +#if !defined(DISABLE_PING) +static void processPingRx (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen != 0 ) { + initTxrxFlags(__func__, TXRX_PING); + if( decodeFrame() ) { + reportEventNoUpdate(EV_RXCOMPLETE); + } + } + // Pick next ping slot + engineUpdate(); +} +#endif // !DISABLE_PING + +// process downlink data at close of RX window. Return zero if another RX window +// should be scheduled, non-zero to prevent scheduling of RX2 (if relevant). +// Confusingly, the caller actualyl does some of the calculation, so the answer from +// us is not always totaly right; the rx1 window check ignores our result unless +// LMIC.datalen was non zero before calling. +// +// Inputs: +// LMIC.dataLen number of bytes receieved; 0 --> no message at all received. +// LMIC.txCnt currnt confirmed uplink count, or 0 for unconfirmed. +// LMIC.txrxflags state of play for the Class A engine and message receipt. +// +// and many other flags in txcomplete(). + +// forward references. +static bit_t processDnData_norx(void); +static bit_t processDnData_txcomplete(void); + +static bit_t processDnData (void) { + // if no TXRXPEND, we shouldn't be here and can do nothign. + // formerly we asserted. + if ((LMIC.opmode & OP_TXRXPEND) == 0) + return 1; + + if( LMIC.dataLen == 0 ) { + // if this is an RX1 window, shouldn't we return 0 to schedule + // RX2? in fact, the rx1 caller ignores what we return, and + // norx() doesn't call txcomplete if this is RX1. + return processDnData_norx(); + } + // if we get here, LMIC.dataLen != 0, so there is some + // traffic. + else if( !decodeFrame() ) { + // if we are in downlink window 1, we need to schedule + // downlink window 2. + if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) + return 0; + else + // otherwise we are in downlink window 2; we will not + // get any more downlink traffic from this uplink, so we need + // to close the books on this uplink attempt + return processDnData_norx(); + } + // downlink frame was accepted. This means that we're done. Except + // there's one bizarre corner case. If we sent a confirmed message + // and got a downlink that didn't have an ACK, we have to retry. + // It is not clear why the network is permitted to do this; the + // fact that they scheduled a downlink for us during one of the RX + // windows is clear confirmation that the uplink made it to the + // network and was valid. However, compliance checks this, so + // we have to handle it and retransmit. + else if (LMIC.txCnt != 0 && (LMIC.txrxFlags & TXRX_NACK) != 0) + { + // grr. we're confirmed but the network downlink did not + // set the ACK bit. We know txCnt is non-zero, so this + // will immediately fall into the retransmit path. We don't + // want to do this unless it's a confirmed uplink. + return processDnData_norx(); + } + // the transmit of the uplink is really complete. + else { + return processDnData_txcomplete(); + } +} + +// nothing was received this window. +static bit_t processDnData_norx(void) { + if( LMIC.txCnt != 0 ) { + if( LMIC.txCnt < TXCONF_ATTEMPTS ) { + // Per [1.0.3] section 18.4, it is recommended that the device adjust datarate down. + // The spec is not clear about what should happen in case the data size is too large + // for the new frame len, but it seems that we should leave theframe len at the new + // data size. Therefore, we set the new data rate here, and then check at transmit time + // whether the packet is now too large; if so, we abandon the transmission. + LMIC.txCnt += 1; + // becase txCnt was at least 1 when we entered this branch, this if() will be taken + // for txCnt == 3, 5, 7. + if (LMIC.txCnt & 1) { + dr_t adjustedDR; + // lower DR + adjustedDR = decDR(LMIC.datarate); + setDrTxpow(DRCHG_NOACK, adjustedDR, KEEP_TXPOW); + } + + // TODO(tmm@mcci.com): check feasibility of lower datarate + // Schedule another retransmission + txDelay(LMIC.rxtime, RETRY_PERIOD_secs); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; + } + // confirmed uplink is complete without an ack: no port and no flag + initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); + } else if (LMIC.upRepeatCount != 0) { + if (LMIC.upRepeatCount < LMIC.upRepeat) { + LMICOS_logEventUint32("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); + LMIC.upRepeatCount += 1; + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; + } + // counted out: nothing received. + initTxrxFlags(__func__, TXRX_NOPORT); + } else { + // Nothing received - implies no port + initTxrxFlags(__func__, TXRX_NOPORT); + } + setAdrAckCount(LMIC.adrAckReq + 1); + LMIC.dataBeg = LMIC.dataLen = 0; + + return processDnData_txcomplete(); +} + +// this Class-A uplink-and-receive cycle is complete. +static bit_t processDnData_txcomplete(void) { + LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + // turn off all the repeat stuff. + LMIC.txCnt = LMIC.upRepeatCount = 0; + + // if there's pending mac data that's not piggyback, launch it now. + if (LMIC.pendMacLen != 0) { + if (LMIC.pendMacPiggyback) { + LMICOS_logEvent("piggyback mac message"); + LMIC.opmode |= OP_POLL; // send back the mac answers even if there's no data. + } else { + // Every mac command on port 0 requires an uplink, if there's data. + // TODO(tmm@mcci.com) -- this is why we need a queueing structure for + // uplinks. + // open code the logic to build this because we don't want to call + // engineUpdate right now. Data is already in the uplink buffer. + LMIC.pendTxConf = 0; // not confirmed + LMIC.pendTxPort = 0; // port 0 + LMIC.pendTxLen = LMIC.pendMacLen; + LMIC.pendMacLen = 0; // discard mac data! + LMIC.opmode |= OP_TXDATA; + LMICOS_logEvent("port0 mac message"); + } + } + + // Half-duplex gateways can have appreciable turn-around times, + // so we force a wait. It might be nice to randomize this a little, + // so that armies of identical devices will not try to talk all + // at once. This is potentially band-specific, so we let it come + // from the band-plan files. + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + +#if LMIC_ENABLE_DeviceTimeReq + // + // if the DeviceTimeReq FSM is active, we need to move it to idle, + // completing the callback. + // + lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState; + if ( requestTimeState != lmic_RequestTimeState_idle ) { + lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.client.pNetworkTimeCb; + int flagSuccess = (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_success); + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle; + if (pNetworkTimeCb != NULL) { + // reset the callback, so that the user's routine + // can post another request if desired. + LMIC.client.pNetworkTimeCb = NULL; + + // call the user's notification routine. + (*pNetworkTimeCb)(LMIC.client.pNetworkTimeUserData, flagSuccess); + } + } +#endif // LMIC_ENABLE_DeviceTimeReq + + if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) { + LMIC.opmode &= ~OP_LINKDEAD; + reportEventNoUpdate(EV_LINK_ALIVE); + } + reportEventAndUpdate(EV_TXCOMPLETE); + // If we haven't heard from NWK in a while although we asked for a sign + // assume link is dead - notify application and keep going + if( LMIC.adrAckReq > LINK_CHECK_DEAD ) { + // We haven't heard from NWK for some time although we + // asked for a response for some time - assume we're disconnected. Lower DR one notch. + EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.adrAckReq)); + dr_t newDr = decDR((dr_t)LMIC.datarate); + // newDr must be feasible; there must be at least + // one channel that supports the new datarate. If not, stay + // at current datarate (which finalizes things). + if (! LMICbandplan_isDataRateFeasible(newDr)) { + LMICOS_logEventUint32("LINK_CHECK_DEAD, new DR not feasible", (newDr << 8) | LMIC.datarate); + newDr = LMIC.datarate; + } + if( newDr == (dr_t)LMIC.datarate) { + // We are already at the minimum datarate + // if the link is already marked dead, we need to join. +#if !defined(DISABLE_JOIN) + if ( LMIC.adrAckReq > LINK_CHECK_UNJOIN ) { + LMIC.opmode |= OP_UNJOIN; + } +#endif // !defined(DISABLE_JOIN) + } else if (newDr == LORAWAN_DR0) { + // the spec says: the ADRACKReq shall not be set if + // the device uses its lowest available data rate. + // (1.0.3, 4.3.1.1, line 458) + // We let the count continue to increase. + } else { + // we successfully lowered the data rate... + // reset so that we'll lower again after the next + // 32 uplinks. + setAdrAckCount(LINK_CHECK_CONT); + } + // Decrease DataRate and restore fullpower. + setDrTxpow(DRCHG_NOADRACK, newDr, pow2dBm(0)); + + // be careful only to report EV_LINK_DEAD once. + u2_t old_opmode = LMIC.opmode; + LMIC.opmode = old_opmode | OP_LINKDEAD; + if (LMIC.opmode != old_opmode) + reportEventNoUpdate(EV_LINK_DEAD); // update? + } +#if !defined(DISABLE_BEACONS) + // If this falls to zero the NWK did not answer our MCMD_BeaconInfoReq commands - try full scan + if( LMIC.bcninfoTries > 0 ) { + if( (LMIC.opmode & OP_TRACK) != 0 ) { + reportEventNoUpdate(EV_BEACON_FOUND); // update? + LMIC.bcninfoTries = 0; + } + else if( --LMIC.bcninfoTries == 0 ) { + startScan(); // NWK did not answer - try scan + } + } +#endif // !DISABLE_BEACONS + return 1; +} + +#if !defined(DISABLE_BEACONS) +static void processBeacon (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite + u1_t flags = LMIC.bcninfo.flags; + ev_t ev; + + if( LMIC.dataLen != 0 && LMIC_BEACON_SUCCESSFUL(decodeBeacon()) ) { + ev = EV_BEACON_TRACKED; + if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) { + // We don't have a previous beacon to calc some drift - assume + // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm + calcBcnRxWindowFromMillis(13,0); + goto rev; + } + // We have a previous BEACON to calculate some drift + s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx); + if( LMIC.missedBcns > 0 ) { + drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1); + } + if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) { + s2_t diff = LMIC.drift - drift; + if( diff < 0 ) diff = -diff; + LMIC.lastDriftDiff = diff; + if( LMIC.maxDriftDiff < diff ) + LMIC.maxDriftDiff = diff; + LMIC.bcninfo.flags &= ~BCN_NODDIFF; + } + LMIC.drift = drift; + LMIC.missedBcns = LMIC.rejoinCnt = 0; + LMIC.bcninfo.flags &= ~BCN_NODRIFT; + EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT, + e_.eui = MAIN::CDEV->getEui(), + e_.info = drift, + e_.info2 = /*occasion BEACON*/0)); + // formerly we'd assert on BCN_PARTIAL|BCN_FULL, but we can't get here if so + } else { + ev = EV_BEACON_MISSED; + LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift; + LMIC.bcninfo.time += BCN_INTV_sec; + LMIC.missedBcns++; + // Delay any possible TX after surmised beacon - it's there although we missed it + txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4); + // if too many missed beacons or we lose sync, drop back to Class A. + if( LMIC.missedBcns > MAX_MISSED_BCNS || + LMIC.bcnRxsyms > MAX_RXSYMS ) { + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEventAndUpdate(EV_LOST_TSYNC); + return; + } + } + LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN); + LMIC.bcnRxsyms = LMIC.rxsyms; + rev: + LMICbandplan_advanceBeaconChannel(); +#if !defined(DISABLE_PING) + if( (LMIC.opmode & OP_PINGINI) != 0 ) + rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! +#endif // !DISABLE_PING + reportEventAndUpdate(ev); +} + +// job entry: time to start receiving a beacon. +static void startRxBcn (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processBeacon); + radioRx(); +} +#endif // !DISABLE_BEACONS + + +#if !defined(DISABLE_PING) +// job entry: time to start receiving in our scheduled downlink slot. +static void startRxPing (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processPingRx); + radioRx(); +} +#endif // !DISABLE_PING + + +// Decide what to do next for the MAC layer of a device. Inner part. +// Only called from outer part. +static void engineUpdate_inner (void) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode); +#endif + // Check for ongoing state: scan or TX/RX transaction + if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) + return; + +#if !defined(DISABLE_JOIN) + if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) { + LMIC_startJoining(); + return; + } + // we're joined but LinkTracking says we're out of luck... + if ( LMIC.devaddr != 0 && (LMIC.opmode & OP_UNJOIN) != 0 ) { + LMIC.opmode &= ~OP_UNJOIN; + LMIC_unjoinAndRejoin(); + return; + } +#endif // !DISABLE_JOIN + + ostime_t now = os_getTime(); + ostime_t txbeg = 0; + +#if !defined(DISABLE_BEACONS) + ostime_t rxtime = 0; + + if( (LMIC.opmode & OP_TRACK) != 0 ) { + // We are tracking a beacon + // formerly asserted ( now - (LMIC.bcnRxtime - os_getRadioRxRampup()) <= 0 ); + rxtime = LMIC.bcnRxtime - os_getRadioRxRampup(); + if (now - rxtime < 0) { + // too late: drop out of Class B. + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEventNoUpdate(EV_LOST_TSYNC); + return; + } + } +#endif // !DISABLE_BEACONS + + if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) { + // Assuming txChnl points to channel which first becomes available again. + bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0); + // Find next suitable channel and return availability time + if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) { + txbeg = LMIC.txend = LMICbandplan_nextTx(now); + LMIC.opmode &= ~OP_NEXTCHNL; + } else { + // no need to consider anything but LMIC.txend. + txbeg = LMIC.txend; + } + // Delayed TX or waiting for duty cycle? + if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 ) + txbeg = LMIC.globalDutyAvail; +#if !defined(DISABLE_BEACONS) + // If we're tracking a beacon... + // then make sure TX-RX transaction is complete before beacon + if( (LMIC.opmode & OP_TRACK) != 0 && + txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) { + // Not enough time to complete TX-RX before beacon - postpone after beacon. + // In order to avoid clustering of postponed TX right after beacon randomize start! + txDelay(rxtime + BCN_RESERVE_osticks, 16); + txbeg = 0; + goto checkrx; + } +#endif // !DISABLE_BEACONS + // Earliest possible time vs overhead to setup radio + if( txbeg - (now + TX_RAMPUP) < 0 ) { + // We could send right now! + txbeg = now; + dr_t txdr = (dr_t)LMIC.datarate; +#if !defined(DISABLE_JOIN) + if( jacc ) { + u1_t ftype; + if( (LMIC.opmode & OP_REJOIN) != 0 ) { +#if CFG_region != LMIC_REGION_as923 + // in AS923 v1.1 or older, no need to change the datarate. + // otherwise we need to check feasibility. + txdr = lowerDR(txdr, LMIC.rejoinCnt); +#endif + } + ftype = HDR_FTYPE_JREQ; + buildJoinRequest(ftype); + LMIC.osjob.func = FUNC_ADDR(jreqDone); + } else +#endif // !DISABLE_JOIN + { + if( LMIC.seqnoDn >= 0xFFFFFF80 ) { + // Imminent roll over - proactively reset MAC + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = 0)); + // Device has to react! NWK will not roll over and just stop sending. + // Thus, we have N frames to detect a possible lock up. + reset: + os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset)); + return; + } + if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) { + // Roll over of up seq counter + EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info2 = LMIC.seqnoUp)); + // Do not run RESET event callback from here! + // App code might do some stuff after send unaware of RESET. + goto reset; + } + if (! buildDataFrame()) { + // can't transmit this message. Report completion. + initTxrxFlags(__func__, TXRX_LENERR); + if (LMIC.pendTxConf || LMIC.txCnt) { + orTxrxFlags(__func__, TXRX_NACK); + } + LMIC.opmode &= ~(OP_POLL|OP_RNDTX|OP_TXDATA|OP_TXRXPEND); + LMIC.dataBeg = LMIC.dataLen = 0; + reportEventNoUpdate(EV_TXCOMPLETE); + return; + } + LMIC.osjob.func = FUNC_ADDR(updataDone); + } // end of else (not joining) + LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr); + LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1 + LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; + LMICbandplan_updateTx(txbeg); + // limit power to value asked in adr + LMIC.radio_txpow = LMIC.txpow > LMIC.adrTxPow ? LMIC.adrTxPow : LMIC.txpow; + reportEventNoUpdate(EV_TXSTART); + os_radio(RADIO_TX); + return; + } + // Cannot yet TX + if( (LMIC.opmode & OP_TRACK) == 0 ) + goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX + // Consider RX tasks + if( txbeg == 0 ) // zero indicates no TX pending + txbeg += 1; // TX delayed by one tick (insignificant amount of time) + } else { + // No TX pending - no scheduled RX + if( (LMIC.opmode & OP_TRACK) == 0 ) + return; + } + +#if !defined(DISABLE_BEACONS) + // Are we pingable? + checkrx: +#if !defined(DISABLE_PING) + if( (LMIC.opmode & OP_PINGINI) != 0 ) { + // One more RX slot in this beacon period? + if( rxschedNext(&LMIC.ping, now+os_getRadioRxRampup()) ) { + if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 ) + goto txdelay; + LMIC.rxsyms = LMIC.ping.rxsyms; + LMIC.rxtime = LMIC.ping.rxtime; + LMIC.freq = LMIC.ping.freq; + LMIC.rps = dndr2rps(LMIC.ping.dr); + LMIC.dataLen = 0; + ostime_t rxtime_ping = LMIC.rxtime - os_getRadioRxRampup(); + // did we miss the time? + if (now - rxtime_ping > 0) { + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEventNoUpdate(EV_LOST_TSYNC); + } else { + os_setTimedCallback(&LMIC.osjob, rxtime_ping, FUNC_ADDR(startRxPing)); + } + return; + } + // no - just wait for the beacon + } +#endif // !DISABLE_PING + + if( txbeg != 0 && (txbeg - rxtime) < 0 ) + goto txdelay; + + LMICbandplan_setBcnRxParams(); + LMIC.rxsyms = LMIC.bcnRxsyms; + LMIC.rxtime = LMIC.bcnRxtime; + if( now - rxtime >= 0 ) { + LMIC.osjob.func = FUNC_ADDR(processBeacon); + + radioRx(); + return; + } + os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn)); + return; +#endif // !DISABLE_BEACONS + + txdelay: + EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY, + e_.eui = MAIN::CDEV->getEui(), + e_.info = osticks2ms(txbeg-now), + e_.info2 = LMIC.seqnoUp-1)); + LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": next engine update in %"LMIC_PRId_ostime_t"\n", now, txbeg-TX_RAMPUP); + os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate)); +} + +// Decide what to do next for the MAC layer of a device. +// Outer part. Safe to call from anywhere; defers if it +// detects a recursive call. +static void engineUpdate (void) { + lmic_engine_update_state_t state; + + state = LMIC.engineUpdateState; + if (state == lmic_EngineUpdateState_idle) { + LMIC.engineUpdateState = lmic_EngineUpdateState_busy; + do { + engineUpdate_inner(); + state = LMIC.engineUpdateState - 1; + LMIC.engineUpdateState = state; + } while (state != lmic_EngineUpdateState_idle); + } else { + LMIC.engineUpdateState = lmic_EngineUpdateState_again; + } +} + +void LMIC_setAdrMode (bit_t enabled) { + LMIC.adrEnabled = enabled ? FCT_ADREN : 0; +} + + +// Should we have/need an ext. API like this? +void LMIC_setDrTxpow (dr_t dr, s1_t txpow) { + setDrTxpow(DRCHG_SET, dr, txpow); +} + + +void LMIC_shutdown (void) { + os_clearCallback(&LMIC.osjob); + os_radio(RADIO_RST); + LMIC.opmode |= OP_SHUTDOWN; +} + +// reset the LMIC. This is called at startup; the clear of LMIC.osjob +// only works because the LMIC is guaranteed to be zero in that case. +// But it's also called at frame-count rollover; in that case we have +// to ensure that the user callback pointers are not clobbered. +void LMIC_reset (void) { + EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, + e_.eui = MAIN::CDEV->getEui(), + e_.info = EV_RESET)); + os_radio(RADIO_RST); + os_clearCallback(&LMIC.osjob); + + // save callback info, clear LMIC, restore. + do { + lmic_client_data_t client = LMIC.client; + + os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC)); + + LMIC.client = client; + } while (0); + + // LMIC.devaddr = 0; // true from os_clearMem(). + LMIC.devNonce = os_getRndU2(); + LMIC.opmode = OP_NONE; + LMIC.errcr = CR_4_5; + LMIC.adrEnabled = FCT_ADREN; + resetJoinParams(); + LMIC.rxDelay = DELAY_DNW1; + // LMIC.pendMacLen = 0; + // LMIC.pendMacPiggyback = 0; + // LMIC.dn2Ans = 0; + // LMIC.macDlChannelAns = 0; + // LMIC.macRxTimingSetupAns = 0; +#if !defined(DISABLE_PING) + LMIC.ping.freq = FREQ_PING; // defaults for ping + LMIC.ping.dr = DR_PING; // ditto + LMIC.ping.intvExp = 0xFF; +#endif // !DISABLE_PING + + LMICbandplan_resetDefaultChannels(); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.devNonce, devNonce); + DO_DEVDB(LMIC.dn2Dr, dn2Dr); + DO_DEVDB(LMIC.dn2Freq, dn2Freq); +#if !defined(DISABLE_PING) + DO_DEVDB(LMIC.ping.freq, pingFreq); + DO_DEVDB(LMIC.ping.dr, pingDr); + DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); +#endif // !DISABLE_PING +#if LMIC_ENABLE_DeviceTimeReq + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle; + LMIC.netDeviceTime = 0; // the "invalid" time. + LMIC.netDeviceTimeFrac = 0; +#endif // LMIC_ENABLE_DeviceTimeReq +} + + +void LMIC_init (void) { + LMIC.opmode = OP_SHUTDOWN; + LMICbandplan_init(); +} + + +void LMIC_clrTxData (void) { + u2_t opmode = LMIC.opmode; + bit_t const txActive = opmode & OP_TXDATA; + if (! txActive) { + return; + } + LMIC.pendTxLen = 0; + opmode &= ~(OP_TXDATA | OP_POLL); + if (! (opmode & OP_JOINING)) { + // in this case, we are joining, and the TX data + // is just pending. + opmode &= ~(OP_TXRXPEND); + } + + LMIC.opmode = opmode; + + if (txActive) + reportEventNoUpdate(EV_TXCANCELED); + + if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING + return; + os_clearCallback(&LMIC.osjob); + os_radio(RADIO_RST); + engineUpdate(); +} + +dr_t LMIC_feasibleDataRateForFrame(dr_t dr, u1_t payloadSize) { + if (payloadSize > MAX_LEN_PAYLOAD) { + return dr; + } + + const u1_t frameSize = payloadSize + OFF_DAT_OPTS + 5; + dr_t trialDr, nextDr; + + for (trialDr = dr; ;) { + if (! LMICbandplan_isDataRateFeasible(trialDr)) + break; + u1_t maxSizeThisDr = LMICbandplan_maxFrameLen(trialDr); + if (maxSizeThisDr == 0) { + break; + } else if (frameSize <= maxSizeThisDr) { + // we found one that is feasible! + return trialDr; + } + // try the next DR + nextDr = incDR(trialDr); + if (nextDr == trialDr) + break; + trialDr = nextDr; + } + + // if we get here, we didn't find a working dr. + return dr; +} + +static bit_t isTxPathBusy(void) { + return (LMIC.opmode & (OP_TXDATA|OP_JOINING)) != 0; +} + +static bit_t adjustDrForFrameIfNotBusy(u1_t len) { + if (isTxPathBusy()) { + return 0; + } + dr_t newDr = LMIC_feasibleDataRateForFrame(LMIC.datarate, len); + if (newDr != LMIC.datarate) { + setDrTxpow(DRCHG_FRAMESIZE, newDr, KEEP_TXPOW); + } + return 1; +} + +void LMIC_setTxData (void) { + adjustDrForFrameIfNotBusy(LMIC.pendTxLen); + LMIC_setTxData_strict(); +} + +void LMIC_setTxData_strict (void) { + LMICOS_logEventUint32(__func__, ((u4_t)LMIC.pendTxPort << 24u) | ((u4_t)LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); + LMIC.opmode |= OP_TXDATA; + if( (LMIC.opmode & OP_JOINING) == 0 ) { + LMIC.txCnt = 0; // reset the confirmed uplink FSM + LMIC.upRepeatCount = 0; // reset the unconfirmed repeat FSM + } + engineUpdate(); +} + + +// send a message, attempting to adjust TX data rate +lmic_tx_error_t LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { + adjustDrForFrameIfNotBusy(dlen); + return LMIC_setTxData2_strict(port, data, dlen, confirmed); +} + +// send a message w/o callback; do not adjust data rate +lmic_tx_error_t LMIC_setTxData2_strict (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { + if ( LMIC.opmode & OP_TXDATA ) { + // already have a message queued + return LMIC_ERROR_TX_BUSY; + } + if( dlen > SIZEOFEXPR(LMIC.pendTxData) ) + return LMIC_ERROR_TX_TOO_LARGE; + if( data != (xref2u1_t)0 ) + os_copyMem(LMIC.pendTxData, data, dlen); + LMIC.pendTxConf = confirmed; + LMIC.pendTxPort = port; + LMIC.pendTxLen = dlen; + LMIC_setTxData_strict(); + if ( (LMIC.opmode & OP_TXDATA) == 0 ) { + if (LMIC.txrxFlags & TXRX_LENERR) { + return LMIC_ERROR_TX_NOT_FEASIBLE; + } else { + // data has already been completed with error for some reason + return LMIC_ERROR_TX_FAILED; + } + } + return 0; +} + +// send a message with callback; try to adjust data rate +lmic_tx_error_t LMIC_sendWithCallback ( + u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, + lmic_txmessage_cb_t *pCb, void *pUserData +) { + adjustDrForFrameIfNotBusy(dlen); + return LMIC_sendWithCallback_strict(port, data, dlen, confirmed, pCb, pUserData); +} + +// send a message with callback; do not adjust datarate +lmic_tx_error_t LMIC_sendWithCallback_strict ( + u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, + lmic_txmessage_cb_t *pCb, void *pUserData +) { + lmic_tx_error_t const result = LMIC_setTxData2_strict(port, data, dlen, confirmed); + if (result == 0) { + LMIC.client.txMessageCb = pCb; + LMIC.client.txMessageUserData = pUserData; + } + return result; +} + + +// Send a payload-less message to signal device is alive +void LMIC_sendAlive (void) { + LMIC.opmode |= OP_POLL; + engineUpdate(); +} + + +// Check if other networks are around. +void LMIC_tryRejoin (void) { + LMIC.opmode |= OP_REJOIN; + engineUpdate(); +} + +//! \brief Setup given session keys +//! and put the MAC in a state as if +//! a join request/accept would have negotiated just these keys. +//! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey` +//! are unique within the network identified by `netid`. +//! NOTE: on Harvard architectures when session keys are in flash: +//! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL +//! \param netid a 24 bit number describing the network id this device is using +//! \param devaddr the 32 bit session address of the device. It is strongly recommended +//! to ensure that different devices use different numbers with high probability. +//! \param nwkKey the 16 byte network session key used for message integrity. +//! If NULL the caller has copied the key into `LMIC.nwkKey` before. +//! \param artKey the 16 byte application router session key used for message confidentiality. +//! If NULL the caller has copied the key into `LMIC.artKey` before. + +// TODO(tmm@mcci.com) we ought to also save the channels that were returned by the +// join accept; right now this has to be done by the caller (or it doesn't get done). +void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { + LMIC.netid = netid; + LMIC.devaddr = devaddr; + if( nwkKey != (xref2u1_t)0 ) + os_copyMem(LMIC.nwkKey, nwkKey, 16); + if( artKey != (xref2u1_t)0 ) + os_copyMem(LMIC.artKey, artKey, 16); + + LMICbandplan_setSessionInitDefaultChannels(); + + LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_UNJOIN|OP_REJOIN|OP_TXRXPEND|OP_PINGINI); + LMIC.opmode |= OP_NEXTCHNL; + stateJustJoined(); + // transition to the ADR_ACK_DELAY state. + setAdrAckCount(LINK_CHECK_CONT); + + DO_DEVDB(LMIC.netid, netid); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.nwkKey, nwkkey); + DO_DEVDB(LMIC.artKey, artkey); + DO_DEVDB(LMIC.seqnoUp, seqnoUp); + DO_DEVDB(LMIC.seqnoDn, seqnoDn); +} + +// Enable/disable link check validation. +// LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames +// for a while. It expects the network to provide a DN message to prove +// connectivity with a span of UP frames. If this no such prove is coming +// then the datarate is lowered and a LINK_DEAD event is generated. +// This mode can be disabled and no connectivity prove (ADRACKREQ) is requested +// nor is the datarate changed. +// This must be called only if a session is established (e.g. after EV_JOINED) +void LMIC_setLinkCheckMode (bit_t enabled) { + LMIC.adrChanged = 0; + LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF; +} + +// Sets the max clock error to compensate for (defaults to 0, which +// allows for +/- 640 at SF7BW250). MAX_CLOCK_ERROR represents +/-100%, +// so e.g. for a +/-1% error you would pass MAX_CLOCK_ERROR * 1 / 100. +void LMIC_setClockError(u2_t error) { + LMIC.client.clockError = error; +} + +// \brief return the uplink sequence number for the next transmission. +// This simple getter returns the uplink sequence number maintained by the LMIC engine. +// The caller should store the value and restore it (see LMIC_setSeqnoUp) on +// LMIC initialization to ensure monotonically increasing sequence numbers. +// It's also useful in debugging, as it allows you to correlate a debug trace event with +// a specific packet sent over the air. +u4_t LMIC_getSeqnoUp(void) { + return LMIC.seqnoUp; +} + +// \brief set the uplink sequence number for the next transmission. +// Use the function on startup to ensure that the next transmission uses +// a sequence number higher than the last transmission. +u4_t LMIC_setSeqnoUp(u4_t seq_no) { + u4_t last = LMIC.seqnoUp; + LMIC.seqnoUp = seq_no; + return last; +} + +// \brief return the current session keys returned from join. +void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { + *netid = LMIC.netid; + *devaddr = LMIC.devaddr; + memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey)); + memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey)); +} + +// \brief post an asynchronous request for the network time. +void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData) { +#if LMIC_ENABLE_DeviceTimeReq + if (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_idle) { + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_tx; + LMIC.client.pNetworkTimeCb = pCallbackfn; + LMIC.client.pNetworkTimeUserData = pUserData; + return; + } +#endif // LMIC_ENABLE_DeviceTimeReq + // if no device time support, or if not in proper state, + // report a failure. + if (pCallbackfn != NULL) + (*pCallbackfn)(pUserData, /* false */ 0); +} + +// \brief return local/remote time pair (if valid, and DeviceTimeReq enabled), +// return true for success, false for error. We adjust the sampled OS time +// back in time to the nearest second boundary. +int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference) { +#if LMIC_ENABLE_DeviceTimeReq + if (pReference != NULL && // valid parameter, and + LMIC.netDeviceTime != 0) { // ... we have a reasonable answer. + const ostime_t tAdjust = LMIC.netDeviceTimeFrac * ms2osticks(1000) / 256; + + pReference->tLocal = LMIC.localDeviceTime - tAdjust; + pReference->tNetwork = LMIC.netDeviceTime; + return 1; + } +#else + LMIC_API_PARAMETER(pReference); +#endif // LMIC_ENABLE_DeviceTimeReq + return 0; +} diff --git a/libraries/arduino-lmic-master/src/lmic/lmic.h b/libraries/arduino-lmic-master/src/lmic/lmic.h new file mode 100644 index 0000000..471f1f9 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic.h @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016 Matthijs Kooijman. + * Copyright (c) 2016-2020 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! @file +//! @brief LMIC API + +#ifndef _lmic_h_ +#define _lmic_h_ + +#include "oslmic.h" +#include "lorabase.h" + +#if LMIC_DEBUG_LEVEL > 0 || LMIC_X_DEBUG_LEVEL > 0 +# if defined(LMIC_DEBUG_INCLUDE) +# define LMIC_STRINGIFY_(x) #x +# define LMIC_STRINGIFY(x) LMIC_STRINGIFY_(x) +# include LMIC_STRINGIFY(LMIC_DEBUG_INCLUDE) +# endif +# ifdef LMIC_DEBUG_PRINTF_FN + extern void LMIC_DEBUG_PRINTF_FN(const char *f, ...); +# endif // ndef LMIC_DEBUG_PRINTF_FN +#endif + +// if LMIC_DEBUG_PRINTF is now defined, just use it. This lets you do anything +// you like with a sufficiently crazy header file. +#if LMIC_DEBUG_LEVEL > 0 +# ifndef LMIC_DEBUG_PRINTF +// otherwise, check whether someone configured a print-function to be used, +// and use it if so. +# ifdef LMIC_DEBUG_PRINTF_FN +# define LMIC_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) +# ifndef LMIC_DEBUG_INCLUDE // If you use LMIC_DEBUG_INCLUDE, put the declaration in there + void LMIC_DEBUG_PRINTF_FN(const char *f, ...); +# endif // ndef LMIC_DEBUG_INCLUDE +# else // ndef LMIC_DEBUG_PRINTF_FN +// if there's no other info, just use printf. In a pure Arduino environment, +// that's what will happen. +# include +# define LMIC_DEBUG_PRINTF(f, ...) printf(f, ## __VA_ARGS__) +# endif // ndef LMIC_DEBUG_PRINTF_FN +# endif // ndef LMIC_DEBUG_PRINTF +# ifndef LMIC_DEBUG_FLUSH +# ifdef LMIC_DEBUG_FLUSH_FN +# define LMIC_DEBUG_FLUSH() LMIC_DEBUG_FLUSH_FN() +# else // ndef LMIC_DEBUG_FLUSH_FN +// if there's no other info, assume that flush is not needed. +# define LMIC_DEBUG_FLUSH() do { ; } while (0) +# endif // ndef LMIC_DEBUG_FLUSH_FN +# endif // ndef LMIC_DEBUG_FLUSH +#else // LMIC_DEBUG_LEVEL == 0 +// If debug level is zero, printf and flush expand to nothing. +# define LMIC_DEBUG_PRINTF(f, ...) do { ; } while (0) +# define LMIC_DEBUG_FLUSH() do { ; } while (0) +#endif // LMIC_DEBUG_LEVEL == 0 + +// +// LMIC_X_DEBUG_LEVEL enables additional, special print functions for debugging +// RSSI features. This is used sparingly. +#if LMIC_X_DEBUG_LEVEL > 0 +# ifdef LMIC_DEBUG_PRINTF_FN +# define LMIC_X_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) +# else +# error "LMIC_DEBUG_PRINTF_FN must be defined for LMIC_X_DEBUG_LEVEL > 0." +# endif +#else +# define LMIC_X_DEBUG_PRINTF(f, ...) do {;} while(0) +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + +// LMIC version -- this is ths IBM LMIC version +#define LMIC_VERSION_MAJOR 1 +#define LMIC_VERSION_MINOR 6 +#define LMIC_VERSION_BUILD 1468577746 + +// Arduino LMIC version +#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \ + ((((major)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0)) + +#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 3, 0, 0) /* v3.3.0 */ + +#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \ + ((((v)*UINT32_C(1)) >> 24u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_MINOR(v) \ + ((((v)*UINT32_C(1)) >> 16u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_PATCH(v) \ + ((((v)*UINT32_C(1)) >> 8u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_LOCAL(v) \ + ((v) & 0xFFu) + +//! Only For Antenna Tuning Tests ! +//#define CFG_TxContinuousMode 1 + +// since this was annouunced as the API variable, we keep it. But it's not used, +// MAX_LEN_FRAME is what the code uses. +enum { MAX_FRAME_LEN = MAX_LEN_FRAME }; //!< Library cap on max frame length + +enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames +enum { MAX_MISSED_BCNS = (2 * 60 * 60 + 127) / 128 }; //!< threshold for dropping out of class B, triggering rejoin requests + // note that we need 100 ppm timing accuracy for + // this, to keep the timing error to +/- 700ms. +enum { MAX_RXSYMS = 350 }; // Stop tracking beacon if sync error grows beyond this. A 0.4% clock error + // at SF9.125k means 512 ms; one sybol is 4.096 ms, + // so this needs to be at least 125 for an STM32L0. + // And for 100ppm clocks and 2 hours of beacon misses, + // this needs to accomodate 1.4 seconds of error at + // 4.096 ms/sym or at least 342 symbols. + +enum { LINK_CHECK_CONT = 0 , // continue with this after reported dead link + LINK_CHECK_DEAD = 32 , // after this UP frames and no response to ack from NWK assume link is dead (ADR_ACK_DELAY) + LINK_CHECK_UNJOIN_MIN = LINK_CHECK_DEAD + 4, // this is the minimum value of LINK_CHECK_UNJOIN if we parameterize + LINK_CHECK_UNJOIN = LINK_CHECK_DEAD + (3 * 240), // after this many UP frames and no response, switch to join (by default) + LINK_CHECK_INIT = -64 , // UP frame count until we ask for ack (ADR_ACK_LIMIT) + LINK_CHECK_OFF =-128 }; // link check disabled + +enum { TIME_RESYNC = 6*128 }; // secs +enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon +enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon +enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time +enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send + +#if CFG_LMIC_EU_like // EU868 spectrum ==================================================== + +enum { MAX_CHANNELS = 16 }; //!< Max supported channels +enum { MAX_BANDS = 4 }; + +enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels +//! \internal +struct band_t { + u2_t txcap; // duty cycle limitation: 1/txcap + s1_t txpow; // maximum TX power + u1_t lastchnl; // last used channel + ostime_t avail; // channel is blocked until this time +}; +TYPEDEF_xref2band_t; //!< \internal + +struct lmic_saved_adr_state_s { + u4_t channelFreq[MAX_CHANNELS]; + u2_t channelMap; +}; + +#elif CFG_LMIC_US_like // US915 spectrum ================================================= + +enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable + +struct lmic_saved_adr_state_s { + u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t activeChannels125khz; + u2_t activeChannels500khz; +}; + +#endif // ========================================================================== + +typedef struct lmic_saved_adr_state_s lmic_saved_adr_state_t; + +// Keep in sync with evdefs.hpp::drChange +enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD, DRCHG_FRAMESIZE }; +enum { KEEP_TXPOW = -128 }; + + +#if !defined(DISABLE_PING) +//! \internal +struct rxsched_t { + dr_t dr; + u1_t intvExp; // 0..7 + u1_t slot; // runs from 0 to 128 + rxsyms_t rxsyms; + ostime_t rxbase; + ostime_t rxtime; // start of next spot + u4_t freq; +}; +TYPEDEF_xref2rxsched_t; //!< \internal +#endif // !DISABLE_PING + + +#if !defined(DISABLE_BEACONS) +//! Parsing and tracking states of beacons. +enum { BCN_NONE = 0x00, //!< No beacon received + BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) + BCN_FULL = 0x02, //!< Full beacon decoded + BCN_NODRIFT = 0x04, //!< No drift value measured yet + BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet +//! Information about the last and previous beacons. +struct bcninfo_t { + ostime_t txtime; //!< Time when the beacon was sent + u4_t time; //!< GPS time in seconds of last beacon (received or surrogate) + s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) + s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) + s1_t rssi; //!< Adjusted RSSI value of last received beacon + s1_t snr; //!< Scaled SNR value of last received beacon + u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values. + // + u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set) +}; +#endif // !DISABLE_BEACONS + +// purpose of receive window - lmic_t.rxState +enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TX_AT=4, }; +// Netid values / lmic_t.netid +enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; +// MAC operation modes (lmic_t.opmode). +enum { OP_NONE = 0x0000, + OP_SCAN = 0x0001, // radio scan to find a beacon + OP_TRACK = 0x0002, // track my networks beacon (netid) + OP_JOINING = 0x0004, // device joining in progress (blocks other activities) + OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData) + OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data + OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST + OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything + OP_TXRXPEND = 0x0080, // TX/RX transaction pending + OP_RNDTX = 0x0100, // prevent TX lining up after a beacon + OP_PINGINI = 0x0200, // pingable is initialized and scheduling active + OP_PINGABLE = 0x0400, // we're pingable + OP_NEXTCHNL = 0x0800, // find a new channel + OP_LINKDEAD = 0x1000, // link was reported as dead + OP_TESTMODE = 0x2000, // developer test mode + OP_UNJOIN = 0x4000, // unjoin and rejoin on next engineUpdate(). +}; +// TX-RX transaction flags - report back to user +enum { TXRX_ACK = 0x80, // confirmed UP frame was acked + TXRX_NACK = 0x40, // confirmed UP frame was not acked + TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port + TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port + TXRX_LENERR = 0x08, // set if frame was discarded due to length error. + TXRX_PING = 0x04, // received in a scheduled RX slot + TXRX_DNW2 = 0x02, // received in 2dn DN slot + TXRX_DNW1 = 0x01, // received in 1st DN slot +}; + +// Event types for event callback +enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND, + EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING, + EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED, + EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET, + EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND, + EV_TXSTART, EV_TXCANCELED, EV_RXSTART, EV_JOIN_TXCOMPLETE }; +typedef enum _ev_t ev_t; + +// this macro can be used to initalize a normal table of event strings +#define LMIC_EVENT_NAME_TABLE__INIT \ + "<>", \ + "EV_SCAN_TIMEOUT", "EV_BEACON_FOUND", \ + "EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING", \ + "EV_JOINED", "EV_RFU1", "EV_JOIN_FAILED", "EV_REJOIN_FAILED", \ + "EV_TXCOMPLETE", "EV_LOST_TSYNC", "EV_RESET", \ + "EV_RXCOMPLETE", "EV_LINK_DEAD", "EV_LINK_ALIVE", "EV_SCAN_FOUND", \ + "EV_TXSTART", "EV_TXCANCELED", "EV_RXSTART", "EV_JOIN_TXCOMPLETE" + +// if working on an AVR (or worried about it), you can use this multi-zero +// string and put this in a single const F() string. Index through this +// counting up from 0, until you get to the entry you want or to an +// entry that begins with a \0. +#define LMIC_EVENT_NAME_MULTISZ__INIT \ + "<>\0" \ + "EV_SCAN_TIMEOUT\0" "EV_BEACON_FOUND\0" \ + "EV_BEACON_MISSED\0" "EV_BEACON_TRACKED\0" "EV_JOINING\0" \ + "EV_JOINED\0" "EV_RFU1\0" "EV_JOIN_FAILED\0" "EV_REJOIN_FAILED\0" \ + "EV_TXCOMPLETE\0" "EV_LOST_TSYNC\0" "EV_RESET\0" \ + "EV_RXCOMPLETE\0" "EV_LINK_DEAD\0" "EV_LINK_ALIVE\0" "EV_SCAN_FOUND\0" \ + "EV_TXSTART\0" "EV_TXCANCELED\0" "EV_RXSTART\0" "EV_JOIN_TXCOMPLETE\0" + +enum { + LMIC_ERROR_SUCCESS = 0, + LMIC_ERROR_TX_BUSY = -1, + LMIC_ERROR_TX_TOO_LARGE = -2, + LMIC_ERROR_TX_NOT_FEASIBLE = -3, + LMIC_ERROR_TX_FAILED = -4, +}; + +typedef int lmic_tx_error_t; + +#define LMIC_ERROR_NAME__INIT \ + "LMIC_ERROR_SUCCESS", \ + "LMIC_ERROR_TX_BUSY", \ + "LMIC_ERROR_TX_TOO_LARGE", \ + "LMIC_ERROR_TX_NOT_FEASIBLE", \ + "LMIC_ERROR_TX_FAILED" + +#define LMIC_ERROR_NAME_MULTISZ__INIT \ + "LMIC_ERROR_SUCCESS\0" \ + "LMIC_ERROR_TX_BUSY\0" \ + "LMIC_ERROR_TX_TOO_LARGE\0" \ + "LMIC_ERROR_TX_NOT_FEASIBLE\0" \ + "LMIC_ERROR_TX_FAILED" + +enum { + LMIC_BEACON_ERROR_INVALID = -2, + LMIC_BEACON_ERROR_WRONG_NETWORK = -1, + LMIC_BEACON_ERROR_SUCCESS_PARTIAL = 0, + LMIC_BEACON_ERROR_SUCCESS_FULL = 1, +}; + +typedef s1_t lmic_beacon_error_t; + +static inline bit_t LMIC_BEACON_SUCCESSFUL(lmic_beacon_error_t e) { + return e < 0; +} + +// LMIC_CFG_max_clock_error_ppm +#if !defined(LMIC_CFG_max_clock_error_ppm) +# define LMIC_CFG_max_clock_error_ppm 2000 /* max clock error: 0.2% (2000 ppm) */ +#endif + + +enum { + // This value represents 100% error in LMIC.clockError + MAX_CLOCK_ERROR = 65536, + //! \brief maximum clock error that users can specify: 2000 ppm (0.2%). + //! \details This is the limit for clock error, unless LMIC_ENABLE_arbitrary_clock_error is set. + //! The default is 4,000 ppm, which is .004, or 0.4%; this is what you get on an + //! STM32L0 running with the HSI oscillator after cal. If your clock error is bigger, + //! usually you want to calibrate it so that millis() and micros() are reasonably + //! accurate. Important: do not use clock error to compensate for late serving + //! of the LMIC. If you see that LMIC.radio.rxlate_count is increasing, you need + //! to adjust your application logic so the LMIC gets serviced promptly when a + //! Class A downlink (or beacon) is pending. + LMIC_kMaxClockError_ppm = 4000, +}; + +// callbacks for client alerts. +// types and functions are always defined, to reduce #ifs in example code and libraries. +typedef void LMIC_ABI_STD lmic_rxmessage_cb_t(void *pUserData, uint8_t port, const uint8_t *pMessage, size_t nMessage); +typedef void LMIC_ABI_STD lmic_txmessage_cb_t(void *pUserData, int fSuccess); +typedef void LMIC_ABI_STD lmic_event_cb_t(void *pUserData, ev_t e); + +// network time request callback function +// defined unconditionally, because APIs and types can't change based on config. +// This is called when a time-request succeeds or when we get a downlink +// without time request, "completing" the pending time request. +typedef void LMIC_ABI_STD lmic_request_network_time_cb_t(void *pUserData, int flagSuccess); + +// how the network represents time. +typedef u4_t lmic_gpstime_t; + +// rather than deal with 1/256 second tick, we adjust ostime back +// (as it's high res) to match tNetwork. +typedef struct lmic_time_reference_s lmic_time_reference_t; + +struct lmic_time_reference_s { + // our best idea of when we sent the uplink (end of packet). + ostime_t tLocal; + // the network's best idea of when we sent the uplink. + lmic_gpstime_t tNetwork; +}; + +enum lmic_request_time_state_e { + lmic_RequestTimeState_idle = 0, // we're not doing anything + lmic_RequestTimeState_tx, // we want to tx a time request on next uplink + lmic_RequestTimeState_rx, // we have tx'ed, next downlink completes. + lmic_RequestTimeState_success // we sucessfully got time. +}; + +typedef u1_t lmic_request_time_state_t; + +enum lmic_engine_update_state_e { + lmic_EngineUpdateState_idle = 0, // engineUpdate is idle. + lmic_EngineUpdateState_busy = 1, // engineUpdate is busy, but has not been reentered. + lmic_EngineUpdateState_again = 2, // engineUpdate is busy, and has to be evaluated again. +}; + +typedef u1_t lmic_engine_update_state_t; + +/* + +Structure: lmic_client_data_t + +Function: + Holds LMIC client data that must live through LMIC_reset(). + +Description: + There are a variety of client registration linkage items that + must live through LMIC_reset(), because LMIC_reset() is called + at frame rollover time. We group them together into a structure + to make copies easy. + +*/ + +//! abstract type for collection of client data that survives LMIC_reset(). +typedef struct lmic_client_data_s lmic_client_data_t; + +//! contents of lmic_client_data_t +struct lmic_client_data_s { + + /* pointer-width things come first */ +#if LMIC_ENABLE_DeviceTimeReq + lmic_request_network_time_cb_t *pNetworkTimeCb; //! call-back routine for network time + void *pNetworkTimeUserData; //! call-back data for network time. +#endif + +#if LMIC_ENABLE_user_events + lmic_event_cb_t *eventCb; //! user-supplied callback function for events. + void *eventUserData; //! data for eventCb + lmic_rxmessage_cb_t *rxMessageCb; //! user-supplied message-received callback + void *rxMessageUserData; //! data for rxMessageCb + lmic_txmessage_cb_t *txMessageCb; //! transmit-complete message handler; reset on each tx complete. + void *txMessageUserData; //! data for txMessageCb. +#endif // LMIC_ENABLE_user_events + + /* next we have things that are (u)int32_t */ + /* none at the moment */ + + /* next we have things that are (u)int16_t */ + + u2_t clockError; //! Inaccuracy in the clock. CLOCK_ERROR_MAX represents +/-100% error + + /* finally, things that are (u)int8_t */ + /* none at the moment */ +}; + +/* + +Structure: lmic_radio_data_t + +Function: + Holds LMIC radio driver. + +Description: + Eventually this will be used for all portable things for the radio driver, + but for now it's where we can start to add things. + +*/ + +typedef struct lmic_radio_data_s lmic_radio_data_t; + +struct lmic_radio_data_s { + // total os ticks of accumulated delay error. Can overflow! + ostime_t rxlate_ticks; + // number of rx late launches. + unsigned rxlate_count; + // total os ticks of accumulated tx delay error. Can overflow! + ostime_t txlate_ticks; + // number of tx late launches. + unsigned txlate_count; +}; + +/* + +Structure: lmic_t + +Function: + Provides the instance data for the LMIC. + +*/ + +struct lmic_t { + // client setup data, survives LMIC_reset(). + lmic_client_data_t client; + + // the OS job object. pointer alignment. + osjob_t osjob; + +#if !defined(DISABLE_BEACONS) + bcninfo_t bcninfo; // Last received beacon info +#endif + +#if !defined(DISABLE_PING) + rxsched_t ping; // pingable setup +#endif + + // the radio driver portable context + lmic_radio_data_t radio; + + /* (u)int32_t things */ + + // Radio settings TX/RX (also accessed by HAL) + ostime_t txend; + ostime_t rxtime; + + // LBT info + ostime_t lbt_ticks; // ticks to listen + + u4_t freq; + + ostime_t globalDutyAvail; // time device can send again + + u4_t netid; // current network id (~0 - none) + devaddr_t devaddr; + u4_t seqnoDn; // device level down stream seqno + u4_t seqnoUp; + u4_t dn2Freq; + +#if !defined(DISABLE_BEACONS) + ostime_t bcnRxtime; +#endif + +#if LMIC_ENABLE_DeviceTimeReq + // put here for alignment, to reduce RAM use. + ostime_t localDeviceTime; // the LMIC.txend value for last DeviceTimeAns + lmic_gpstime_t netDeviceTime; // the netDeviceTime for lastDeviceTimeAns + // zero ==> not valid. +#endif // LMIC_ENABLE_DeviceTimeReq + + // Channel scheduling -- very much private +#if CFG_LMIC_EU_like + band_t bands[MAX_BANDS]; + u4_t channelFreq[MAX_CHANNELS]; +#if !defined(DISABLE_MCMD_DlChannelReq) + u4_t channelDlFreq[MAX_CHANNELS]; +#endif + // bit map of enabled datarates for each channel + u2_t channelDrMap[MAX_CHANNELS]; + u2_t channelMap; +#elif CFG_LMIC_US_like + u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) + u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto + u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t activeChannels125khz; + u2_t activeChannels500khz; +#endif + + /* (u)int16_t things */ + rps_t rps; // radio parameter selections: SF, BW, CodingRate, NoCrc, implicit hdr + u2_t opmode; // engineUpdate() operating mode flags + u2_t devNonce; // last generated nonce + + s2_t adrAckReq; // counter for link integrity tracking (LINK_CHECK_OFF=off) + +#if !defined(DISABLE_BEACONS) + s2_t drift; // last measured drift + s2_t lastDriftDiff; + s2_t maxDriftDiff; + rxsyms_t bcnRxsyms; // +#endif + + /* (u)int8_t things */ + lmic_engine_update_state_t engineUpdateState; // state of the engineUpdate() evaluator. + s1_t rssi; + s1_t snr; // LMIC.snr is SNR times 4 + rxsyms_t rxsyms; // symbols for receive timeout. + u1_t dndr; + s1_t txpow; // transmit dBm (administrative) + s1_t radio_txpow; // the radio driver's copy of txpow, in dB limited by adrTxPow, and + // also adjusted for EIRP/antenna gain considerations. + // This is just the radio's idea of power. So if you are + // controlling EIRP, and you have 3 dB antenna gain, this + // needs to reduced by 3 dB. + s1_t lbt_dbmax; // max permissible dB on our channel (eg -80) + + u1_t txChnl; // channel for next TX + u1_t globalDutyRate; // max rate: 1/2^k + + u1_t upRepeat; // configured up repeat + s1_t adrTxPow; // ADR adjusted TX power + u1_t datarate; // current data rate + u1_t errcr; // error coding rate (used for TX only) + u1_t rejoinCnt; // adjustment for rejoin datarate + + u1_t upRepeatCount; // current up-repeat + bit_t initBandplanAfterReset; // cleared by LMIC_reset(), set by first join. See issue #244 + + u1_t pendTxPort; + u1_t pendTxConf; // confirmed data + u1_t pendTxLen; // count of bytes in pendTxData. + u1_t pendTxData[MAX_LEN_PAYLOAD]; + + u1_t pendMacLen; // number of bytes of pending Mac response data + bit_t pendMacPiggyback; // received on port 0 or piggyback? + // response data if piggybacked + u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX]; + + u1_t nwkKey[16]; // network session key + u1_t artKey[16]; // application router session key + + u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 + u1_t lastDnConf; // downlink with seqnoDn-1 requested confirmation + u1_t adrChanged; + + u1_t rxDelay; // Rx delay after TX + + u1_t margin; + s1_t devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command + u1_t adrEnabled; + u1_t moreData; // NWK has more data pending +#if LMIC_ENABLE_TxParamSetupReq + u1_t txParam; // the saved TX param byte. +#endif +#if LMIC_ENABLE_DeviceTimeReq + lmic_request_time_state_t txDeviceTimeReqState; // current state, initially idle. + u1_t netDeviceTimeFrac; // updated on any DeviceTimeAns. +#endif + + // rx1DrOffset is the offset from uplink to downlink datarate + u1_t rx1DrOffset; // captured from join. zero by default. + + // 2nd RX window (after up stream) + u1_t dn2Dr; +#if !defined(DISABLE_MCMD_RXParamSetupReq) + u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs +#endif +#if !defined(DISABLE_MCMD_DlChannelReq) + u1_t macDlChannelAns; // 0 ==> no answer pending, 0x80+ACK bits +#endif +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + bit_t macRxTimingSetupAns; // 0 ==> no answer pend, non-zero inserts response. +#endif + + // Class B state +#if !defined(DISABLE_BEACONS) + u1_t missedBcns; // unable to track last N beacons + u1_t bcninfoTries; // how often to try (scan mode only) +#endif + // Public part of MAC state + u1_t txCnt; + u1_t txrxFlags; // transaction flags (TX-RX combo) + u1_t dataBeg; // 0 or start of data (dataBeg-1 is port) + u1_t dataLen; // 0 no data or zero length data, >0 byte count of data + u1_t frame[MAX_LEN_FRAME]; + +#if !defined(DISABLE_BEACONS) + u1_t bcnChnl; +#endif + + u1_t noRXIQinversion; + u1_t saveIrqFlags; // last LoRa IRQ flags +}; + +//! \var struct lmic_t LMIC +//! The state of LMIC MAC layer is encapsulated in this variable. +DECLARE_LMIC; //!< \internal + +//! Construct a bit map of allowed datarates from drlo to drhi (both included). +#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi)))) +bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap); +bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band); +bit_t LMIC_disableChannel (u1_t channel); +bit_t LMIC_enableSubBand(u1_t band); +bit_t LMIC_enableChannel(u1_t channel); +bit_t LMIC_disableSubBand(u1_t band); +bit_t LMIC_selectSubBand(u1_t band); + +void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow +void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) + +#if !defined(DISABLE_JOIN) +bit_t LMIC_startJoining (void); +void LMIC_tryRejoin (void); +void LMIC_unjoin (void); +void LMIC_unjoinAndRejoin (void); +#endif + +void LMIC_shutdown (void); +void LMIC_init (void); +void LMIC_reset (void); +void LMIC_clrTxData (void); +void LMIC_setTxData (void); +void LMIC_setTxData_strict(void); +lmic_tx_error_t LMIC_setTxData2(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); +lmic_tx_error_t LMIC_setTxData2_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); +lmic_tx_error_t LMIC_sendWithCallback(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData); +lmic_tx_error_t LMIC_sendWithCallback_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData); +void LMIC_sendAlive (void); + +#if !defined(DISABLE_BEACONS) +bit_t LMIC_enableTracking (u1_t tryBcnInfo); +void LMIC_disableTracking (void); +#endif + +#if !defined(DISABLE_PING) +void LMIC_stopPingable (void); +void LMIC_setPingable (u1_t intvExp); +#endif + +void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey); +void LMIC_setLinkCheckMode (bit_t enabled); +void LMIC_setClockError(u2_t error); + +u4_t LMIC_getSeqnoUp (void); +u4_t LMIC_setSeqnoUp (u4_t); +void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey); + +void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData); +int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference); + +int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData); +int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData); + +// APIs for client half of compliance. +typedef u1_t lmic_compliance_rx_action_t; + +enum lmic_compliance_rx_action_e { + LMIC_COMPLIANCE_RX_ACTION_PROCESS = 0, // process this message normally + LMIC_COMPLIANCE_RX_ACTION_START, // enter compliance mode, discard this message + LMIC_COMPLIANCE_RX_ACTION_IGNORE, // continue in compliance mode, discard this message + LMIC_COMPLIANCE_RX_ACTION_END // exit compliance mode, discard this message +}; + +lmic_compliance_rx_action_t LMIC_complianceRxMessage(u1_t port, const u1_t *pMessage, size_t nMessage); + +// Declare onEvent() function, to make sure any definition will have the +// C conventions, even when in a C++ file. +#if LMIC_ENABLE_onEvent +DECL_ON_LMIC_EVENT; +#endif /* LMIC_ENABLE_onEvent */ + +// Special APIs - for development or testing +// !!!See implementation for caveats!!! + +#ifdef __cplusplus +} // extern "C" +#endif + +// names for backward compatibility +#include "lmic_compat.h" + +#endif // _lmic_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_as923.c b/libraries/arduino-lmic-master/src/lmic/lmic_as923.c new file mode 100644 index 0000000..f46f50b --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_as923.c @@ -0,0 +1,402 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_as923) +// ================================================================================ +// +// BEG: AS923 related stuff +// + +// see table in section 2.7.3 +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), // [6] + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), // [7] + ILLEGAL_RPS +}; + +// see table in 2.7.6 -- this assumes UplinkDwellTime = 0. +static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { + 59+5, // [0] + 59+5, // [1] + 59+5, // [2] + 123+5, // [3] + 250+5, // [4] + 250+5, // [5] + 250+5, // [6] + 250+5 // [7] +}; + +// see table in 2.7.6 -- this assumes UplinkDwellTime = 1. +static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { + 0, // [0] + 0, // [1] + 19+5, // [2] + 61+5, // [3] + 133+5, // [4] + 250+5, // [5] + 250+5, // [6] + 250+5 // [7] +}; + +static uint8_t +LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) { + if (mcmd_txparam == 0xFF) + return AS923_INITIAL_TxParam_UplinkDwellTime; + + return (mcmd_txparam & MCMD_TxParam_TxDWELL_MASK) != 0; +} + +static uint8_t +LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) { + if (mcmd_txparam == 0xFF) + return AS923_INITIAL_TxParam_DownlinkDwellTime; + + return (mcmd_txparam & MCMD_TxParam_RxDWELL_MASK) != 0; +} + +uint8_t LMICas923_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens_dwell0)) { + if (LMICas923_getUplinkDwellBit(LMIC.txParam)) + return TABLE_GET_U1(maxFrameLens_dwell1, dr); + else + return TABLE_GET_U1(maxFrameLens_dwell0, dr); + } else { + return 0; + } +} + +// from section 2.7.3. These are all referenced to the max EIRP of the +// device, which is set by TxParams +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 0, // [0]: MaxEIRP + -2, // [1]: MaxEIRP - 2dB + -6, // [2]: MaxEIRP - 4dB + -8, // [3]: MaxEIRP - 6dB + -4, // [4]: MaxEIRP - 8dB + -10, // [5]: MaxEIRP - 10dB + -12, // [6]: MaxEIRP - 12dB + -14, // [7]: MaxEIRP - 14dB +}; + +// from LoRaWAN 5.8: mapping from txParam to MaxEIRP +static CONST_TABLE(s1_t, TXMAXEIRP)[16] = { + 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 +}; + +static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) { + // if uninitialized, return default. + if (mcmd_txparam == 0xFF) + return AS923_TX_EIRP_MAX_DBM; + else + return TABLE_GET_S1( + TXMAXEIRP, + (mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >> + MCMD_TxParam_MaxEIRP_SHIFT + ); +} + +// translate from an encoded power to an actual power using +// the maxeirp setting; return -128 if not legal. +int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + s1_t const adj = + TABLE_GET_S1( + TXPOWLEVELS, + pindex + ); + + return LMICas923_getMaxEIRP(LMIC.txParam) + adj; + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // DR_SF7B: 250K bps, DR_SF7 + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICas923_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// Default duty cycle is 1%. +enum { NUM_DEFAULT_CHANNELS = 2 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + AS923_F1 | BAND_CENTI, + AS923_F2 | BAND_CENTI, +}; + +// as923 ignores join, becuase the channel setup is the same either way. +void LMICas923_initDefaultChannels(bit_t join) { + LMIC_API_PARAMETER(join); + + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; futxpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_CENTI; + } else { + if (band != BAND_CENTI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = + drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) + : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICas923_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < AS923_FREQ_MIN || freq > AS923_FREQ_MAX) + freq = 0; + return freq; +} + +// when can we join next? +ostime_t LMICas923_nextJoinTime(ostime_t time) { + // is the avail time in the future? + if ((s4_t) (time - LMIC.bands[BAND_CENTI].avail) < 0) + // yes: then wait until then. + time = LMIC.bands[BAND_CENTI].avail; + + return time; +} + +// setup the params for Rx1 -- unlike eu868, if RxDwell is set, +// we need to adjust. +void LMICas923_setRx1Params(void) { + int minDr; + int const txdr = LMIC.dndr; + int effective_rx1DrOffset; + int candidateDr; + + LMICeulike_setRx1Freq(); + + effective_rx1DrOffset = LMIC.rx1DrOffset; + // per section 2.7.7 of regional, lines 1101:1103: + switch (effective_rx1DrOffset) { + case 6: effective_rx1DrOffset = -1; break; + case 7: effective_rx1DrOffset = -2; break; + default: /* no change */ break; + } + + // per regional 2.2.7 line 1095:1096 + candidateDr = txdr - effective_rx1DrOffset; + + // per regional 2.2.7 lines 1097:1100 + if (LMICas923_getDownlinkDwellBit(LMIC.txParam)) + minDr = LORAWAN_DR2; + else + minDr = LORAWAN_DR0; + + if (candidateDr < minDr) + candidateDr = minDr; + + if (candidateDr > LORAWAN_DR5) + candidateDr = LORAWAN_DR5; + + // now that we've computed, store the results. + LMIC.dndr = (uint8_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + + +// return the next time, but also do channel hopping here +// identical to the EU868 version; but note that we only have BAND_CENTI +// at work. +ostime_t LMICas923_nextTx(ostime_t now) { + u1_t bmap = 0xF; + do { + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + for (u1_t bi = 0; bi<4; bi++) { + if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[band = bi].avail; + } + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return mintime; + } + } + if ((bmap &= ~(1 << band)) == 0) { + // No feasible channel found! + return mintime; + } + } while (1); +} + +#if !defined(DISABLE_BEACONS) +void LMICas923_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICas923_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +void +LMICas923_initJoinLoop(void) { + // LMIC.txParam is set to 0xFF by the central code at init time. + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ AS923_TX_EIRP_MAX_DBM); +} + +void +LMICas923_updateTx(ostime_t txbeg) { + u4_t freq = LMIC.channelFreq[LMIC.txChnl]; + u4_t dwellDelay; + u4_t globalDutyDelay; + + // Update global/band specific duty cycle stats + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + // Update channel/global duty cycle stats + xref2band_t band = &LMIC.bands[freq & 0x3]; + LMIC.freq = freq & ~(u4_t)3; + LMIC.txpow = LMICas923_getMaxEIRP(LMIC.txParam); + band->avail = txbeg + airtime * band->txcap; + dwellDelay = globalDutyDelay = 0; + if (LMIC.globalDutyRate != 0) { + globalDutyDelay = (airtime << LMIC.globalDutyRate); + } + if (LMICas923_getUplinkDwellBit(LMIC.txParam)) { + dwellDelay = AS923_UPLINK_DWELL_TIME_osticks; + } + if (dwellDelay > globalDutyDelay) { + globalDutyDelay = dwellDelay; + } + if (globalDutyDelay != 0) + LMIC.globalDutyAvail = txbeg + globalDutyDelay; +} + + +// +// END: AS923 related stuff +// +// ================================================================================ +#endif diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_au915.c b/libraries/arduino-lmic-master/src/lmic/lmic_au915.c new file mode 100644 index 0000000..08df1ad --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_au915.c @@ -0,0 +1,302 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_au915) +// ================================================================================ +// +// BEG: AU915 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, // [-1] + MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + MAKERPS(SF9 , BW125, CR_4_5, 0, 0), // [3] + MAKERPS(SF8 , BW125, CR_4_5, 0, 0), // [4] + MAKERPS(SF7 , BW125, CR_4_5, 0, 0), // [5] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [6] + ILLEGAL_RPS , // [7] + MAKERPS(SF12, BW500, CR_4_5, 0, 0), // [8] + MAKERPS(SF11, BW500, CR_4_5, 0, 0), // [9] + MAKERPS(SF10, BW500, CR_4_5, 0, 0), // [10] + MAKERPS(SF9 , BW500, CR_4_5, 0, 0), // [11] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [12] + MAKERPS(SF7 , BW500, CR_4_5, 0, 0), // [13] + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 0, + 61+5, 137+5, 250+5, 250+5, 250+5, 250+5 }; + +static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { + 0, 0, 19+5, 61+5, 133+5, 250+5, 250+5, 0, + 61+5, 137+5, 250+5, 250+5, 250+5, 250+5 }; + +static bit_t +LMICau915_getUplinkDwellBit() { + // if uninitialized, return default. + if (LMIC.txParam == 0xFF) { + return AU915_INITIAL_TxParam_UplinkDwellTime; + } + return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0; +} + +uint8_t LMICau915_maxFrameLen(uint8_t dr) { + if (LMICau915_getUplinkDwellBit()) { + if (dr < LENOF_TABLE(maxFrameLens_dwell0)) + return TABLE_GET_U1(maxFrameLens_dwell0, dr); + else + return 0; + } else { + if (dr < LENOF_TABLE(maxFrameLens_dwell1)) + return TABLE_GET_U1(maxFrameLens_dwell1, dr); + else + return 0; + } +} + +// from LoRaWAN 5.8: mapping from txParam to MaxEIRP +static CONST_TABLE(s1_t, TXMAXEIRP)[16] = { + 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 +}; + +static int8_t LMICau915_getMaxEIRP(uint8_t mcmd_txparam) { + // if uninitialized, return default. + if (mcmd_txparam == 0xFF) + return AU915_TX_EIRP_MAX_DBM; + else + return TABLE_GET_S1( + TXMAXEIRP, + (mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >> + MCMD_TxParam_MaxEIRP_SHIFT + ); +} + +int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK) + return -128; + else { + return ((s1_t)(LMICau915_getMaxEIRP(LMIC.txParam) - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); + } +} + +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // DR_SF8C + us2osticksRound(128 << 0), // ------ + us2osticksRound(128 << 5), // DR_SF12CR + us2osticksRound(128 << 4), // DR_SF11CR + us2osticksRound(128 << 3), // DR_SF10CR + us2osticksRound(128 << 2), // DR_SF9CR + us2osticksRound(128 << 1), // DR_SF8CR + us2osticksRound(128 << 0), // DR_SF7CR +}; + +// get ostime for symbols based on datarate. This is not like us915, +// becuase the times don't match between the upper half and lower half +// of the table. +ostime_t LMICau915_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + + +u4_t LMICau915_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < AU915_FREQ_MIN || freq > AU915_FREQ_MAX) + freq = 0; + return freq; +} + +// au915: no support for xchannels. +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + LMIC_API_PARAMETER(chidx); + LMIC_API_PARAMETER(freq); + LMIC_API_PARAMETER(drmap); + LMIC_API_PARAMETER(band); + + return 0; // all channels are hardwired. +} + +bit_t LMIC_disableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72) { + if (ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz--; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz--; + } + LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72) { + if (!ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz++; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz++; + } + LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // enable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_enableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Enable it, too. + result |= LMIC_enableChannel(64 + band); + return result; +} + +bit_t LMIC_disableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // disable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_disableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Disable it, too. + result |= LMIC_disableChannel(64 + band); + return result; +} + +bit_t LMIC_selectSubBand(u1_t band) { + bit_t result = 0; + + ASSERT(band < 8); + for (int b = 0; b<8; ++b) { + if (band == b) + result |= LMIC_enableSubBand(b); + else + result |= LMIC_disableSubBand(b); + } + return result; +} + +void LMICau915_updateTx(ostime_t txbeg) { + u1_t chnl = LMIC.txChnl; + LMIC.txpow = LMICau915_getMaxEIRP(LMIC.txParam); + if (chnl < 64) { + LMIC.freq = AU915_125kHz_UPFBASE + chnl*AU915_125kHz_UPFSTEP; + } else { + ASSERT(chnl < 64 + 8); + LMIC.freq = AU915_500kHz_UPFBASE + (chnl - 64)*AU915_500kHz_UPFSTEP; + } + + // Update global duty cycle stat and deal with dwell time. + u4_t dwellDelay; + u4_t globalDutyDelay; + dwellDelay = globalDutyDelay = 0; + + if (LMIC.globalDutyRate != 0) { + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + globalDutyDelay = txbeg + (airtime << LMIC.globalDutyRate); + } + if (LMICau915_getUplinkDwellBit(LMIC.txParam)) { + dwellDelay = AU915_UPLINK_DWELL_TIME_osticks; + } + if (dwellDelay > globalDutyDelay) { + globalDutyDelay = dwellDelay; + } + if (globalDutyDelay != 0) { + LMIC.globalDutyAvail = txbeg + globalDutyDelay; + } +} + +#if !defined(DISABLE_BEACONS) +void LMICau915_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = AU915_500kHz_DNFBASE + LMIC.bcnChnl * AU915_500kHz_DNFSTEP; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +// set the Rx1 dndr, rps. +void LMICau915_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + u1_t candidateDr; + LMIC.freq = AU915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU915_500kHz_DNFSTEP; + if ( /* TX datarate */txdr < AU915_DR_SF8C) + candidateDr = txdr + 8 - LMIC.rx1DrOffset; + else + candidateDr = AU915_DR_SF7CR; + + if (candidateDr < LORAWAN_DR8) + candidateDr = LORAWAN_DR8; + else if (candidateDr > LORAWAN_DR13) + candidateDr = LORAWAN_DR13; + + LMIC.dndr = candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void LMICau915_initJoinLoop(void) { + // LMIC.txParam is set to 0xFF by the central code at init time. + LMICuslike_initJoinLoop(); + + // initialize the adrTxPower. + LMIC.adrTxPow = LMICau915_getMaxEIRP(LMIC.txParam); // dBm + +} + +// +// END: AU915 related stuff +// +// ================================================================================ +#endif diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan.h new file mode 100644 index 0000000..db5863d --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan.h @@ -0,0 +1,231 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_h_ +# define _lmic_bandplan_h_ + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +#if defined(CFG_eu868) +# include "lmic_bandplan_eu868.h" +#elif defined(CFG_us915) +# include "lmic_bandplan_us915.h" +#elif defined(CFG_au915) +# include "lmic_bandplan_au915.h" +#elif defined(CFG_as923) +# include "lmic_bandplan_as923.h" +#elif defined(CFG_kr920) +# include "lmic_bandplan_kr920.h" +#elif defined(CFG_in866) +# include "lmic_bandplan_in866.h" +#else +# error "CFG_... not properly set for bandplan" +#endif + +// check post-conditions +#ifndef DNW2_SAFETY_ZONE +# error "DNW2_SAFETY_ZONE not defined by bandplan" +#endif + +#ifndef LMICbandplan_maxFrameLen +# error "LMICbandplan_maxFrameLen() not defined by bandplan" +#endif + +#ifndef pow2dBm +# error "pow2dBm() not defined by bandplan" +#endif + +#ifndef dr2hsym +# error "dr2hsym() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isValidBeacon1) && !defined(DISABLE_BEACONS) +# error "LMICbandplan_isValidBeacon1 not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isFSK) +# error "LMICbandplan_isFSK() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_txDoneFSK) +# error "LMICbandplan_txDoneFSK() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_joinAcceptChannelClear) +# error "LMICbandplan_joinAcceptChannelClear() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_getInitialDrJoin) +# error "LMICbandplan_getInitialDrJoin() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_hasJoinCFlist) +# error "LMICbandplan_hasJoinCFlist() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_advanceBeaconChannel) +# error "LMICbandplan_advanceBeaconChannel() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_resetDefaultChannels) +# error "LMICbandplan_resetDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setSessionInitDefaultChannels) +# error "LMICbandplan_setSessionInitDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setBcnRxParams) +# error "LMICbandplan_setBcnRxParams() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_canMapChannels) +# error "LMICbandplan_canMapChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_mapChannels) +# error "LMICbandplan_mapChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_convFreq) +# error "LMICbandplan_convFreq() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setRx1Params) +# error "LMICbandplan_setRx1Params() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_initJoinLoop) +# error "LMICbandplan_initJoinLoop() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextTx) +# error "LMICbandplan_nextTx() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_updateTx) +# error "LMICbandplan_updateTx() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextJoinState) +# error "LMICbandplan_nextJoinState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_initDefaultChannels) +# error "LMICbandplan_initDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextJoinTime) +# error "LMICbandplan_nextJoinTime() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_init) +# error "LMICbandplan_init() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_saveAdrState) +# error "LMICbandplan_saveAdrState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_compareAdrState) +# error "LMICbandplan_compareAdrState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_restoreAdrState) +# error "LMICbandplan_restoreAdrState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isDataRateFeasible) +# error "LMICbandplan_isDataRateFeasible() not defined by bandplan" +#endif + +// +// Things common to lmic.c code +// +#define LMICbandplan_MINRX_SYMS_LoRa_ClassA 6 +#define LMICbandplan_RX_ERROR_ABS_osticks ms2osticks(10) + +// Semtech inherently (by calculating in ms and taking ceilings) +// rounds up to the next higher ms. It's a lot easier for us +// to just add margin for things like hardware ramp-up time +// and clock calibration when running from the LSE and HSI +// clocks on an STM32. +#define LMICbandplan_RX_EXTRA_MARGIN_osticks us2osticks(2000) + +// probably this should be the same as the Class-A value, but +// we have not the means to thoroughly test this. This is the +// number of rxsyms used in the computations for ping and beacon +// windows. +#define LMICbandplan_MINRX_SYMS_LoRa_ClassB 5 + +#define LMICbandplan_PAMBL_SYMS 8 +#define LMICbandplan_PAMBL_FSK 5 +#define LMICbandplan_PRERX_FSK 1 +#define LMICbandplan_RXLEN_FSK (1+5+2) + +// Legacy names +#if !defined(MINRX_SYMS) +# define MINRX_SYMS LMICbandplan_MINRX_SYMS_LoRa_ClassB +#endif // !defined(MINRX_SYMS) +#define PAMBL_SYMS LMICbandplan_PAMBL_SYMS +#define PAMBL_FSK LMICbandplan_PAMBL_FSK +#define PRERX_FSK LMICbandplan_PRERX_FSK +#define RXLEN_FSK LMICbandplan_RXLEN_FSK + +// this is regional, but so far all regions are the same +#if !defined(LMICbandplan_MAX_FCNT_GAP) +# define LMICbandplan_MAX_FCNT_GAP 16384 +#endif // !defined LWAN_MAX_FCNT_GAP + +// this is probably regional, but for now default can be the same +#if !defined(LMICbandplan_TX_RECOVERY_ms) +# define LMICbandplan_TX_RECOVERY_ms 500 +#endif + +#define BCN_INTV_osticks sec2osticks(BCN_INTV_sec) +#define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms) +#define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms) +#define DELAY_JACC1_osticks sec2osticks(DELAY_JACC1) +#define DELAY_JACC2_osticks sec2osticks(DELAY_JACC2) +#define DELAY_EXTDNW2_osticks sec2osticks(DELAY_EXTDNW2) +#define BCN_RESERVE_osticks ms2osticks(BCN_RESERVE_ms) +#define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms) +#define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms) +#define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN) + +// Special APIs - for development or testing +#define isTESTMODE() 0 + +// internal APIs +ostime_t LMICcore_rndDelay(u1_t secSpan); +void LMICcore_setDrJoin(u1_t reason, u1_t dr); +ostime_t LMICcore_adjustForDrift(ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in); + +#endif // _lmic_bandplan_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_as923.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_as923.h new file mode 100644 index 0000000..16f4518 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_as923.h @@ -0,0 +1,111 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_as923_h_ +# define _lmic_bandplan_as923_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (as923); 0 --> not valid dr. +uint8_t LMICas923_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICas923_maxFrameLen(dr) + +int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICas923_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICas923_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICas923_dr2hsym(dr) + +static inline int +LMICas923_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICas923_isValidBeacon1(pFrame) + +// override default for LMICbandplan_resetDefaultChannels +void +LMICas923_resetDefaultChannels(void); + +#undef LMICbandplan_resetDefaultChannels +#define LMICbandplan_resetDefaultChannels() \ + LMICas923_resetDefaultChannels() + +// override default for LMICbandplan_init +void LMICas923_init(void); + +#undef LMICbandplan_init +#define LMICbandplan_init() \ + LMICas923_init() + + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == AS923_DR_FSK) + +#define LMICbandplan_getInitialDrJoin() (AS923_DR_SF10) + +void LMICas923_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICas923_setBcnRxParams() + +u4_t LMICas923_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICas923_convFreq(ptr) + +void LMICas923_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICas923_initJoinLoop() + +// for as923, depending on dwell, we may need to do something else +#undef LMICbandplan_setRx1Params +void LMICas923_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICas923_setRx1Params() + +ostime_t LMICas923_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICas923_nextTx(now) + +ostime_t LMICas923_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICas923_nextJoinState() + +void LMICas923_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICas923_initDefaultChannels(join) + +// override default for LMICbandplan_updateTX +#undef LMICbandplan_updateTx +void LMICas923_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICas923_updateTx(txbeg) + +#undef LMICbandplan_nextJoinTime +ostime_t LMICas923_nextJoinTime(ostime_t now); +#define LMICbandplan_nextJoinTime(now) LMICas923_nextJoinTime(now) + +#endif // _lmic_bandplan_as923_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_au915.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_au915.h new file mode 100644 index 0000000..f17194b --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_au915.h @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_au915_h_ +# define _lmic_bandplan_au915_h_ + +// preconditions for lmic_us_like.h +#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR6) +#define LMICuslike_getJoin125kHzDR() (LORAWAN_DR2) + +#ifndef _lmic_us_like_h_ +# include "lmic_us_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (au915); 0 --> not valid dr. +uint8_t LMICau915_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICau915_maxFrameLen(dr) + +int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICau915_pow2dbm(mcmd_ladr_p1) + +ostime_t LMICau915_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICau915_dr2hsym(dr) + + +#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR2) + +void LMICau915_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICau915_initJoinLoop() + +void LMICau915_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICau915_setBcnRxParams() + +u4_t LMICau915_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICau915_convFreq(ptr) + +void LMICau915_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICau915_setRx1Params() + +void LMICau915_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICau915_updateTx(txbeg) + +#endif // _lmic_bandplan_au915_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_eu868.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_eu868.h new file mode 100644 index 0000000..efff7d5 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_eu868.h @@ -0,0 +1,91 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_eu868_h_ +# define _lmic_eu868_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (eu868); 0 --> not valid dr. +uint8_t LMICeu868_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICeu868_maxFrameLen(dr) + +int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICeu868_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICeu868_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICeu868_dr2hsym(dr) + + +// TODO(tmm@mcci.com) this looks bogus compared to current 1.02 regional +// spec. https://github.com/mcci-catena/arduino-lmic/issues/18 +static inline int +LMICeu868_isValidBeacon1(const uint8_t *d) { + return d[OFF_BCN_CRC1] != (u1_t)os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICeu868_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == EU868_DR_FSK) + +#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7) + +void LMICeu868_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICeu868_setBcnRxParams() + +u4_t LMICeu868_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICeu868_convFreq(ptr) + +void LMICeu868_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICeu868_initJoinLoop() + +ostime_t LMICeu868_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICeu868_nextTx(now) + +ostime_t LMICeu868_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICeu868_nextJoinState() + +void LMICeu868_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICeu868_initDefaultChannels(join) + +#undef LMICbandplan_nextJoinTime +ostime_t LMICeu868_nextJoinTime(ostime_t now); +#define LMICbandplan_nextJoinTime(now) LMICeu868_nextJoinTime(now) + +void LMICeu868_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICeu868_setRx1Params() + +#endif // _lmic_eu868_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_in866.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_in866.h new file mode 100644 index 0000000..dad10ca --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_in866.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_in866_h_ +# define _lmic_bandplan_in866_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (in866); 0 --> not valid dr. +uint8_t LMICin866_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICin866_maxFrameLen(dr) + +int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICin866_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICin866_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICin866_dr2hsym(dr) + +static inline int +LMICin866_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICin866_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* TX datarate */LMIC.dndr == IN866_DR_FSK) + +#define LMICbandplan_getInitialDrJoin() (IN866_DR_SF7) + +void LMICin866_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICin866_setBcnRxParams() + +u4_t LMICin866_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICin866_convFreq(ptr) + +void LMICin866_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICin866_initJoinLoop() + +ostime_t LMICin866_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICin866_nextTx(now) + +ostime_t LMICin866_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICin866_nextJoinState() + +void LMICin866_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICin866_initDefaultChannels(join) + +void LMICin866_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICin866_setRx1Params() + +#endif // _lmic_bandplan_in866_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_kr920.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_kr920.h new file mode 100644 index 0000000..2c22f22 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_kr920.h @@ -0,0 +1,91 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_kr920_h_ +# define _lmic_kr920_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (kr920); 0 --> not valid dr. +uint8_t LMICkr920_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICkr920_maxFrameLen(dr) + +int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICkr920_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICkr920_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICkr920_dr2hsym(dr) + + +// TODO(tmm@mcci.com) this looks bogus compared to current 1.02 regional +// spec. https://github.com/mcci-catena/arduino-lmic/issues/18 +static inline int +LMICkr920_isValidBeacon1(const uint8_t *d) { + return d[OFF_BCN_CRC1] != (u1_t)os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICkr920_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* always false */ 0) + +#define LMICbandplan_getInitialDrJoin() (KR920_DR_SF7) + +void LMICkr920_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICkr920_setBcnRxParams() + +u4_t LMICkr920_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICkr920_convFreq(ptr) + +void LMICkr920_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICkr920_initJoinLoop() + +ostime_t LMICkr920_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICkr920_nextTx(now) + +ostime_t LMICkr920_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICkr920_nextJoinState() + +void LMICkr920_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICkr920_initDefaultChannels(join) + +void LMICkr920_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICkr920_setRx1Params() + +#undef LMICbandplan_updateTx +void LMICkr920_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(t) LMICkr920_updateTx(t) + +#endif // _lmic_kr920_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_us915.h b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_us915.h new file mode 100644 index 0000000..e08a795 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_bandplan_us915.h @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_us915_h_ +# define _lmic_bandplan_us915_h_ + +// preconditions for lmic_us_like.h +#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR4) +#define LMICuslike_getJoin125kHzDR() (LORAWAN_DR0) + +#ifndef _lmic_us_like_h_ +# include "lmic_us_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (us915); 0 --> not valid dr. +uint8_t LMICus915_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICus915_maxFrameLen(dr) + +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICus915_pow2dbm(mcmd_ladr_p1) + +ostime_t LMICus915_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICus915_dr2hsym(dr) + + +#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR0) + +void LMICus915_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICus915_setBcnRxParams() + +u4_t LMICus915_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICus915_convFreq(ptr) + +void LMICus915_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICus915_initJoinLoop() + +void LMICus915_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICus915_setRx1Params() + +void LMICus915_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICus915_updateTx(txbeg) + +#endif // _lmic_bandplan_us915_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_compat.h b/libraries/arduino-lmic-master/src/lmic/lmic_compat.h new file mode 100644 index 0000000..96dca49 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_compat.h @@ -0,0 +1,72 @@ +/* + +Module: lmic_compat.h + +Function: + Symbols that are defined for backward compatibility + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation January 2020 + +Description: + This include file centralizes backwards compatibility + definitions. The idea is to centralize the decision, + so it's clear as to what's deprecated. + +*/ + +#ifndef _lmic_compat_h_ /* prevent multiple includes */ +#define _lmic_compat_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#ifndef ARDUINO_LMIC_VERSION +# error "This file is normally included from lmic.h, not stand alone" +#endif + +#define LMIC_DEPRECATE(m) _Pragma(#m) + +#if ! defined(LMIC_REGION_au921) && ARDUINO_LMIC_VERSION < ARDUINO_LMIC_VERSION_CALC(5,0,0,0) +# define LMIC_REGION_au921 LMIC_DEPRECATE(GCC warning "LMIC_REGION_au921 is deprecated, EOL at V5, use LMIC_REGION_au915") \ + LMIC_REGION_au915 + +// Frequency plan symbols +# define AU921_DR_SF12 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF12 +# define AU921_DR_SF11 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF11 +# define AU921_DR_SF10 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF10 +# define AU921_DR_SF9 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF9 +# define AU921_DR_SF8 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8 +# define AU921_DR_SF7 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF7 +# define AU921_DR_SF8C LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8C +# define AU921_DR_NONE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_NONE +# define AU921_DR_SF12CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF12CR +# define AU921_DR_SF11CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF11CR +# define AU921_DR_SF10CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF10CR +# define AU921_DR_SF9CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF9CR +# define AU921_DR_SF8CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8CR +# define AU921_DR_SF7CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF7CR +# define AU921_125kHz_UPFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_125kHz_UPFBASE +# define AU921_125kHz_UPFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_125kHz_UPFSTEP +# define AU921_500kHz_UPFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_UPFBASE +# define AU921_500kHz_UPFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_UPFSTEP +# define AU921_500kHz_DNFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_DNFBASE +# define AU921_500kHz_DNFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_DNFSTEP +# define AU921_FREQ_MIN LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_FREQ_MIN +# define AU921_FREQ_MAX LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_FREQ_MAX +# define AU921_TX_EIRP_MAX_DBM LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_TX_EIRP_MAX_DBM +# define AU921_INITIAL_TxParam_UplinkDwellTime LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_INITIAL_TxParam_UplinkDwellTime +# define AU921_UPLINK_DWELL_TIME_osticks LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_UPLINK_DWELL_TIME_osticks +# define DR_PAGE_AU921 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") DR_PAGE_AU915 +# define AU921_LMIC_REGION_EIRP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_LMIC_REGION_EIRP +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _lmic_compat_h_ */ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_compliance.c b/libraries/arduino-lmic-master/src/lmic/lmic_compliance.c new file mode 100644 index 0000000..d1356b9 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_compliance.c @@ -0,0 +1,771 @@ +/* + +Module: lmic_compliance.c + +Function: + Implementation of the compliance engine. + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation March 2019 + +Description: + See function descriptions. + +*/ + +#include "lmic.h" +#include "lmic_compliance.h" +#include "lorawan_spec_compliance.h" +#include +#include + +#if defined(LMIC_PRINTF_TO) +# include +# define LMIC_COMPLIANCE_PRINTF(f, ...) printf(f, ## __VA_ARGS__) +#else +# define LMIC_COMPLIANCE_PRINTF(f, ...) do { ; } while (0) +#endif + +/****************************************************************************\ +| +| Manifest constants and local declarations. +| +\****************************************************************************/ + +static void acEnterActiveMode(void); +static void acExitActiveMode(void); +static void acSendUplink(void); +static void acSetTimer(ostime_t); +static void acSendUplinkBuffer(void); +static void evActivate(void); +static void evDeactivate(void); +static void evJoinCommand(void); +static void evMessage(const uint8_t *pMessage, size_t nMessage); +static lmic_compliance_fsmstate_t fsmDispatch(lmic_compliance_fsmstate_t, bool); +static void fsmEval(void); +static void fsmEvalDeferred(void); +static osjobcbfn_t fsmJobCb; +static bool isActivateMessage(const uint8_t *pMessage, size_t nMessage); +static void evEchoCommand(const uint8_t *pMessage, size_t nMessage); +static lmic_event_cb_t lmicEventCb; +static lmic_txmessage_cb_t sendUplinkCompleteCb; +static osjobcbfn_t timerExpiredCb; + +/* these are declared global so the optimizer can chuck them without warnings */ +const char *LMICcompliance_txSuccessToString(int fSuccess); +const char * LMICcompliance_fsmstate_getName(lmic_compliance_fsmstate_t state); + +/****************************************************************************\ +| +| Read-only data. +| +\****************************************************************************/ + +/****************************************************************************\ +| +| Variables. +| +\****************************************************************************/ + +lmic_compliance_t LMIC_Compliance; + +/* + +Name: LMIC_complianceRxMessage() + +Function: + Add compliance-awareness to LMIC applications by filtering messages. + +Definition: + lmic_compliance_rx_action_t LMIC_complianceRxMessage( + u1_t port, + const u1_t *pMessage, + size_t nMessage + ); + +Description: + Clients who want to handle the LoRaWAN compliance protocol on + port 224 should call this routine each time a downlink message is + received. This function will update the internal compliance state, + and return an appropriate action to the user. + + If the result is `LMIC_COMPLIANCE_RX_ACTION_PROCESS`, then the client should + process the message as usual. Otherwise, the client should discard the + message. The other values further allow the client to track entry into, + and exit from, compliance state. `LMIC_COMPLIANCE_RX_ACTION_START` signals + entry into compliance state; `LMIC_COMPLIANCE_RX_ACTION_END` signals exit + from compliance state; and `LMIC_COMPLIANCE_RX_ACTION_IGNORE` indicates + a mesage that should be discarded while already in compliance + state. + +Returns: + See description. + +*/ + +lmic_compliance_rx_action_t +LMIC_complianceRxMessage( + uint8_t port, + const uint8_t *pMessage, + size_t nMessage +) { + lmic_compliance_state_t const complianceState = LMIC_Compliance.state; + + // update the counter used by the status message. + ++LMIC_Compliance.downlinkCount; + + // filter normal messages. + if (port != LORAWAN_PORT_COMPLIANCE) { + return lmic_compliance_state_IsActive(complianceState) + ? LMIC_COMPLIANCE_RX_ACTION_PROCESS + : LMIC_COMPLIANCE_RX_ACTION_IGNORE + ; + } + + // it's a message to port 224. + // if we're not active, ignore everything but activation messages + if (! lmic_compliance_state_IsActive(complianceState)) { + if (isActivateMessage(pMessage, nMessage)) { + evActivate(); + } // else ignore. + } else { + evMessage(pMessage, nMessage); + } + if (lmic_compliance_state_IsActive(complianceState) == lmic_compliance_state_IsActive(LMIC_Compliance.state)) + return LMIC_COMPLIANCE_RX_ACTION_IGNORE; + else if (! lmic_compliance_state_IsActive(complianceState)) + return LMIC_COMPLIANCE_RX_ACTION_START; + else + return LMIC_COMPLIANCE_RX_ACTION_END; +} + +/* + +Name: isActivateMessage() + +Function: + See whether a message is a LoRaWAN activate test mode message. + +Definition: + static bool isActivateMessage( + const uint8_t *pMessage, + size_t nMessage + ); + +Description: + The message body is compared to an activate message (per the + LoRa Alliance End Device Certification spec). + +Returns: + The result is `true` if the message is an activation message; + it's `false` otherwise. + +*/ + +static bool +isActivateMessage( + const uint8_t *pMessage, + size_t nMessage +) { + const uint8_t body[LORAWAN_COMPLIANCE_CMD_ACTIVATE_LEN] = { + LORAWAN_COMPLIANCE_CMD_ACTIVATE, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC, + }; + + if (nMessage != sizeof(body)) + return false; + + if (memcmp(pMessage, body, sizeof(body)) == 0) + return true; + else + return false; +} + +/* + +Name: evActivate() + +Function: + Report an activation event to the finite state machine. + +Definition: + void evActivate(void); + +Description: + We report an activation event, and re-evaluate the FSM. + +Returns: + No explicit result. + +*/ + +static void evActivate(void) { + if (! lmic_compliance_state_IsActive(LMIC_Compliance.state)) { + LMIC_Compliance.downlinkCount = 0; + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_ACTIVATE; + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_ACTIVATING; + + LMIC_Compliance.saveEvent.pEventCb = LMIC.client.eventCb; + LMIC_Compliance.saveEvent.pUserData = LMIC.client.eventUserData; + +#if CFG_LMIC_EU_like + band_t *b = LMIC.bands; + lmic_compliance_band_t *b_save = LMIC_Compliance.saveBands; + + for (; b < &LMIC.bands[MAX_BANDS]; ++b, ++b_save) { + b_save->txcap = b->txcap; + b->txcap = 1; + b->avail = os_getTime(); + } +#endif // CFG_LMIC_EU_like + + LMIC_registerEventCb(lmicEventCb, NULL); + + fsmEvalDeferred(); + } else { + LMIC_COMPLIANCE_PRINTF("Redundant ActivateTM message ignored.\n"); + } +} + +/* + +Name: evMessage() + +Function: + Process an inbound message while active. + +Definition: + void evMessage(const uint8_t *pMessage, size_t nMessage); + +Description: + The event is parsed, and the appropriate event(s) are sent into + the finite state machine. Note that because of the way the LMIC + works, we can assume that no uplink event is pending; so it's safe + to launch a send from here. + +Returns: + No explicit result. + +*/ + +static void evMessage( + const uint8_t *pMessage, + size_t nMessage +) { + if (nMessage == 0) + return; + + const uint8_t cmd = pMessage[0]; + switch (cmd) { + case LORAWAN_COMPLIANCE_CMD_DEACTIVATE: { + evDeactivate(); + break; + } + case LORAWAN_COMPLIANCE_CMD_ACTIVATE: { + if (isActivateMessage(pMessage, nMessage)) + evActivate(); + break; + } + case LORAWAN_COMPLIANCE_CMD_SET_CONFIRM: { + LMIC_Compliance.fsmFlags |= LMIC_COMPLIANCE_FSM_CONFIRM; + break; + } + case LORAWAN_COMPLIANCE_CMD_SET_UNCONFIRM: { + LMIC_Compliance.fsmFlags &= ~LMIC_COMPLIANCE_FSM_CONFIRM; + break; + } + case LORAWAN_COMPLIANCE_CMD_ECHO: { + evEchoCommand(pMessage, nMessage); + break; + } + case LORAWAN_COMPLIANCE_CMD_LINK: { + // not clear what this request does. + break; + } + case LORAWAN_COMPLIANCE_CMD_JOIN: { + evJoinCommand(); + break; + } + default: + break; + } +} + +/* + +Name: evDeactivate() + +Function: + Report an deactivation event to the finite state machine. + +Definition: + void evDectivate(void); + +Description: + We report a deactivation event, and re-evaluate the FSM. + We also set a flag so that we're return the appropriate + status from the compliance entry point to the real + application. + +Returns: + No explicit result. + +*/ + +static void evDeactivate(void) { + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_DEACTIVATE; + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_STOPPING; + + // restore user's event handler. + LMIC_registerEventCb(LMIC_Compliance.saveEvent.pEventCb, LMIC_Compliance.saveEvent.pUserData); + + // restore band settings +#if CFG_LMIC_EU_like + band_t *b = LMIC.bands; + lmic_compliance_band_t const *b_save = LMIC_Compliance.saveBands; + + for (; b < &LMIC.bands[MAX_BANDS]; ++b, ++b_save) { + b->txcap = b_save->txcap; + } +#endif // CFG_LMIC_EU_like + + fsmEvalDeferred(); +} + +/* + +Name: evJoinCommand() + +Function: + Report that a join has been commanded. + +Definition: + void evJoinCommand(void); + +Description: + We unjoin from the network, and then report a deactivation + of test mode. That will get us out of test mode and back + to the compliance app. The next message send will trigger + a join. + +Returns: + No explicit result. + +*/ + +static void evJoinCommand( + void +) { + LMIC_unjoin(); + evDeactivate(); +} + +/* + +Name: evEchoCommand() + +Function: + Format and transmit the response to an echo downlink (aka echo request). + +Definition: + void evEchoCommand( + const uint8_t *pMessage, + size_t nMessage + ); + +Description: + The echo response is formatted and transmitted. Since we just received + a downlink, it's always safe to do this. + +Returns: + No explicit result. + +*/ + +static void evEchoCommand( + const uint8_t *pMessage, + size_t nMessage +) { + uint8_t *pResponse; + + if (nMessage > sizeof(LMIC_Compliance.uplinkMessage)) + return; + + // create the echo message. + pResponse = LMIC_Compliance.uplinkMessage; + + // copy the command byte unchanged. + *pResponse++ = *pMessage++; + --nMessage; + + // each byte in the body has to be incremented by one. + for (; nMessage > 0; --nMessage) { + *pResponse++ = (uint8_t)(*pMessage++ + 1); + } + + // now that the message is formatted, tell the fsm to send it; + // need to use a separate job. + LMIC_Compliance.uplinkSize = (uint8_t) (pResponse - LMIC_Compliance.uplinkMessage); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_ECHO_REQUEST; + fsmEvalDeferred(); +} + + +/* + +Name: fsmEval() + +Function: + Evaluate the FSM, preventing recursion. + +Definition: + void fsmEval(void); + +Description: + We check for a nested call to evaluate the FSM; + if detected, the processing is deferred until the + current evaluation completes. Otherwise, we start + a new FSM evaluation, which proceeds until the FSM + returns a "no-change" result. + +Returns: + No explicit result. + +*/ + +const char * LMICcompliance_fsmstate_getName(lmic_compliance_fsmstate_t state) { + const char * const names[] = { LMIC_COMPLIANCE_FSMSTATE__NAMES }; + + if ((unsigned) state >= sizeof(names)/sizeof(names[0])) + return "<>"; + else + return names[state]; +} + +static void fsmEvalDeferred(void) { + os_setCallback(&LMIC_Compliance.fsmJob, fsmJobCb); +} + +static void fsmJobCb(osjob_t *j) { + LMIC_API_PARAMETER(j); + fsmEval(); +} + +static void fsmEval(void) { + bool fNewState; + + // check for reentry. + do { + lmic_compliance_fsmflags_t const fsmFlags = LMIC_Compliance.fsmFlags; + + if (fsmFlags & LMIC_COMPLIANCE_FSM_ACTIVE) { + LMIC_Compliance.fsmFlags = fsmFlags | LMIC_COMPLIANCE_FSM_REENTERED; + return; + } + + // record that we're active + LMIC_Compliance.fsmFlags = fsmFlags | LMIC_COMPLIANCE_FSM_ACTIVE; + } while (0); + + // evaluate and change state + fNewState = false; + for (;;) { + lmic_compliance_fsmstate_t const oldState = LMIC_Compliance.fsmState; + lmic_compliance_fsmstate_t newState; + + newState = fsmDispatch(oldState, fNewState); + + if (newState == LMIC_COMPLIANCE_FSMSTATE_NOCHANGE) { + lmic_compliance_fsmflags_t const fsmFlags = LMIC_Compliance.fsmFlags; + + if ((fsmFlags & LMIC_COMPLIANCE_FSM_REENTERED) == 0) { + // not reentered, no change: get out. + LMIC_Compliance.fsmFlags = fsmFlags & ~LMIC_COMPLIANCE_FSM_ACTIVE; + return; + } else { + // reentered. reset reentered flag and keep going. + LMIC_Compliance.fsmFlags = fsmFlags & ~LMIC_COMPLIANCE_FSM_REENTERED; + fNewState = false; + } + } else { + // state change! + LMIC_COMPLIANCE_PRINTF("%s: change state %s(%u) => %s(%u)\n", + __func__, + LMICcompliance_fsmstate_getName(oldState), (unsigned) oldState, + LMICcompliance_fsmstate_getName(newState), (unsigned) newState + ); + fNewState = true; + LMIC_Compliance.fsmState = newState; + } + } +} + +/* + +Name: fsmDispatch() + +Function: + Dispatch to the appropriate event handler. + +Definition: + lmic_compliance_fsmstate_t fsmDispatch( + lmic_compliance_fsmstate_t state, + bool fEntry + ); + +Description: + This function is called by the evalutator as needed. `state` + is set to the current state of the FSM, and `fEntry` is + true if and only if this state has just been entered via a + transition arrow. (Might be a transition to self.) + +Returns: + This function returns LMIC_COMPLIANCE_FSMSTATE_NOCHANGE if + the FSM is to remain in this state until an event occurs. + Otherwise it returns the new state. + +*/ + +static inline lmic_compliance_eventflags_t +eventflags_TestAndClear(lmic_compliance_eventflags_t flag) { + const lmic_compliance_eventflags_t old = LMIC_Compliance.eventflags; + const lmic_compliance_eventflags_t result = old & flag; + + if (result != 0) + LMIC_Compliance.eventflags = old ^ result; + + return result; +} + +static lmic_compliance_fsmstate_t +fsmDispatch( + lmic_compliance_fsmstate_t state, + bool fEntry +) { + lmic_compliance_fsmstate_t newState; + + // currently, this is a stub. + newState = LMIC_COMPLIANCE_FSMSTATE_NOCHANGE; + + switch (state) { + case LMIC_COMPLIANCE_FSMSTATE_INITIAL: { + newState = LMIC_COMPLIANCE_FSMSTATE_INACTIVE; + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_INACTIVE: { + if (fEntry) { + acExitActiveMode(); + } + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_ACTIVATE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_ACTIVE; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_ACTIVE: { + if (fEntry) { + acEnterActiveMode(); + acSetTimer(sec2osticks(1)); + } + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) { + newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_TXBUSY: { + if (fEntry) { + acSetTimer(sec2osticks(1)); + } + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) { + newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_TESTMODE: { + if (LMIC.opmode & OP_TXDATA) { + // go back and wait some more. + newState = LMIC_COMPLIANCE_FSMSTATE_TXBUSY; + } + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_DEACTIVATE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_INACTIVE; + } else if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_ECHO_REQUEST)) { + newState = LMIC_COMPLIANCE_FSMSTATE_ECHOING; + } else { + newState = LMIC_COMPLIANCE_FSMSTATE_REPORTING; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_ECHOING: { + if (fEntry) + acSendUplinkBuffer(); + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_RECOVERY; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_REPORTING: { + if (fEntry) + acSendUplink(); + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_RECOVERY; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_RECOVERY: { + if (fEntry) { + if (LMIC_Compliance.eventflags & (LMIC_COMPLIANCE_EVENT_DEACTIVATE | + LMIC_COMPLIANCE_EVENT_ECHO_REQUEST)) { + acSetTimer(sec2osticks(1)); + } else { + acSetTimer(sec2osticks(5)); + } + } + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) { + newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE; + } + break; + } + + default: { + break; + } + } + + return newState; +} + +static void acEnterActiveMode(void) { + // indicate to the outer world that we're active. + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_ACTIVE; +} + +void acSetTimer(ostime_t delay) { + os_setTimedCallback(&LMIC_Compliance.timerJob, os_getTime() + delay, timerExpiredCb); +} + +static void timerExpiredCb(osjob_t *j) { + LMIC_API_PARAMETER(j); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED; + fsmEval(); +} + +static void lmicEventCb( + void *pUserData, + ev_t ev +) { + LMIC_API_PARAMETER(pUserData); + + // pass to user handler + if (LMIC_Compliance.saveEvent.pEventCb) { + LMIC_Compliance.saveEvent.pEventCb( + LMIC_Compliance.saveEvent.pUserData, ev + ); + } + + // if it's a EV_JOINED, or a TXCMOMPLETE, we should tell the FSM. + if ((UINT32_C(1) << ev) & (EV_JOINED | EV_TXCOMPLETE)) { + fsmEvalDeferred(); + } +} + + +static void acExitActiveMode(void) { + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_IDLE; + os_clearCallback(&LMIC_Compliance.timerJob); + LMIC_clrTxData(); +} + + +static void acSendUplink(void) { + uint8_t payload[2]; + uint32_t const downlink = LMIC_Compliance.downlinkCount; + + // build the uplink message + payload[0] = (uint8_t) (downlink >> 8); + payload[1] = (uint8_t) downlink; + + // reset the flags + LMIC_Compliance.eventflags &= ~LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + + // don't try to send if busy; might be sending echo message. + lmic_tx_error_t const eSend = + LMIC_sendWithCallback_strict( + LORAWAN_PORT_COMPLIANCE, + payload, sizeof(payload), + /* confirmed? */ + !! (LMIC_Compliance.fsmFlags & LMIC_COMPLIANCE_FSM_CONFIRM), + sendUplinkCompleteCb, NULL + ); + + if (eSend == LMIC_ERROR_SUCCESS) { + // queued successfully + LMIC_COMPLIANCE_PRINTF( + "lmic_compliance.%s: queued uplink message(%u, %p)\n", + __func__, + (unsigned) downlink & 0xFFFF, + LMIC.client.txMessageCb + ); + } else { + // failed to queue; just skip this cycle. + LMIC_COMPLIANCE_PRINTF( + "lmic_compliance.%s: error(%d) sending uplink message(%u), %u bytes\n", + __func__, + eSend, + (unsigned) downlink & 0xFFFF, + (unsigned) sizeof(payload) + ); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + fsmEval(); + } +} + +static void sendUplinkCompleteCb(void *pUserData, int fSuccess) { + LMIC_API_PARAMETER(pUserData); + LMIC_API_PARAMETER(fSuccess); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + LMIC_COMPLIANCE_PRINTF("%s(%s)\n", __func__, LMICcompliance_txSuccessToString(fSuccess)); + fsmEvalDeferred(); +} + +static void acSendUplinkBuffer(void) { + // send uplink data. + lmic_tx_error_t const eSend = + LMIC_sendWithCallback_strict( + LORAWAN_PORT_COMPLIANCE, + LMIC_Compliance.uplinkMessage, LMIC_Compliance.uplinkSize, + /* confirmed? */ (LMIC_Compliance.fsmFlags & LMIC_COMPLIANCE_FSM_CONFIRM) != 0, + sendUplinkCompleteCb, + NULL); + + if (eSend == LMIC_ERROR_SUCCESS) { + LMIC_COMPLIANCE_PRINTF("%s: queued %u bytes\n", __func__, LMIC_Compliance.uplinkSize); + } else { + LMIC_COMPLIANCE_PRINTF("%s: uplink %u bytes failed (error %d)\n", __func__, LMIC_Compliance.uplinkSize, eSend); + if (eSend == LMIC_ERROR_TX_NOT_FEASIBLE) { + // Reverse the increment of the downlink count. Needed for US compliance. + if (CFG_region == LMIC_REGION_us915) + --LMIC_Compliance.downlinkCount; + } + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + fsmEval(); + } +} + +const char *LMICcompliance_txSuccessToString(int fSuccess) { + return fSuccess ? "ok" : "failed"; +} diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_compliance.h b/libraries/arduino-lmic-master/src/lmic/lmic_compliance.h new file mode 100644 index 0000000..8efe0e8 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_compliance.h @@ -0,0 +1,138 @@ +/* + +Module: lmic_compliance.h + +Function: + Internal header file for compliance-related work. + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation March 2019 + +Description: + This header file allows us to break up the compliance + functions into multiple .c files if we wish. + +*/ + +#ifndef _lmic_compliance_h_ /* prevent multiple includes */ +#define _lmic_compliance_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +#include +#include + +typedef struct lmic_compliance_s lmic_compliance_t; + +// concrete type for the state enumeration for the compliance engine. +typedef uint8_t lmic_compliance_state_t; + +enum lmic_compliance_state_e { + LMIC_COMPLIANCE_STATE_IDLE = 0, // app state + LMIC_COMPLIANCE_STATE_STOPPING = 1, // transitioning back to app + LMIC_COMPLIANCE_STATE_ACTIVATING = 2, // transitioning to compliance state + LMIC_COMPLIANCE_STATE_ACTIVE = 3, // in compliance state +}; + +// return true if a state value indicates that the FSM is active. +static inline bool +lmic_compliance_state_IsActive(lmic_compliance_state_t s) { + return s >= LMIC_COMPLIANCE_STATE_ACTIVATING; +} + +// events from the outside world to the FSM +typedef uint8_t lmic_compliance_eventflags_t; + +enum lmic_compliance_eventflags_e { + LMIC_COMPLIANCE_EVENT_ACTIVATE = 1u << 0, + LMIC_COMPLIANCE_EVENT_DEACTIVATE = 1u << 1, + LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED = 1u << 2, + LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE = 1u << 3, + LMIC_COMPLIANCE_EVENT_ECHO_REQUEST = 1u << 4, +}; + +typedef uint8_t lmic_compliance_fsmflags_t; +enum lmic_compliance_fsmflags_e { + LMIC_COMPLIANCE_FSM_ACTIVE = 1u << 0, + LMIC_COMPLIANCE_FSM_REENTERED = 1u << 1, + LMIC_COMPLIANCE_FSM_CONFIRM = 1u << 2, +}; + +typedef uint8_t lmic_compliance_fsmstate_t; +enum lmic_compliance_fsmstate_e { + LMIC_COMPLIANCE_FSMSTATE_INITIAL = 0, + LMIC_COMPLIANCE_FSMSTATE_NOCHANGE = 1, + LMIC_COMPLIANCE_FSMSTATE_ACTIVE = 2, + LMIC_COMPLIANCE_FSMSTATE_INACTIVE = 3, + LMIC_COMPLIANCE_FSMSTATE_TESTMODE = 4, // sending test uplinks + LMIC_COMPLIANCE_FSMSTATE_ECHOING = 5, + LMIC_COMPLIANCE_FSMSTATE_REPORTING = 6, + LMIC_COMPLIANCE_FSMSTATE_RECOVERY = 7, + LMIC_COMPLIANCE_FSMSTATE_TXBUSY = 8, +}; + +#define LMIC_COMPLIANCE_FSMSTATE__NAMES \ + "INITIAL", "NOCHANGE", "ACTIVE", "INACTIVE", "TESTMODE", \ + "ECHOING", "REPORTING", "RECOVERY", "TXBUSY" + +typedef struct lmic_compliance_eventcb_s lmic_compliance_eventcb_t; +struct lmic_compliance_eventcb_s { + // save the user's event CB while active. + lmic_event_cb_t *pEventCb; + // save the user's event data while active. + void *pUserData; +}; + +// structure for saving band settings during test +typedef struct lmic_compliance_band_s lmic_compliance_band_t; +struct lmic_compliance_band_s { + u2_t txcap; // saved 1/duty cycle +}; + +// the state of the compliance engine. +struct lmic_compliance_s { + // uint64 + // uintptr + osjob_t timerJob; // the job for driving uplinks + osjob_t fsmJob; // job for reevaluating the FSM. + lmic_compliance_eventcb_t saveEvent; // the user's event handler. + + // uint32 + + // uint16 +#if CFG_LMIC_EU_like + lmic_compliance_band_t saveBands[MAX_BANDS]; +#endif // CFG_LMIC_EU_like + + // we are required to maintain a downlink count + // that is reset on join/test entry and incremented for + // each valid test message. + uint16_t downlinkCount; + + // uint8 + + lmic_compliance_state_t state; // current state of compliance engine. + lmic_compliance_eventflags_t eventflags; // incoming events. + lmic_compliance_fsmflags_t fsmFlags; // FSM operational flags + lmic_compliance_fsmstate_t fsmState; // FSM current state + + uint8_t uplinkSize; + uint8_t uplinkMessage[MAX_LEN_PAYLOAD]; +}; + +extern lmic_compliance_t LMIC_Compliance; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _lmic_compliance_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_config_preconditions.h b/libraries/arduino-lmic-master/src/lmic/lmic_config_preconditions.h new file mode 100644 index 0000000..875d71b --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_config_preconditions.h @@ -0,0 +1,225 @@ +/* lmic_config_preconditions.h Fri May 19 2017 23:58:34 tmm */ + +/* + +Module: lmic_config_preconditions.h + +Function: + Preconditions for LMIC configuration. + +Version: + V2.0.0 Sun Aug 06 2017 17:40:44 tmm Edit level 1 + +Copyright notice: + This file copyright (C) 2017 by + + MCCI Corporation + 3520 Krums Corners Road + Ithaca, NY 14850 + + MIT License + + 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. + +Author: + Terry Moore, MCCI Corporation July 2017 + +Revision history: + 2.0.0 Sun Aug 06 2017 17:40:44 tmm + Module created. + +*/ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# define _LMIC_CONFIG_PRECONDITIONS_H_ + +// We need to be able to compile with different options without editing source. +// When building with a more advanced environment, set the following variable: +// ARDUINO_LMIC_PROJECT_CONFIG_H=my_project_config.h +// +// otherwise the lmic_project_config.h from the ../../project_config directory will be used. +#ifndef ARDUINO_LMIC_PROJECT_CONFIG_H +# define ARDUINO_LMIC_PROJECT_CONFIG_H ../../project_config/lmic_project_config.h +#endif + +#define CFG_TEXT_1(x) CFG_TEXT_2(x) +#define CFG_TEXT_2(x) #x + +// constants for comparison +#define LMIC_REGION_eu868 1 +#define LMIC_REGION_us915 2 +#define LMIC_REGION_cn783 3 +#define LMIC_REGION_eu433 4 +#define LMIC_REGION_au915 5 +#define LMIC_REGION_cn490 6 +#define LMIC_REGION_as923 7 +#define LMIC_REGION_kr920 8 +#define LMIC_REGION_in866 9 + +// Some regions have country-specific overrides. For generality, we specify +// country codes using the LMIC_COUNTY_CODE_C() macro These values are chosen +// from the 2-letter domain suffixes standardized by ISO-3166-1 alpha2 (see +// https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). They are therefore +// 16-bit constants. By convention, we use UPPER-CASE letters, thus +// LMIC_COUNTRY_CODE('J', 'P'), not ('j', 'p'). +#define LMIC_COUNTRY_CODE_C(c1, c2) ((c1) * 256 + (c2)) + +// this special code means "no country code defined" +#define LMIC_COUNTRY_CODE_NONE 0 + +// specific countries. Only the ones that are needed by the code are defined. +#define LMIC_COUNTRY_CODE_JP LMIC_COUNTRY_CODE_C('J', 'P') + +// include the file that the user is really supposed to edit. But for really strange +// ports, this can be suppressed +#ifndef ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS +# include CFG_TEXT_1(ARDUINO_LMIC_PROJECT_CONFIG_H) +#endif /* ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS */ + +#if defined(CFG_au921) && !defined(CFG_au915) +# warning "CFG_au921 was deprecated in favour of CFG_au915. Support for CFG_au921 might be removed in the future." +# define CFG_au915 +#endif + +// for backwards compatibility to legacy code, define CFG_au921 if we see CFG_au915. +#if defined(CFG_au915) && !defined(CFG_au921) +# define CFG_au921 +#endif + +// a mask of the supported regions +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define LMIC_REGIONS_SUPPORTED ( \ + (1 << LMIC_REGION_eu868) | \ + (1 << LMIC_REGION_us915) | \ + /* (1 << LMIC_REGION_cn783) | */ \ + /* (1 << LMIC_REGION_eu433) | */ \ + (1 << LMIC_REGION_au915) | \ + /* (1 << LMIC_REGION_cn490) | */ \ + (1 << LMIC_REGION_as923) | \ + (1 << LMIC_REGION_kr920) | \ + (1 << LMIC_REGION_in866) | \ + 0) + +// +// Our input is a -D of one of CFG_eu868, CFG_us915, CFG_as923, CFG_au915, CFG_in866 +// More will be added in the the future. So at this point we create CFG_region with +// following values. These are in order of the sections in the manual. Not all of the +// below are supported yet. +// +// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in +// the below. +// +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +// +# define CFG_LMIC_REGION_MASK \ + ((defined(CFG_eu868) << LMIC_REGION_eu868) | \ + (defined(CFG_us915) << LMIC_REGION_us915) | \ + (defined(CFG_cn783) << LMIC_REGION_cn783) | \ + (defined(CFG_eu433) << LMIC_REGION_eu433) | \ + (defined(CFG_au915) << LMIC_REGION_au915) | \ + (defined(CFG_cn490) << LMIC_REGION_cn490) | \ + (defined(CFG_as923) << LMIC_REGION_as923) | \ + (defined(CFG_kr920) << LMIC_REGION_kr920) | \ + (defined(CFG_in866) << LMIC_REGION_in866) | \ + 0) + +// the selected region. +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#if defined(CFG_eu868) +# define CFG_region LMIC_REGION_eu868 +#elif defined(CFG_us915) +# define CFG_region LMIC_REGION_us915 +#elif defined(CFG_cn783) +# define CFG_region LMIC_REGION_cn783 +#elif defined(CFG_eu433) +# define CFG_region LMIC_REGION_eu433 +#elif defined(CFG_au915) +# define CFG_region LMIC_REGION_au915 +#elif defined(CFG_cn490) +# define CFG_region LMIC_REGION_cn490 +#elif defined(CFG_as923jp) +# define CFG_as923 1 /* CFG_as923jp implies CFG_as923 */ +# define CFG_region LMIC_REGION_as923 +# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP +#elif defined(CFG_as923) +# define CFG_region LMIC_REGION_as923 +#elif defined(CFG_kr920) +# define CFG_region LMIC_REGION_kr920 +#elif defined(CFG_in866) +# define CFG_region LMIC_REGION_in866 +#else +# define CFG_region 0 +#endif + +// a bitmask of EU-like regions -- these are regions which have up to 16 +// channels indidually programmable via downloink. +// +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define CFG_LMIC_EU_like_MASK ( \ + (1 << LMIC_REGION_eu868) | \ + /* (1 << LMIC_REGION_us915) | */ \ + (1 << LMIC_REGION_cn783) | \ + (1 << LMIC_REGION_eu433) | \ + /* (1 << LMIC_REGION_au915) | */ \ + /* (1 << LMIC_REGION_cn490) | */ \ + (1 << LMIC_REGION_as923) | \ + (1 << LMIC_REGION_kr920) | \ + (1 << LMIC_REGION_in866) | \ + 0) + +// a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels +// overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but +// subsets of channels can be selected via masks. +// +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define CFG_LMIC_US_like_MASK ( \ + /* (1 << LMIC_REGION_eu868) | */ \ + (1 << LMIC_REGION_us915) | \ + /* (1 << LMIC_REGION_cn783) | */ \ + /* (1 << LMIC_REGION_eu433) | */ \ + (1 << LMIC_REGION_au915) | \ + /* (1 << LMIC_REGION_cn490) | */ \ + /* (1 << LMIC_REGION_as923) | */ \ + /* (1 << LMIC_REGION_kr920) | */ \ + /* (1 << LMIC_REGION_in866) | */ \ + 0) + +// +// booleans that are true if the configured region is EU-like or US-like. +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +// +#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK)) +#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK)) + +// +// The supported LMIC LoRaWAAN spec versions. These need to be numerically ordered, +// so that we can (for example) compare +// +// LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3. +// +#define LMIC_LORAWAN_SPEC_VERSION_1_0_2 0x01000200u +#define LMIC_LORAWAN_SPEC_VERSION_1_0_3 0x01000300u + +#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_env.h b/libraries/arduino-lmic-master/src/lmic/lmic_env.h new file mode 100644 index 0000000..06793e0 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_env.h @@ -0,0 +1,251 @@ +/* + +Module: lmic_env.h + +Function: + Sets up macros etc. to make things a little easier for portabilty + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation November 2018 + +Description: + This file is an adaptation of MCCI's standard IOCTL framework. + We duplicate a bit of functionality that we might get from other + libraries, so that the LMIC library can continue to stand alone. + +*/ + +#ifndef _lmic_env_h_ /* prevent multiple includes */ +#define _lmic_env_h_ + +/* + +Macro: LMIC_C_ASSERT() + +Function: + Declaration-like macro that will cause a compile error if arg is FALSE. + +Definition: + LMIC_C_ASSERT( + BOOL fErrorIfFalse + ); + +Description: + This macro, if used where an external reference declarataion is + permitted, will either compile cleanly, or will cause a compilation + error. The results of using this macro where a declaration is not + permitted are unspecified. + + This is different from #if !(fErrorIfFalse) / #error in that the + expression is evaluated by the compiler rather than by the pre- + processor. Therefore things like sizeof() can be used. + +Returns: + No explicit result -- either compiles cleanly or causes a compile + error. + +*/ + +#ifndef LMIC_C_ASSERT +# define LMIC_C_ASSERT(e) \ + void LMIC_C_ASSERT__(int LMIC_C_ASSERT_x[(e) ? 1: -1]) +#endif + +/****************************************************************************\ +| +| Define the begin/end declaration tags for C++ co-existance +| +\****************************************************************************/ + +#ifdef __cplusplus +# define LMIC_BEGIN_DECLS extern "C" { +# define LMIC_END_DECLS } +#else +# define LMIC_BEGIN_DECLS /* nothing */ +# define LMIC_END_DECLS /* nothing */ +#endif + +//---------------------------------------------------------------------------- +// Annotations to avoid various "unused" warnings. These must appear as a +// statement in the function body; the macro annotates the variable to quiet +// compiler warnings. The way this is done is compiler-specific, and so these +// definitions are fall-backs, which might be overridden. +// +// Although these are all similar, we don't want extra macro expansions, +// so we define each one explicitly rather than relying on a common macro. +//---------------------------------------------------------------------------- + +// signal that a parameter is intentionally unused. +#ifndef LMIC_UNREFERENCED_PARAMETER +# define LMIC_UNREFERENCED_PARAMETER(v) do { (void) (v); } while (0) +#endif + +// an API parameter is a parameter that is required by an API definition, but +// happens to be unreferenced in this implementation. This is a stronger +// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here +// becuase of an API contract, but we have no use for it in this function. +#ifndef LMIC_API_PARAMETER +# define LMIC_API_PARAMETER(v) do { (void) (v); } while (0) +#endif + +// an intentionally-unreferenced variable. +#ifndef LMIC_UNREFERENCED_VARIABLE +# define LMIC_UNREFERENCED_VARIABLE(v) do { (void) (v); } while (0) +#endif + +// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1, +// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or +// or varables that are only refereneced at the target debug level. + +// Parameter referenced only if debugging at level > 0. +#ifndef LMIC_DEBUG1_PARAMETER +# if LMIC_DEBUG_LEVEL > 0 +# define LMIC_DEBUG1_PARAMETER(v) do { ; } while (0) +# else +# define LMIC_DEBUG1_PARAMETER(v) do { (void) (v); } while (0) +# endif +#endif + +// variable referenced only if debugging at level > 0 +#ifndef LMIC_DEBUG1_VARIABLE +# if LMIC_DEBUG_LEVEL > 0 +# define LMIC_DEBUG1_VARIABLE(v) do { ; } while (0) +# else +# define LMIC_DEBUG1_VARIABLE(v) do { (void) (v); } while (0) +# endif +#endif + +// parameter referenced only if debugging at level > 1 +#ifndef LMIC_DEBUG2_PARAMETER +# if LMIC_DEBUG_LEVEL > 1 +# define LMIC_DEBUG2_PARAMETER(v) do { ; } while (0) +# else +# define LMIC_DEBUG2_PARAMETER(v) do { (void) (v); } while (0) +# endif +#endif + +// variable referenced only if debugging at level > 1 +#ifndef LMIC_DEBUG2_VARIABLE +# if LMIC_DEBUG_LEVEL > 1 +# define LMIC_DEBUG2_VARIABLE(v) do { ; } while (0) +# else +# define LMIC_DEBUG2_VARIABLE(v) do { (void) (v); } while (0) +# endif +#endif + +// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0 +#ifndef LMIC_X_DEBUG_PARAMETER +# if LMIC_X_DEBUG_LEVEL > 0 +# define LMIC_X_DEBUG_PARAMETER(v) do { ; } while (0) +# else +# define LMIC_X_DEBUG_PARAMETER(v) do { (void) (v); } while (0) +# endif +#endif + +// variable referenced only if LMIC_X_DEBUG_LEVEL > 0 +#ifndef LMIC_X_DEBUG_VARIABLE +# if LMIC_X_DEBUG_LEVEL > 0 +# define LMIC_X_DEBUG_VARIABLE(v) do { ; } while (0) +# else +# define LMIC_X_DEBUG_VARIABLE(v) do { (void) (v); } while (0) +# endif +#endif + +// parameter referenced only if EV() macro is enabled (which it never is) +// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and +// this code is really C-99 to its bones. +#ifndef LMIC_EV_PARAMETER +# define LMIC_EV_PARAMETER(v) do { (void) (v); } while (0) +#endif + +// variable referenced only if EV() macro is defined. +#ifndef LMIC_EV_VARIABLE +# define LMIC_EV_VARIABLE(v) do { (void) (v); } while (0) +#endif + + /* + +Macro: LMIC_ABI_STD + +Index: Macro: LMIC_ABI_VARARGS + +Function: + Annotation macros to force a particular binary calling sequence. + +Definition: + #define LMIC_ABI_STD compiler-specific + #define LMIC_ABI_VARARGS compiler-specific + +Description: + These macros are used when declaring a function type, and indicate + that a particular calling sequence is to be used. They are normally + used between the type portion of the function declaration and the + name of the function. For example: + + typedef void LMIC_ABI_STD myCallBack_t(void); + + It's important to use this in libraries on platforms with multiple + calling sequences, because different components can be compiled with + different defaults. + +Returns: + Not applicable. + +*/ + +/* ABI marker for normal (fixed parameter count) functions -- used for function types */ +#ifndef LMIC_ABI_STD +# ifdef _MSC_VER +# define LMIC_ABI_STD __stdcall +# else +# define LMIC_ABI_STD /* nothing */ +# endif +#endif + +/* ABI marker for VARARG functions -- used for function types */ +#ifndef LMIC_ABI_VARARGS +# ifdef _MSC_VER +# define LMIC_ABI_VARARGS __cdecl +# else +# define LMIC_ABI_VARARGS /* nothing */ +# endif +#endif + +/* + +Macro: LMIC_DECLARE_FUNCTION_WEAK() + +Function: + Declare an external function as a weak reference. + +Definition: + #define LMIC_DECLARE_FUNCTION_WEAK(ReturnType, FunctionName, Params) ... + +Description: + This macro generates a weak reference to the specified function. + +Example: + LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e)); + + This saya that onEvent is a weak external reference. When calling + onEvent, you must always first check whether it's supplied: + + if (onEvent != NULL) + onEvent(e); + +Returns: + This macro expands to a declaration, without a trailing semicolon. + +Notes: + This form allows for compilers that use _Pragma(weak, name) instead + of inline attributes. + +*/ + +#define LMIC_DECLARE_FUNCTION_WEAK(a_ReturnType, a_FunctionName, a_Params) \ + a_ReturnType __attribute__((__weak__)) a_FunctionName a_Params + +#endif /* _lmic_env_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_eu868.c b/libraries/arduino-lmic-master/src/lmic/lmic_eu868.c new file mode 100644 index 0000000..f8d51d1 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_eu868.c @@ -0,0 +1,283 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_eu868) +// ================================================================================ +// +// BEG: EU868 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 250+5 +}; + +uint8_t LMICeu868_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 16, 14, 12, 10, 8, 6, 4, 2 +}; + +int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // DR_SF7B + us2osticksRound(80) // FSK -- time for 1/2 byte (unused by LMIC) +}; + +ostime_t LMICeu868_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[6] = { + // Join frequencies and duty cycle limit (0.1%) + EU868_F1 | BAND_MILLI, EU868_F2 | BAND_MILLI, EU868_F3 | BAND_MILLI, + // Default operational frequencies and duty cycle limit (1%) + EU868_F1 | BAND_CENTI, EU868_F2 | BAND_CENTI, EU868_F3 | BAND_CENTI, +}; + +void LMICeu868_initDefaultChannels(bit_t join) { + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + u1_t su = join ? 0 : NUM_DEFAULT_CHANNELS; + for (u1_t fu = 0; fu BAND_AUX) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +// this table is from highest to lowest +static CONST_TABLE(u4_t, bandAssignments)[] = { + 870000000 /* .. and above */ | BAND_MILLI, + 869700000 /* .. 869700000 */ | BAND_CENTI, + 869650000 /* .. 869700000 */ | BAND_MILLI, + 869400000 /* .. 869650000 */ | BAND_DECI, + 868600000 /* .. 869640000 */ | BAND_MILLI, + 865000000 /* .. 868400000 */ | BAND_CENTI, +}; + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + + if (band == -1) { + for (u1_t i = 0; i < LENOF_TABLE(bandAssignments); ++i) { + const u4_t thisFreqBand = TABLE_GET_U4(bandAssignments, i); + const u4_t thisFreq = thisFreqBand & ~3; + if (freq >= thisFreq) { + band = ((u1_t)thisFreqBand & 3); + break; + } + } + + // if we didn't identify a frequency, it's millis. + if (band == -1) { + band = BAND_MILLI; + } + } + + if ((u1_t)band > BAND_AUX) + return 0; + + freq |= band; + + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7) : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICeu868_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX) + freq = 0; + return freq; +} + +ostime_t LMICeu868_nextJoinTime(ostime_t time) { + // is the avail time in the future? + if ((s4_t) (time - LMIC.bands[BAND_MILLI].avail) < 0) + // yes: then wait until then. + time = LMIC.bands[BAND_MILLI].avail; + + return time; +} + +ostime_t LMICeu868_nextTx(ostime_t now) { + u1_t bmap = 0xF; + do { + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + for (u1_t bi = 0; bi<4; bi++) { + if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[band = bi].avail; + } + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return mintime; + } + } + if ((bmap &= ~(1 << band)) == 0) { + // No feasible channel found! + return mintime; + } + } while (1); +} + + +#if !defined(DISABLE_BEACONS) +void LMICeu868_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICeu868_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// set the Rx1 dndr, rps. +void LMICeu868_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + s1_t drOffset; + s1_t candidateDr; + + LMICeulike_setRx1Freq(); + + if ( LMIC.rx1DrOffset <= 5) + drOffset = (s1_t) LMIC.rx1DrOffset; + else + // make a reasonable assumption for unspecified value. + drOffset = 5; + + candidateDr = (s1_t) txdr - drOffset; + if (candidateDr < LORAWAN_DR0) + candidateDr = 0; + else if (candidateDr > LORAWAN_DR7) + candidateDr = LORAWAN_DR7; + + LMIC.dndr = (u1_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void +LMICeu868_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ EU868_TX_EIRP_MAX_DBM); +} + +// +// END: EU868 related stuff +// +// ================================================================================ +#endif \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_eu_like.c b/libraries/arduino-lmic-master/src/lmic/lmic_eu_like.c new file mode 100644 index 0000000..46694e3 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_eu_like.c @@ -0,0 +1,269 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_EU_like + +bit_t LMIC_enableSubBand(u1_t band) { + LMIC_API_PARAMETER(band); + return 0; +} + +bit_t LMIC_disableSubBand(u1_t band) { + LMIC_API_PARAMETER(band); + return 0; +} + +bit_t LMIC_disableChannel(u1_t channel) { + u2_t old_chmap = LMIC.channelMap; + LMIC.channelFreq[channel] = 0; + LMIC.channelDrMap[channel] = 0; + LMIC.channelMap = old_chmap & ~(1 << channel); + return LMIC.channelMap != old_chmap; +} + +// this is a no-op provided for compatibilty +bit_t LMIC_enableChannel(u1_t channel) { + LMIC_API_PARAMETER(channel); + return 0; +} + +// check whether a map operation will work. +// chpage is 0 or 6; 6 turns all on; 0 selects channels 0..15 via mask. +// The spec is unclear as to whether we should veto a channel mask that enables +// a channel that hasn't been configured; we veto it. +bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap) { + switch (chpage) { + case MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT: + // we don't allow any channel to be turned on if its frequency is zero. + for (u1_t chnl = 0; chnltxpow; + band->avail = txbeg + airtime * band->txcap; + if (LMIC.globalDutyRate != 0) + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); +} + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { + u1_t failed = 0; + + // Try each default channel with same DR + // If all fail try next lower datarate + if (++LMIC.txChnl == /* NUM_DEFAULT_CHANNELS */ nDefaultChannels) + LMIC.txChnl = 0; + if ((++LMIC.txCnt % nDefaultChannels) == 0) { + // Lower DR every nth try (having all default channels with same DR) + // + // TODO(tmm@mcci.com) add new DR_REGION_JOIN_MIN instead of LORAWAN_DR0; + // then we can eliminate the LMIC_REGION_as923 below because we'll set + // the failed flag here. This will cause the outer caller to take the + // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() + // + +// TODO(tmm@mcci.com) - see above; please remove regional dependency from this file. +#if CFG_region == LMIC_REGION_as923 + // in the join of AS923 v1.1 or older, only DR2 is used. + // no need to change the DR. + LMIC.datarate = AS923_DR_SF10; + failed = 1; +#else + if (LMIC.datarate == LORAWAN_DR0) + failed = 1; // we have tried all DR - signal EV_JOIN_FAILED + else { + LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate)); + } +#endif + } + // Clear NEXTCHNL because join state engine controls channel hopping + LMIC.opmode &= ~OP_NEXTCHNL; + // Move txend to randomize synchronized concurrent joins. + // Duty cycle is based on txend. + ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = time + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF12:255, SF11:127, .., SF7:8secs + // + : DNW2_SAFETY_ZONE + LMICcore_rndDelay(255 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif // !DISABLE_JOIN + +void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + pStateBuffer->channelFreq, + LMIC.channelFreq, + sizeof(LMIC.channelFreq) + ); + pStateBuffer->channelMap = LMIC.channelMap; +} + +bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + if (memcmp(pStateBuffer->channelFreq, LMIC.channelFreq, sizeof(LMIC.channelFreq)) != 0) + return 1; + return pStateBuffer->channelMap != LMIC.channelMap; +} + +void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelFreq, + pStateBuffer->channelFreq, + sizeof(LMIC.channelFreq) + ); + LMIC.channelMap = pStateBuffer->channelMap; +} + +void LMICeulike_setRx1Freq(void) { +#if !defined(DISABLE_MCMD_DlChannelReq) + uint32_t dlFreq = LMIC.channelDlFreq[LMIC.txChnl]; + if (dlFreq != 0) + LMIC.freq = dlFreq; +#endif // !DISABLE_MCMD_DlChannelReq +} + +// Class A txDone handling for FSK. +void +LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func) { + // one symbol == one bit at 50kHz == 20us. + ostime_t const hsym = us2osticksRound(10); + + // start a little earlier. PRERX_FSK is in bytes; one byte at 50 kHz == 160us + delay -= LMICbandplan_PRERX_FSK * us2osticksRound(160); + + // set LMIC.rxtime and LMIC.rxsyms: + LMIC.rxtime = LMIC.txend + LMICcore_adjustForDrift(delay, hsym, 8 * LMICbandplan_RXLEN_FSK); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - os_getRadioRxRampup(), func); +} + +#endif // CFG_LMIC_EU_like diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_eu_like.h b/libraries/arduino-lmic-master/src/lmic/lmic_eu_like.h new file mode 100644 index 0000000..c9bbeac --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_eu_like.h @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_eu_like_h_ +# define _lmic_eu_like_h_ + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +// make sure we want US-like code +#if !CFG_LMIC_EU_like +# error "lmic not configured for EU-like bandplan" +#endif + +// TODO(tmm@mcci.com): this should come from the lmic.h or lorabase.h file; and +// it's probably affected by the fix to this issue: +// https://github.com/mcci-catena/arduino-lmic/issues/2 +#define DNW2_SAFETY_ZONE ms2osticks(3000) + +// provide a default for LMICbandplan_isValidBeacon1() +static inline int +LMICeulike_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#define LMICbandplan_isValidBeacon1(pFrame) LMICeulike_isValidBeacon1(pFrame) + + +// provide a default for LMICbandplan_isFSK() +#define LMICbandplan_isFSK() (0) + +// provide a default LMICbandplan_txDoneDoFSK() +void LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func); +#define LMICbandplan_txDoneFSK(delay, func) LMICeulike_txDoneFSK(delay, func) + +#define LMICbandplan_joinAcceptChannelClear() LMICbandplan_initDefaultChannels(/* normal, not join */ 0) + +enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2, BAND_AUX = 3 }; + +// there's a CFList on joins for EU-like plans +#define LMICbandplan_hasJoinCFlist() (1) + +#define LMICbandplan_advanceBeaconChannel() \ + do { /* nothing */ } while (0) + +#define LMICbandplan_resetDefaultChannels() \ + do { /* nothing */ } while (0) + +#define LMICbandplan_setSessionInitDefaultChannels() \ + do { LMICbandplan_initDefaultChannels(/* normal, not join */ 0); } while (0) + +bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_canMapChannels(c, m) LMICeulike_canMapChannels(c, m) + +bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_mapChannels(c, m) LMICeulike_mapChannels(c, m) + +void LMICeulike_initJoinLoop(u1_t nDefaultChannels, s1_t adrTxPow); + +void LMICeulike_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(t) LMICeulike_updateTx(t) + +ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels); + +static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) { + return now; +} +#define LMICbandplan_nextJoinTime(now) LMICeulike_nextJoinTime(now) + +#define LMICbandplan_init() \ + do { /* nothing */ } while (0) + +void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_saveAdrState(pState) LMICeulike_saveAdrState(pState) + +bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_compareAdrState(pState) LMICeulike_compareAdrState(pState) + +void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_restoreAdrState(pState) LMICeulike_restoreAdrState(pState) + +// set Rx1 frequency (might be different than uplink). +void LMICeulike_setRx1Freq(void); + +bit_t LMICeulike_isDataRateFeasible(dr_t dr); +#define LMICbandplan_isDataRateFeasible(dr) LMICeulike_isDataRateFeasible(dr) + + +#endif // _lmic_eu_like_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_in866.c b/libraries/arduino-lmic-master/src/lmic/lmic_in866.c new file mode 100644 index 0000000..15916a1 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_in866.c @@ -0,0 +1,247 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_in866) +// ================================================================================ +// +// BEG: IN866 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + ILLEGAL_RPS, // [6] + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), // [7] + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 0, 250+5 +}; + +uint8_t LMICin866_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 +}; + +int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // -- + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICin866_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// All frequencies are marked as BAND_MILLI, and we don't do duty-cycle. But this lets +// us reuse code. +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + IN866_F1 | BAND_MILLI, + IN866_F2 | BAND_MILLI, + IN866_F3 | BAND_MILLI, +}; + +// india ignores join, becuase the channel setup is the same either way. +void LMICin866_initDefaultChannels(bit_t join) { + LMIC_API_PARAMETER(join); + + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; fu BAND_MILLI) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_MILLI; + } else { + if (band > BAND_MILLI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(IN866_DR_SF12, IN866_DR_SF7) : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICin866_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < IN866_FREQ_MIN || freq > IN866_FREQ_MAX) + freq = 0; + return freq; +} + +// return the next time, but also do channel hopping here +// since there's no duty cycle limitation, and no dwell limitation, +// we simply loop through the channels sequentially. +ostime_t LMICin866_nextTx(ostime_t now) { + const u1_t band = BAND_MILLI; + + for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return now; + } + } + } + + // no enabled channel found! just use the last channel. + return now; +} + +#if !defined(DISABLE_BEACONS) +void LMICin866_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICin866_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// set the Rx1 dndr, rps. +void LMICin866_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + s1_t drOffset; + s1_t candidateDr; + + LMICeulike_setRx1Freq(); + + if ( LMIC.rx1DrOffset <= 5) + drOffset = (s1_t) LMIC.rx1DrOffset; + else + drOffset = 5 - (s1_t) LMIC.rx1DrOffset; + + candidateDr = (s1_t) txdr - drOffset; + if (candidateDr < LORAWAN_DR0) + candidateDr = 0; + else if (candidateDr > LORAWAN_DR5) + candidateDr = LORAWAN_DR5; + + LMIC.dndr = (u1_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void +LMICin866_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ IN866_TX_EIRP_MAX_DBM); +} + +// +// END: IN866 related stuff +// +// ================================================================================ +#endif \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_kr920.c b/libraries/arduino-lmic-master/src/lmic/lmic_kr920.c new file mode 100644 index 0000000..9ea1dac --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_kr920.c @@ -0,0 +1,274 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_kr920) +// ================================================================================ +// +// BEG: KR920 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + ILLEGAL_RPS, // [6] +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5 +}; + +uint8_t LMICkr920_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 14, 12, 10, 8, 6, 4, 2, 0 +}; + +int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 +}; + +ostime_t LMICkr920_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// All frequencies are marked as BAND_MILLI, and we don't do duty-cycle. But this lets +// us reuse code. +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + KR920_F1 | BAND_MILLI, + KR920_F2 | BAND_MILLI, + KR920_F3 | BAND_MILLI, +}; + +// korea ignores the join flag, becuase the channel setup is the same either way. +void LMICkr920_initDefaultChannels(bit_t join) { + LMIC_API_PARAMETER(join); + + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; fu BAND_MILLI) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_MILLI; + } else { + if (band > BAND_MILLI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(KR920_DR_SF12, KR920_DR_SF7) : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICkr920_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < KR920_FREQ_MIN || freq > KR920_FREQ_MAX) + freq = 0; + return freq; +} + +// return the next time, but also do channel hopping here +// since there's no duty cycle limitation, and no dwell limitation, +// we simply loop through the channels sequentially. +ostime_t LMICkr920_nextTx(ostime_t now) { + const u1_t band = BAND_MILLI; + + for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return now; + } + } + } + + // no enabled channel found! just use the last channel. + return now; +} + +#if !defined(DISABLE_BEACONS) +void LMICkr920_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICkr920_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// set the Rx1 dndr, rps. +void LMICkr920_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + s1_t drOffset; + s1_t candidateDr; + + LMICeulike_setRx1Freq(); + + if ( LMIC.rx1DrOffset <= 5) + drOffset = (s1_t) LMIC.rx1DrOffset; + else + drOffset = 5 - (s1_t) LMIC.rx1DrOffset; + + candidateDr = (s1_t) txdr - drOffset; + if (candidateDr < LORAWAN_DR0) + candidateDr = 0; + else if (candidateDr > LORAWAN_DR5) + candidateDr = LORAWAN_DR5; + + LMIC.dndr = (u1_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void +LMICkr920_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ KR920_TX_EIRP_MAX_DBM); +} + +void LMICkr920_updateTx(ostime_t txbeg) { + u4_t freq = LMIC.channelFreq[LMIC.txChnl]; + // Update global/band specific duty cycle stats + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + // Update channel/global duty cycle stats + xref2band_t band = &LMIC.bands[freq & 0x3]; + LMIC.freq = freq & ~(u4_t)3; + LMIC.txpow = band->txpow; + if (LMIC.freq <= KR920_FDOWN && LMIC.txpow > KR920_TX_EIRP_MAX_DBM_LOW) { + LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW; + } + band->avail = txbeg + airtime * band->txcap; + if (LMIC.globalDutyRate != 0) + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); +} + +// +// END: KR920 related stuff +// +// ================================================================================ +#endif diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_us915.c b/libraries/arduino-lmic-master/src/lmic/lmic_us915.c new file mode 100644 index 0000000..0d84ac1 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_us915.c @@ -0,0 +1,254 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_us915) +// ================================================================================ +// +// BEG: US915 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, // [-1] + MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [0] + MAKERPS(SF9 , BW125, CR_4_5, 0, 0), // [1] + MAKERPS(SF8 , BW125, CR_4_5, 0, 0), // [2] + MAKERPS(SF7 , BW125, CR_4_5, 0, 0), // [3] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [4] + ILLEGAL_RPS , // [5] + ILLEGAL_RPS , // [6] + ILLEGAL_RPS , // [7] + MAKERPS(SF12, BW500, CR_4_5, 0, 0), // [8] + MAKERPS(SF11, BW500, CR_4_5, 0, 0), // [9] + MAKERPS(SF10, BW500, CR_4_5, 0, 0), // [10] + MAKERPS(SF9 , BW500, CR_4_5, 0, 0), // [11] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [12] + MAKERPS(SF7 , BW500, CR_4_5, 0, 0), // [13] + ILLEGAL_RPS // [14] +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 19+5, 61+5, 133+5, 250+5, 250+5, 0, 0,0, + 61+5, 133+5, 250+5, 250+5, 250+5, 250+5 + }; + +uint8_t LMICus915_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) > + ((LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3) + ? US915_LinkAdrReq_POW_MAX_1_0_2 + : US915_LinkAdrReq_POW_MAX_1_0_3)) + return -128; + else + return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); +} + +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 5), // DR_SF10 DR_SF12CR + us2osticksRound(128 << 4), // DR_SF9 DR_SF11CR + us2osticksRound(128 << 3), // DR_SF8 DR_SF10CR + us2osticksRound(128 << 2), // DR_SF7 DR_SF9CR + us2osticksRound(128 << 1), // DR_SF8C DR_SF8CR + us2osticksRound(128 << 0) // ------ DR_SF7CR +}; + +ostime_t LMICus915_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, (dr) & 7); // map DR_SFnCR -> 0-6 +} + + + +u4_t LMICus915_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < US915_FREQ_MIN || freq > US915_FREQ_MAX) + freq = 0; + return freq; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + LMIC_API_PARAMETER(band); + + if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS) + return 0; // channels 0..71 are hardwired + LMIC.xchFreq[chidx - 72] = freq; + // TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const + LMIC.xchDrMap[chidx - 72] = drmap == 0 ? DR_RANGE_MAP(US915_DR_SF10, US915_DR_SF8C) : drmap; + LMIC.channelMap[chidx >> 4] |= (1 << (chidx & 0xF)); + return 1; +} + +bit_t LMIC_disableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72 + MAX_XCHANNELS) { + if (ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz--; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz--; + } + LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72 + MAX_XCHANNELS) { + if (!ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz++; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz++; + } + LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // enable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_enableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Enable it, too. + result |= LMIC_enableChannel(64 + band); + return result; +} + +bit_t LMIC_disableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // disable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_disableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Disable it, too. + result |= LMIC_disableChannel(64 + band); + return result; +} + +bit_t LMIC_selectSubBand(u1_t band) { + bit_t result = 0; + + ASSERT(band < 8); + for (int b = 0; b<8; ++b) { + if (band == b) + result |= LMIC_enableSubBand(b); + else + result |= LMIC_disableSubBand(b); + } + return result; +} + +void LMICus915_updateTx(ostime_t txbeg) { + u1_t chnl = LMIC.txChnl; + if (chnl < 64) { + LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP; + if (LMIC.activeChannels125khz >= 50) + LMIC.txpow = 30; + else + LMIC.txpow = 21; + } else { + // at 500kHz bandwidth, we're allowed more power. + LMIC.txpow = 26; + if (chnl < 64 + 8) { + LMIC.freq = US915_500kHz_UPFBASE + (chnl - 64)*US915_500kHz_UPFSTEP; + } + else { + ASSERT(chnl < 64 + 8 + MAX_XCHANNELS); + LMIC.freq = LMIC.xchFreq[chnl - 72]; + } + } + + // Update global duty cycle stats + if (LMIC.globalDutyRate != 0) { + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); + } +} + +#if !defined(DISABLE_BEACONS) +void LMICus915_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +// set the Rx1 dndr, rps. +void LMICus915_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + u1_t candidateDr; + LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; + if ( /* TX datarate */txdr < LORAWAN_DR4) + candidateDr = txdr + 10 - LMIC.rx1DrOffset; + else + candidateDr = LORAWAN_DR13 - LMIC.rx1DrOffset; + + if (candidateDr < LORAWAN_DR8) + candidateDr = LORAWAN_DR8; + else if (candidateDr > LORAWAN_DR13) + candidateDr = LORAWAN_DR13; + + LMIC.dndr = candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void LMICus915_initJoinLoop(void) { + LMICuslike_initJoinLoop(); + + // initialize the adrTxPower. + LMIC.adrTxPow = 20; // dBm +} + +// +// END: US915 related stuff +// +// ================================================================================ +#endif diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_us_like.c b/libraries/arduino-lmic-master/src/lmic/lmic_us_like.c new file mode 100644 index 0000000..d1383bd --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_us_like.c @@ -0,0 +1,339 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_US_like + +#ifndef LMICuslike_getFirst500kHzDR +# error "LMICuslike_getFirst500kHzDR() not defined by bandplan" +#endif + +static void setNextChannel(uint start, uint end, uint count) { + ASSERT(count>0); + ASSERT(start>= 1) { + if (chmap & 1) { + LMIC_enableSubBand(subband); + } else { + LMIC_disableSubBand(subband); + } + } + } else { + u1_t base, top; + + if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { + // operate on channels 0..15, 16..31, 32..47, 48..63 + // note that the chpage hasn't been shifted right, so + // it's really the base. + base = chpage; + top = base + 16; + if (base == 64) { + top = 72; + } + } else /* if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON || + chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) */ { + u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON; + + // enable or disable all 125kHz channels + for (u1_t chnl = 0; chnl < 64; ++chnl) { + if (en125) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + + // then apply mask to top 8 channels. + base = 64; + top = 72; + } + + // apply chmap to channels in [base..top-1]. + // Use enable/disable channel to keep activeChannel counts in sync. + for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) { + if (chmap & 0x0001) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + } + + LMICOS_logEventUint32("LMICuslike_mapChannels", ((u4_t)LMIC.activeChannels125khz << 16u)|(LMIC.activeChannels500khz << 0u)); + return (LMIC.activeChannels125khz > 0) || (LMIC.activeChannels500khz > 0); +} + +// US does not have duty cycling - return now as earliest TX time +// but also do the channel hopping dance. +ostime_t LMICuslike_nextTx(ostime_t now) { + // TODO(tmm@mcci.com): use a static const for US-like + if (LMIC.datarate >= LMICuslike_getFirst500kHzDR()) { // 500kHz + if (LMIC.activeChannels500khz > 0) { + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + } else if (LMIC.activeChannels125khz > 0) { + LMIC.datarate = lowerDR(LMICuslike_getFirst500kHzDR(), 1); + setNextChannel(0, 64, LMIC.activeChannels125khz); + LMICOS_logEvent("LMICuslike_nextTx: no 500k, choose 125k"); + } else { + LMICOS_logEvent("LMICuslike_nextTx: no channels at all (500)"); + } + } + else { // 125kHz + if (LMIC.activeChannels125khz > 0) { + setNextChannel(0, 64, LMIC.activeChannels125khz); + } else if (LMIC.activeChannels500khz > 0) { + LMIC.datarate = LMICuslike_getFirst500kHzDR(); + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + LMICOS_logEvent("LMICuslike_nextTx: no 125k, choose 500k"); + } else { + LMICOS_logEvent("LMICuslike_nextTx: no channels at all (125)"); + } + } + return now; +} + +bit_t LMICuslike_isDataRateFeasible(dr_t dr) { + if (dr >= LMICuslike_getFirst500kHzDR()) { // 500kHz + return LMIC.activeChannels500khz > 0; + } else { + return LMIC.activeChannels125khz > 6; + } +} + +#if !defined(DISABLE_JOIN) +void LMICuslike_initJoinLoop(void) { + // set an initial condition so that setNextChannel()'s preconds are met + LMIC.txChnl = 0; + + // then chose a new channel. This gives us a random first channel for + // the join. Minor nit: if channel 0 is enabled, it will never be used + // as the first join channel. The join logic uses the current txChnl, + // then changes after the rx window expires; so we need to set a valid + // starting point. + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // make sure LMIC.txend is valid. + LMIC.txend = os_getTime(); + ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0); + + // make sure the datarate is set to DR2 per LoRaWAN regional reqts V1.0.2, + // section 2.*.2 + LMICcore_setDrJoin(DRCHG_SET, LMICbandplan_getInitialDrJoin()); + + // TODO(tmm@mcci.com) need to implement the transmit randomization and + // duty cycle restrictions from LoRaWAN V1.0.2 section 7. +} +#endif // !DISABLE_JOIN + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICuslike_nextJoinState(void) { + // Try the following: + // DR0 (SF10) on a random channel 0..63 + // (honoring enable mask) + // DR4 (SF8C) on a random 500 kHz channel 64..71 + // (always determined by + // previously selected + // 125 kHz channel) + // + u1_t failed = 0; + // TODO(tmm@mcci.com) parameterize for US-like + if (LMIC.datarate != LMICuslike_getFirst500kHzDR()) { + // assume that 500 kHz equiv of last 125 kHz channel + // is also enabled, and use it next. + LMIC.txChnl = 64 + (LMIC.txChnl >> 3); + LMICcore_setDrJoin(DRCHG_SET, LMICuslike_getFirst500kHzDR()); + } + else { + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // TODO(tmm@mcci.com) parameterize + s1_t dr = LMICuslike_getJoin125kHzDR(); + if ((++LMIC.txCnt & 0x7) == 0) { + failed = 1; // All DR exhausted - signal failed + } + LMICcore_setDrJoin(DRCHG_SET, dr); + } + LMIC.opmode &= ~OP_NEXTCHNL; + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = os_getTime() + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF10:16, SF9=8,..SF8C:1secs + : LMICcore_rndDelay(16 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif + +void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + pStateBuffer->channelMap, + LMIC.channelMap, + sizeof(LMIC.channelMap) + ); + pStateBuffer->activeChannels125khz = LMIC.activeChannels125khz; + pStateBuffer->activeChannels500khz = LMIC.activeChannels500khz; +} + +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelMap, + pStateBuffer->channelMap, + sizeof(LMIC.channelMap) + ); + LMIC.activeChannels125khz = pStateBuffer->activeChannels125khz; + LMIC.activeChannels500khz = pStateBuffer->activeChannels500khz; +} + + +bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + return memcmp(pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap)) != 0; +} + +#endif // CFG_LMIC_US_like diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_us_like.h b/libraries/arduino-lmic-master/src/lmic/lmic_us_like.h new file mode 100644 index 0000000..6281236 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_us_like.h @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_us_like_h_ +# define _lmic_us_like_h_ + +// make sure we want US-like code +#if !CFG_LMIC_US_like +# error "lmic not configured for us-like bandplan" +#endif + +// TODO(tmm@mcci.com): this should come from the lmic.h or lorabase.h file; and +// it's probably affected by the fix to this issue: +// https://github.com/mcci-catena/arduino-lmic/issues/2 +#define DNW2_SAFETY_ZONE ms2osticks(750) + +#define IS_CHANNEL_125khz(c) (c<64) +#define IS_CHANNEL_500khz(c) (c>=64 && c<72) +#define ENABLED_CHANNEL(chnl) ((LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0x0F))) != 0) + +// library functions: called from bandplan +void LMICuslike_initJoinLoop(void); + +// provide the isValidBeacon1 function -- int for bool. +static inline int +LMICuslike_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#define LMICbandplan_isValidBeacon1(pFrame) LMICuslike_isValidBeacon1(pFrame) + +// provide a default for LMICbandplan_isFSK() +#define LMICbandplan_isFSK() (0) + +// provide a default LMICbandplan_txDoneFSK() +#define LMICbandplan_txDoneFSK(delay, func) do { } while (0) + +// provide a default LMICbandplan_joinAcceptChannelClear() +#define LMICbandplan_joinAcceptChannelClear() do { } while (0) + +// no CFList on joins for US-like plans +#define LMICbandplan_hasJoinCFlist() (0) + +#define LMICbandplan_advanceBeaconChannel() \ + do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0) + +// TODO(tmm@mcci.com): decide whether we want to do this on every +// reset or just restore the last sub-band selected by the user. +#define LMICbandplan_resetDefaultChannels() \ + LMICbandplan_initDefaultChannels(/* normal */ 0) + +void LMICuslike_initDefaultChannels(bit_t fJoin); +#define LMICbandplan_initDefaultChannels(fJoin) LMICuslike_initDefaultChannels(fJoin) + +#define LMICbandplan_setSessionInitDefaultChannels() \ + do { /* nothing */} while (0) + +bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_canMapChannels(chpage, chmap) LMICuslike_canMapChannels(chpage, chmap) + +bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_mapChannels(chpage, chmap) LMICuslike_mapChannels(chpage, chmap) + +ostime_t LMICuslike_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICuslike_nextTx(now) + +ostime_t LMICuslike_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICuslike_nextJoinState(); + +static inline ostime_t LMICuslike_nextJoinTime(ostime_t now) { + return now; +} +#define LMICbandplan_nextJoinTime(now) LMICuslike_nextJoinTime(now) + +#define LMICbandplan_init() \ + do { /* nothing */ } while (0) + +void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_saveAdrState(pState) LMICuslike_saveAdrState(pState) + +bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_compareAdrState(pState) LMICuslike_compareAdrState(pState) + +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_restoreAdrState(pState) LMICuslike_restoreAdrState(pState) + +bit_t LMICuslike_isDataRateFeasible(dr_t dr); +#define LMICbandplan_isDataRateFeasible(dr) LMICuslike_isDataRateFeasible(dr) + +#endif // _lmic_us_like_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_util.c b/libraries/arduino-lmic-master/src/lmic/lmic_util.c new file mode 100644 index 0000000..0d56c25 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_util.c @@ -0,0 +1,335 @@ +/* + +Module: lmic_util.c + +Function: + Encoding and decoding utilities for LMIC clients. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI September 2019 + +*/ + +#include "lmic_util.h" + +#include + +/* + +Name: LMIC_f2sflt16() + +Function: + Encode a floating point number into a uint16_t. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range (-1.0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15: sign + bits 14..11: biased exponent + bits 10..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0xFFFF for negative values <= 1.0; + 0x7FFF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2sflt16( + float f + ) + { + if (f <= -1.0) + return 0xFFFF; + else if (f >= 1.0) + return 0x7FFF; + else + { + int iExp; + float normalValue; + uint16_t sign; + + normalValue = frexpf(f, &iExp); + + sign = 0; + if (normalValue < 0) + { + // set the "sign bit" of the result + // and work with the absolute value of normalValue. + sign = 0x8000; + normalValue = -normalValue; + } + + // abs(f) is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + iExp = 0; + + // bit 15 is the sign + // bits 14..11 are the exponent + // bits 10..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 11) + 0.5; + if (outputFraction >= (1 << 11u)) + { + // reduce output fraction + outputFraction = 1 << 10; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0x7FFF | sign; + + return (uint16_t)(sign | (iExp << 11u) | outputFraction); + } + } + +/* + +Name: LMIC_f2sflt12() + +Function: + Encode a floating point number into a uint16_t using only 12 bits. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range (-1.0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15-12: zero + bit 11: sign + bits 10..7: biased exponent + bits 6..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0xFFF for negative values <= 1.0; + 0x7FF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2sflt12( + float f + ) + { + if (f <= -1.0) + return 0xFFF; + else if (f >= 1.0) + return 0x7FF; + else + { + int iExp; + float normalValue; + uint16_t sign; + + normalValue = frexpf(f, &iExp); + + sign = 0; + if (normalValue < 0) + { + // set the "sign bit" of the result + // and work with the absolute value of normalValue. + sign = 0x800; + normalValue = -normalValue; + } + + // abs(f) is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + iExp = 0; + + // bit 15 is the sign + // bits 14..11 are the exponent + // bits 10..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 7) + 0.5; + if (outputFraction >= (1 << 7u)) + { + // reduce output fraction + outputFraction = 1 << 6; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0x7FF | sign; + + return (uint16_t)(sign | (iExp << 7u) | outputFraction); + } + } + +/* + +Name: LMIC_f2uflt16() + +Function: + Encode a floating point number into a uint16_t. + +Definition: + uint16_t LMIC_f2uflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range [0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15..12: biased exponent + bits 11..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0x0000 for values < 0.0; + 0xFFFF for positive values >= 1.0; + Otherwise an appropriate encoding of the input float. + +*/ + +uint16_t +LMIC_f2uflt16( + float f + ) + { + if (f < 0.0) + return 0; + else if (f >= 1.0) + return 0xFFFF; + else + { + int iExp; + float normalValue; + + normalValue = frexpf(f, &iExp); + + // f is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + // underflow. + iExp = 0; + + // bits 15..12 are the exponent + // bits 11..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 12) + 0.5; + if (outputFraction >= (1 << 12u)) + { + // reduce output fraction + outputFraction = 1 << 11; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0xFFFF; + + return (uint16_t)((iExp << 12u) | outputFraction); + } + } + +/* + +Name: LMIC_f2uflt12() + +Function: + Encode positive floating point number into a uint16_t using only 12 bits. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range [0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15-12: zero + bits 11..8: biased exponent + bits 7..0: mantissa + + The float is properly rounded, and saturates. + +Returns: + 0x000 for negative values < 0.0; + 0xFFF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2uflt12( + float f + ) + { + if (f < 0.0) + return 0x000; + else if (f >= 1.0) + return 0xFFF; + else + { + int iExp; + float normalValue; + + normalValue = frexpf(f, &iExp); + + // f is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + // graceful underflow + iExp = 0; + + // bits 11..8 are the exponent + // bits 7..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 8) + 0.5; + if (outputFraction >= (1 << 8u)) + { + // reduce output fraction + outputFraction = 1 << 7; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0xFFF; + + return (uint16_t)((iExp << 8u) | outputFraction); + } + } diff --git a/libraries/arduino-lmic-master/src/lmic/lmic_util.h b/libraries/arduino-lmic-master/src/lmic/lmic_util.h new file mode 100644 index 0000000..7e1b3c8 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lmic_util.h @@ -0,0 +1,34 @@ +/* + +Module: lmic_util.h + +Function: + Declare encoding and decoding utilities for LMIC clients. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI September 2018 + +*/ + +#ifndef _LMIC_UTIL_H_ +# define _LMIC_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +uint16_t LMIC_f2sflt16(float); +uint16_t LMIC_f2sflt12(float); +uint16_t LMIC_f2uflt16(float); +uint16_t LMIC_f2uflt12(float); + +#ifdef __cplusplus +} +#endif + +#endif /* _LMIC_UTIL_H_ */ diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase.h b/libraries/arduino-lmic-master/src/lmic/lorabase.h new file mode 100644 index 0000000..fc0fd2d --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase.h @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyritght (c) 2017 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _lorabase_h_ +#define _lorabase_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +// ================================================================================ +// BEG: Keep in sync with lorabase.hpp +// + +enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; +enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; +enum _bw_t { BW125=0, BW250, BW500, BWrfu }; +typedef u1_t cr_t; +typedef u1_t sf_t; +typedef u1_t bw_t; +typedef u1_t dr_t; +typedef u2_t rxsyms_t; + +// Radio parameter set (encodes SF/BW/CR/IH/NOCRC) +// 2..0: Spreading factor +// 4..3: bandwidth: 0 == 125kHz, 1 == 250 kHz, 2 == 500 kHz. 3 == reserved. +// 6..5: coding rate: 0 == 4/5, 1 == 4/6, 2 == 4/7, 3 == 4/8 +// 7: nocrc: 0 == with crc, 1 == without crc +// 15..8: Implicit header control: 0 ==> none, 1..0xFF ==> length in bytes. + +typedef u2_t rps_t; +TYPEDEF_xref2rps_t; + +enum { ILLEGAL_RPS = 0xFF }; + +// Global maximum frame length +enum { STD_PREAMBLE_LEN = 8 }; +enum { MAX_LEN_FRAME = LMIC_MAX_FRAME_LENGTH }; +enum { LEN_DEVNONCE = 2 }; +enum { LEN_ARTNONCE = 3 }; +enum { LEN_NETID = 3 }; +enum { DELAY_JACC1 = 5 }; // in secs +enum { DELAY_DNW1 = 1 }; // in secs down window #1 +enum { DELAY_EXTDNW2 = 1 }; // in secs +enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs +enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1 +enum { BCN_INTV_exp = 7 }; +enum { BCN_INTV_sec = 1< 1) + MCMD_TxParamSetupReq = 0x09, // u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP + MCMD_DlChannelReq = 0x0A, // u1: channel, u3: frequency + MCMD_DeviceTimeAns = 0x0D, // u4: seconds since epoch, u1: fractional second + + // Class B + MCMD_PingSlotInfoAns = 0x10, // - + MCMD_PingSlotChannelReq = 0x11, // u3: freq, u1:dr [7-4]:RFU [3:0]:datarate + MCMD_BeaconTimingAns = 0x12, // u2: delay(in TUNIT millis), u1:channel (DEPRECATED) + MCMD_BeaconFreqReq = 0x13, // u3: freq +}; + +enum { + MCMD_BeaconTimingAns_TUNIT = 30 // time unit of delay value in millis +}; +enum { + MCMD_LinkADRAns_RFU = 0xF8, // RFU bits + MCMD_LinkADRAns_PowerACK = 0x04, // 0=not supported power level + MCMD_LinkADRAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_LinkADRAns_ChannelACK = 0x01, // 0=unknown channel enabled +}; +enum { + MCMD_RXParamSetupAns_RFU = 0xF8, // RFU bits + MCMD_RXParamSetupAns_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed + MCMD_RXParamSetupAns_RX2DataRateACK = 0x02, // 0=unknown data rate + MCMD_RXParamSetupAns_ChannelACK = 0x01, // 0=unknown channel enabled +}; +enum { + MCMD_NewChannelAns_RFU = 0xFC, // RFU bits + MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency +}; +enum { + MCMD_RXTimingSetupReq_RFU = 0xF0, // RFU bits + MCMD_RXTimingSetupReq_Delay = 0x0F, // delay in secs, 1..15; 0 is mapped to 1. +}; +enum { + MCMD_DlChannelAns_RFU = 0xFC, // RFU bits + MCMD_DlChannelAns_FreqACK = 0x02, // 0 = uplink frequency not defined for this channel + MCMD_DlChannelAns_ChannelACK = 0x01, // 0 = rejected channel freq +}; +enum { + MCMD_PingSlotFreqAns_RFU = 0xFC, + MCMD_PingSlotFreqAns_DataRateACK = 0x02, + MCMD_PingSlotFreqAns_ChannelACK = 0x01, +}; + +enum { + MCMD_DEVS_EXT_POWER = 0x00, // external power supply + MCMD_DEVS_BATT_MIN = 0x01, // min battery value + MCMD_DEVS_BATT_MAX = 0xFE, // max battery value + MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level +}; + +// Bit fields byte#3 of MCMD_LinkADRReq payload +enum { + MCMD_LinkADRReq_Redundancy_RFU = 0x80, + MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK= 0x70, + MCMD_LinkADRReq_Redundancy_NbTrans_MASK = 0x0F, + + MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT = 0x00, // direct masking for EU + MCMD_LinkADRReq_ChMaskCntl_EULIKE_ALL_ON = 0x60, // EU: enable everything. + + MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K = 0x40, // mask is for the 8 us-like 500 kHz channels + MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL = 0x50, // first special for us-like + MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK = 0x50, // special: bits are banks. + MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON = 0x60, // special channel page enable, bits applied to 64..71 + MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF = 0x70, // special channel page: disble 125K, bits apply to 64..71 + + MCMD_LinkADRReq_ChMaskCntl_CN470_ALL_ON = 0x60, // turn all on for China. +}; + +// Bit fields byte#0 of MCMD_LinkADRReq payload +enum { + MCMD_LinkADRReq_DR_MASK = 0xF0, + MCMD_LinkADRReq_POW_MASK = 0x0F, + MCMD_LinkADRReq_DR_SHIFT = 4, + MCMD_LinkADRReq_POW_SHIFT = 0, +}; + +// bit fields of the TxParam request +enum { + MCMD_TxParam_RxDWELL_SHIFT = 5, + MCMD_TxParam_RxDWELL_MASK = 1 << MCMD_TxParam_RxDWELL_SHIFT, + MCMD_TxParam_TxDWELL_SHIFT = 4, + MCMD_TxParam_TxDWELL_MASK = 1 << MCMD_TxParam_TxDWELL_SHIFT, + MCMD_TxParam_MaxEIRP_SHIFT = 0, + MCMD_TxParam_MaxEIRP_MASK = 0xF << MCMD_TxParam_MaxEIRP_SHIFT, +}; + +// Device address +typedef u4_t devaddr_t; + +// RX quality (device) +enum { RSSI_OFF=64, SNR_SCALEUP=4 }; + +static inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); } +static inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); } +static inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); } +static inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); } +static inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); } +static inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); } +static inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); } +static inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } +static inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } +static inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } +static inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { + return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); +} +#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) +// Two frames with params r1/r2 would interfere on air: same SFx + BWx +static inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } + +extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; +static inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } +static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } +static inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } +static inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } +static inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate +static inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate +static inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR +static inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range +static inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps + +// +// BEG: Keep in sync with lorabase.hpp +// ================================================================================ + + +// Calculate airtime +ostime_t calcAirTime (rps_t rps, u1_t plen); +// Sensitivity at given SF/BW +int getSensitivity (rps_t rps); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _lorabase_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase_as923.h b/libraries/arduino-lmic-master/src/lmic/lorabase_as923.h new file mode 100644 index 0000000..d65bb75 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase_as923.h @@ -0,0 +1,105 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_as923_h_ +#define _lorabase_as923_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for AS923 (always in scope) +| +\****************************************************************************/ + +enum _dr_as923_t { + AS923_DR_SF12 = 0, + AS923_DR_SF11, + AS923_DR_SF10, + AS923_DR_SF9, + AS923_DR_SF8, + AS923_DR_SF7, + AS923_DR_SF7B, + AS923_DR_FSK, + AS923_DR_NONE +}; + +// Bands: +// g1 : 1% 16dBm +// freq band datarates +enum { + AS923_F1 = 923200000, // g1 SF7-12 + AS923_F2 = 923400000, // g1 SF7-12 + AS923_FDOWN = 923200000, // (RX2 freq, DR2) + AS923_FBCN = 923400000, // default BCN, DR3 + AS923_FPING = 923400000, // default ping, DR3 +}; +enum { + AS923_FREQ_MIN = 915000000, + AS923_FREQ_MAX = 928000000 +}; +enum { + AS923_TX_EIRP_MAX_DBM = 16 // 16 dBm +}; +enum { DR_PAGE_AS923 = 0x10 * (LMIC_REGION_as923 - 1) }; + +enum { AS923_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { AS923JP_LBT_US = 5000 }; // microseconds of LBT time -- 5000 ==> + // 5 ms. We use us rather than ms for + // future 128us support, and just for + // backward compatibility -- there + // is code that uses the _US constant, + // and it's awkward to break it. + +enum { AS923JP_LBT_DB_MAX = -80 }; // maximum channel strength in dB; if TX + // we measure more than this, we don't tx. + +// AS923 v1.1, all channels face a 1% duty cycle. So this will have to change +// in the future via a config. But this code base needs major changes for +// v1.1 in any case. +enum { AS923_V102_TX_CAP = 100 }; // v1.0.2 allows 100% + +#ifndef AS923_TX_CAP +# define AS923_TX_CAP AS923_V102_TX_CAP +#endif + +// TxParam defaults +enum { + // initial value of UplinkDwellTime before TxParamSetupReq received. + AS923_INITIAL_TxParam_UplinkDwellTime = 1, + // initial value of DownlinkDwellTime before TxParamSetupReq received. + AS923_INITIAL_TxParam_DownlinkDwellTime = 1, + AS923_UPLINK_DWELL_TIME_osticks = sec2osticks(20), +}; + +#endif /* _lorabase_as923_h_ */ diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase_au915.h b/libraries/arduino-lmic-master/src/lmic/lorabase_au915.h new file mode 100644 index 0000000..7bac54e --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase_au915.h @@ -0,0 +1,89 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_au915_h_ +#define _lorabase_au915_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for AU 915 (always in scope) +| +\****************************************************************************/ + +// Frequency plan for AU 915 MHz +enum _dr_au915_t { + AU915_DR_SF12 = 0, + AU915_DR_SF11, + AU915_DR_SF10, + AU915_DR_SF9, + AU915_DR_SF8, + AU915_DR_SF7, + AU915_DR_SF8C, + AU915_DR_NONE, + // Devices behind a router: + AU915_DR_SF12CR = 8, + AU915_DR_SF11CR, + AU915_DR_SF10CR, + AU915_DR_SF9CR, + AU915_DR_SF8CR, + AU915_DR_SF7CR +}; + +// Default frequency plan for AU 915MHz +enum { + AU915_125kHz_UPFBASE = 915200000, + AU915_125kHz_UPFSTEP = 200000, + AU915_500kHz_UPFBASE = 915900000, + AU915_500kHz_UPFSTEP = 1600000, + AU915_500kHz_DNFBASE = 923300000, + AU915_500kHz_DNFSTEP = 600000 +}; +enum { + AU915_FREQ_MIN = 915000000, + AU915_FREQ_MAX = 928000000 +}; +enum { + AU915_TX_EIRP_MAX_DBM = 30 // 30 dBm +}; +enum { + // initial value of UplinkDwellTime before TxParamSetupReq received. + AU915_INITIAL_TxParam_UplinkDwellTime = 1, + AU915_UPLINK_DWELL_TIME_osticks = sec2osticks(20), +}; + +enum { DR_PAGE_AU915 = 0x10 * (LMIC_REGION_au915 - 1) }; + +enum { AU915_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +#endif /* _lorabase_au915_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase_eu868.h b/libraries/arduino-lmic-master/src/lmic/lorabase_eu868.h new file mode 100644 index 0000000..0040ad0 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase_eu868.h @@ -0,0 +1,92 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_eu868_h_ +#define _lorabase_eu868_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for EU868 (always in scope) +| +\****************************************************************************/ + +// +// Default frequency plan for EU 868MHz ISM band +// data rates +// this is a little confusing: the integer values of these constants are the +// DataRates from the LoRaWAN Regional Parmaeter spec. The names are just +// convenient indications, so we can use them in the rare case that we need to +// choose a DataRate by SF and configuration, not by DR code. + +enum _dr_eu868_t { + EU868_DR_SF12 = 0, + EU868_DR_SF11, + EU868_DR_SF10, + EU868_DR_SF9, + EU868_DR_SF8, + EU868_DR_SF7, + EU868_DR_SF7B, + EU868_DR_FSK, + EU868_DR_NONE +}; + +// Bands: +// g1 : 1% 14dBm +// g2 : 0.1% 14dBm +// g3 : 10% 27dBm +// freq band datarates +enum { + EU868_F1 = 868100000, // g1 SF7-12 + EU868_F2 = 868300000, // g1 SF7-12 FSK SF7/250 + EU868_F3 = 868500000, // g1 SF7-12 + EU868_F4 = 868850000, // g2 SF7-12 + EU868_F5 = 869050000, // g2 SF7-12 + EU868_F6 = 869525000, // g3 SF7-12 + EU868_J4 = 864100000, // g2 SF7-12 used during join + EU868_J5 = 864300000, // g2 SF7-12 ditto + EU868_J6 = 864500000, // g2 SF7-12 ditto +}; +enum { + EU868_FREQ_MIN = 863000000, + EU868_FREQ_MAX = 870000000 +}; +enum { + EU868_TX_EIRP_MAX_DBM = 16 // 16 dBm EIRP. So subtract 3 dBm for a 3 dBi antenna. +}; + +enum { EU868_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { DR_PAGE_EU868 = 0x10 * (LMIC_REGION_eu868 - 1) }; + +#endif /* _lorabase_eu868_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase_in866.h b/libraries/arduino-lmic-master/src/lmic/lorabase_in866.h new file mode 100644 index 0000000..6955a76 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase_in866.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_in866_h_ +#define _lorabase_in866_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for IN866 (always in scope) +| +\****************************************************************************/ + +enum _dr_in866_t { + IN866_DR_SF12 = 0, // DR0 + IN866_DR_SF11, // DR1 + IN866_DR_SF10, // DR2 + IN866_DR_SF9, // DR3 + IN866_DR_SF8, // DR4 + IN866_DR_SF7, // DR5 + IN866_DR_RFU, // - + IN866_DR_FSK, // DR7 + IN866_DR_NONE +}; + +// There is no dwell-time or duty-cycle limitation for IN +// +// max power: 30dBM +// +// freq datarates +enum { + IN866_F1 = 865062500, // SF7-12 (DR0-5) + IN866_F2 = 865402500, // SF7-12 (DR0-5) + IN866_F3 = 865985000, // SF7-12 (DR0-5) + IN866_FB = 866550000, // beacon/ping +}; +enum { + IN866_FREQ_MIN = 865000000, + IN866_FREQ_MAX = 867000000 +}; +enum { + IN866_TX_EIRP_MAX_DBM = 30 // 30 dBm +}; +enum { DR_PAGE_IN866 = 0x10 * (LMIC_REGION_in866 - 1) }; + +enum { IN866_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +#endif /* _lorabase_in866_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase_kr920.h b/libraries/arduino-lmic-master/src/lmic/lorabase_kr920.h new file mode 100644 index 0000000..02fd5a0 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase_kr920.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017, 2019 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_kr920_h_ +#define _lorabase_kr920_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for KR920 (always in scope) +| +\****************************************************************************/ + +enum _dr_kr920_t { + KR920_DR_SF12 = 0, // DR0 + KR920_DR_SF11, // DR1 + KR920_DR_SF10, // DR2 + KR920_DR_SF9, // DR3 + KR920_DR_SF8, // DR4 + KR920_DR_SF7, // DR5 + KR920_DR_NONE +}; + +// There is no dwell-time or duty-cycle limitation for IN +// +// max power: 30dBM +// +// freq datarates +enum { + KR920_F1 = 922100000, // SF7-12 (DR0-5) + KR920_F2 = 922300000, // SF7-12 (DR0-5) + KR920_F3 = 922500000, // SF7-12 (DR0-5) + KR920_FBCN = 923100000, // beacon/ping + KR920_F14DBM = 922100000, // Allows 14 dBm (not 10) if >= this. + KR920_FDOWN = 921900000, // RX2 downlink frequency +}; +enum { + KR920_FREQ_MIN = 920900000, + KR920_FREQ_MAX = 923300000 +}; +enum { + KR920_TX_EIRP_MAX_DBM = 14, // 14 dBm for most + KR920_TX_EIRP_MAX_DBM_LOW = 10, // 10 dBm for some +}; +enum { DR_PAGE_KR920 = 0x10 * (LMIC_REGION_kr920 - 1) }; + +enum { KR920_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { KR920_LBT_US = 128 }; // microseconds of LBT time. + +enum { KR920_LBT_DB_MAX = -80 }; // maximum channel strength in dB; if TX + // we measure more than this, we don't tx. + +#endif /* _lorabase_in866_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lorabase_us915.h b/libraries/arduino-lmic-master/src/lmic/lorabase_us915.h new file mode 100644 index 0000000..ed960c6 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorabase_us915.h @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_us915_h_ +#define _lorabase_us915_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for US915 (always in scope) +| +\****************************************************************************/ + +// Frequency plan for US 915MHz ISM band +// data rates +enum _dr_us915_t { + US915_DR_SF10 = 0, + US915_DR_SF9, + US915_DR_SF8, + US915_DR_SF7, + US915_DR_SF8C, + US915_DR_NONE, + // Devices "behind a router" (and upper half of DR list): + US915_DR_SF12CR = 8, + US915_DR_SF11CR, + US915_DR_SF10CR, + US915_DR_SF9CR, + US915_DR_SF8CR, + US915_DR_SF7CR +}; + +// Default frequency plan for US 915MHz +enum { + US915_125kHz_UPFBASE = 902300000, + US915_125kHz_UPFSTEP = 200000, + US915_500kHz_UPFBASE = 903000000, + US915_500kHz_UPFSTEP = 1600000, + US915_500kHz_DNFBASE = 923300000, + US915_500kHz_DNFSTEP = 600000 +}; +enum { + US915_FREQ_MIN = 902000000, + US915_FREQ_MAX = 928000000 +}; +enum { + US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're + // on an 64-channel bandplan. See code + // that computes tx power. +}; + +enum { + US915_LinkAdrReq_POW_MAX_1_0_2 = 0xA, + US915_LinkAdrReq_POW_MAX_1_0_3 = 0xE, +}; + +enum { DR_PAGE_US915 = 0x10 * (LMIC_REGION_us915 - 1) }; + +enum { US915_LMIC_REGION_EIRP = 0 }; // region doesn't use EIRP, uses tx power + +#endif /* _lorabase_us915_h_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/lorawan_spec_compliance.h b/libraries/arduino-lmic-master/src/lmic/lorawan_spec_compliance.h new file mode 100644 index 0000000..e479b24 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/lorawan_spec_compliance.h @@ -0,0 +1,64 @@ +/* + +Module: lorawan_spec_compliance.h + +Function: + Details from the LoRaWAN specification for compliance. + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation March 2019 + +*/ + +#ifndef _lorawan_spec_COMPLIANCE_H_ /* prevent multiple includes */ +#define _lorawan_spec_COMPLIANCE_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +enum { + // the port for MAC commands + LORAWAN_PORT_MAC = 0u, + // the first port available for applications + LORAWAN_PORT_USER_MIN = 1u, + // the last port available for applications + LORAWAN_PORT_USER_MAX = 223u, + // the base of the reserved port numbers + LORAWAN_PORT_RESERVED = 224u, + // the port for the compliance protocol + LORAWAN_PORT_COMPLIANCE = LORAWAN_PORT_RESERVED + 0u, +}; + +enum lowawan_compliance_cmd_e { + LORAWAN_COMPLIANCE_CMD_DEACTIVATE = 0u, + LORAWAN_COMPLIANCE_CMD_ACTIVATE = 1u, + LORAWAN_COMPLIANCE_CMD_SET_CONFIRM = 2u, + LORAWAN_COMPLIANCE_CMD_SET_UNCONFIRM = 3u, + LORAWAN_COMPLIANCE_CMD_ECHO = 4u, + LORAWAN_COMPLIANCE_CMD_LINK = 5u, + LORAWAN_COMPLIANCE_CMD_JOIN = 6u, + LORAWAN_COMPLIANCE_CMD_CW = 7u, + LORAWAN_COMPLIANCE_CMD_MFG_BASE = 0x80u, +}; + +typedef unsigned char lorawan_compliance_cmd_t; + +// info on specific commands +enum { + LORAWAN_COMPLIANCE_CMD_ACTIVATE_LEN = 4u, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC = 1u, + + // Maximum crypto frame size; although the spec says 18, it + // is also used for testing max packet size. + LORAWAN_COMPLIANCE_CMD_ECHO_LEN_MAX = 242u, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _lorawan_spec_COMPLIANCE_H_ */ \ No newline at end of file diff --git a/libraries/arduino-lmic-master/src/lmic/oslmic.c b/libraries/arduino-lmic-master/src/lmic/oslmic.c new file mode 100644 index 0000000..fa205f4 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/oslmic.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016-2017, 2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LMIC_DR_LEGACY 0 + +#include "lmic.h" + +extern const struct lmic_pinmap lmic_pins; + +// RUNTIME STATE +static struct { + osjob_t* scheduledjobs; + osjob_t* runnablejobs; +} OS; + +int os_init_ex (const void *pintable) { + memset(&OS, 0x00, sizeof(OS)); + hal_init_ex(pintable); + if (! radio_init()) + return 0; + LMIC_init(); + return 1; +} + +void os_init() { + if (os_init_ex((const void *)&lmic_pins)) + return; + ASSERT(0); +} + +ostime_t os_getTime () { + return hal_ticks(); +} + +// unlink job from queue, return if removed +static int unlinkjob (osjob_t** pnext, osjob_t* job) { + for( ; *pnext; pnext = &((*pnext)->next)) { + if(*pnext == job) { // unlink + *pnext = job->next; + return 1; + } + } + return 0; +} + +static osjob_t** getJobQueue(osjob_t* job) { + return os_jobIsTimed(job) ? &OS.scheduledjobs : &OS.runnablejobs; +} + +// clear scheduled job +void os_clearCallback (osjob_t* job) { + hal_disableIRQs(); + + unlinkjob(getJobQueue(job), job); + + hal_enableIRQs(); +} + +// schedule immediately runnable job +void os_setCallback (osjob_t* job, osjobcb_t cb) { + osjob_t** pnext; + hal_disableIRQs(); + + // remove if job was already queued + unlinkjob(getJobQueue(job), job); + + // fill-in job. Ascending memory order is write-queue friendly + job->next = NULL; + job->deadline = 0; + job->func = cb; + + // add to end of run queue + for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next)); + *pnext = job; + hal_enableIRQs(); +} + +// schedule timed job +void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) { + osjob_t** pnext; + + // special case time 0 -- it will be one tick late. + if (time == 0) + time = 1; + + hal_disableIRQs(); + + // remove if job was already queued + unlinkjob(getJobQueue(job), job); + + // fill-in job + job->next = NULL; + job->deadline = time; + job->func = cb; + + // insert into schedule + for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { + if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) + // enqueue before next element and stop + job->next = *pnext; + break; + } + } + *pnext = job; + hal_enableIRQs(); +} + +// execute jobs from timer and from run queue +void os_runloop () { + while(1) { + os_runloop_once(); + } +} + +void os_runloop_once() { + osjob_t* j = NULL; + hal_processPendingIRQs(); + + hal_disableIRQs(); + // check for runnable jobs + if(OS.runnablejobs) { + j = OS.runnablejobs; + OS.runnablejobs = j->next; + } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs + j = OS.scheduledjobs; + OS.scheduledjobs = j->next; + } else { // nothing pending + hal_sleep(); // wake by irq (timer already restarted) + } + hal_enableIRQs(); + if(j) { // run job callback + j->func(j); + } +} + +// return true if there are any jobs scheduled within time ticks from now. +// return false if any jobs scheduled are at least time ticks in the future. +bit_t os_queryTimeCriticalJobs(ostime_t time) { + if (OS.scheduledjobs && + OS.scheduledjobs->deadline - os_getTime() < time) + return 1; + else + return 0; +} diff --git a/libraries/arduino-lmic-master/src/lmic/oslmic.h b/libraries/arduino-lmic-master/src/lmic/oslmic.h new file mode 100644 index 0000000..448b029 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/oslmic.h @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2018, 2019 MCCI Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file +#ifndef _oslmic_h_ +#define _oslmic_h_ + +// Dependencies required for the LMIC to run. +// These settings can be adapted to the underlying system. +// You should not, however, change the lmic merely for porting purposes.[hc] + +#include "config.h" + +#ifndef _lmic_env_h_ +# include "lmic_env.h" +#endif + +#ifndef _oslmic_types_h_ +# include "oslmic_types.h" +#endif + +LMIC_BEGIN_DECLS + + +#include +#include "hal.h" +#define EV(a,b,c) /**/ +#define DO_DEVDB(field1,field2) /**/ +#if !defined(CFG_noassert) +#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__) +#else +#define ASSERT(cond) /**/ +#endif + +#define os_clearMem(a,b) memset(a,0,b) +#define os_copyMem(a,b,c) memcpy(a,b,c) + +typedef struct osjob_t osjob_t; +typedef struct band_t band_t; +typedef struct chnldef_t chnldef_t; +typedef struct rxsched_t rxsched_t; +typedef struct bcninfo_t bcninfo_t; +typedef const u1_t* xref2cu1_t; +typedef u1_t* xref2u1_t; + +// int32_t == s4_t is long on some platforms; and someday +// we will want 64-bit ostime_t. So, we will use a macro for the +// print formatting of ostime_t. +#ifndef LMIC_PRId_ostime_t +# include +# define LMIC_PRId_ostime_t PRId32 +#endif + +#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t +#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t +#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t +#define TYPEDEF_xref2band_t typedef band_t* xref2band_t +#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t + +#define SIZEOFEXPR(x) sizeof(x) + +#define DECL_ON_LMIC_EVENT LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e)) + +extern u4_t AESAUX[]; +extern u4_t AESKEY[]; +#define AESkey ((u1_t*)AESKEY) +#define AESaux ((u1_t*)AESAUX) +#define FUNC_ADDR(func) (&(func)) + +u1_t radio_rand1 (void); +#define os_getRndU1() radio_rand1() + +#define DEFINE_LMIC struct lmic_t LMIC +#define DECLARE_LMIC extern struct lmic_t LMIC + +typedef struct oslmic_radio_rssi_s oslmic_radio_rssi_t; + +struct oslmic_radio_rssi_s { + s2_t min_rssi; + s2_t max_rssi; + s2_t mean_rssi; + u2_t n_rssi; +}; + +int radio_init (void); +void radio_irq_handler (u1_t dio); +void radio_irq_handler_v2 (u1_t dio, ostime_t tref); +void os_init (void); +int os_init_ex (const void *pPinMap); +void os_runloop (void); +void os_runloop_once (void); +u1_t radio_rssi (void); +void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi); + +//================================================================================ + +#ifndef RX_RAMPUP_DEFAULT +//! \brief RX_RAMPUP_DEFAULT specifies the extra time we must allow to set up an RX event due +//! to platform issues. It's specified in units of ostime_t. It must reflect +//! platform jitter and latency, as well as the speed of the LMIC when running +//! on this plaform. It's not used directly; clients call os_getRadioRxRampup(), +//! which might adaptively vary this based on observed timeouts. +#define RX_RAMPUP_DEFAULT (us2osticks(10000)) +#endif + +#ifndef TX_RAMPUP +// TX_RAMPUP specifies the extra time we must allow to set up a TX event) due +// to platform issues. It's specified in units of ostime_t. It must reflect +// platform jitter and latency, as well as the speed of the LMIC when running +// on this plaform. +#define TX_RAMPUP (us2osticks(10000)) +#endif + +#ifndef OSTICKS_PER_SEC +#define OSTICKS_PER_SEC 32768 +#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 +#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. +#endif + +#if !HAS_ostick_conv +#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000)) +#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000)) +#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC)) +#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC)) +#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC)) +// Special versions +#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000)) +#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000)) +#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000)) +#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000)) +#endif + + +struct osjob_t; // fwd decl. + +//! the function type for osjob_t callbacks +typedef void (osjobcbfn_t)(struct osjob_t*); + +//! the pointer-to-function for osjob_t callbacks +typedef osjobcbfn_t *osjobcb_t; + +struct osjob_t { + struct osjob_t* next; + ostime_t deadline; + osjobcb_t func; +}; +TYPEDEF_xref2osjob_t; + +//! determine whether a job is timed or immediate. os_setTimedCallback() +// must treat incoming == 0 as being 1 instead. +static inline int os_jobIsTimed(xref2osjob_t job) { + return (job->deadline != 0); +} + +#ifndef HAS_os_calls + +#ifndef os_getDevKey +void os_getDevKey (xref2u1_t buf); +#endif +#ifndef os_getArtEui +void os_getArtEui (xref2u1_t buf); +#endif +#ifndef os_getDevEui +void os_getDevEui (xref2u1_t buf); +#endif +#ifndef os_setCallback +void os_setCallback (xref2osjob_t job, osjobcb_t cb); +#endif +#ifndef os_setTimedCallback +void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb); +#endif +#ifndef os_clearCallback +void os_clearCallback (xref2osjob_t job); +#endif +#ifndef os_getRadioRxRampup +ostime_t os_getRadioRxRampup (void); +#endif +#ifndef os_getTime +ostime_t os_getTime (void); +#endif +#ifndef os_getTimeSecs +uint os_getTimeSecs (void); +#endif +#ifndef os_radio +void os_radio (u1_t mode); +#endif +#ifndef os_getBattLevel +u1_t os_getBattLevel (void); +#endif +#ifndef os_queryTimeCriticalJobs +//! Return non-zero if any jobs are scheduled between now and now+time. +bit_t os_queryTimeCriticalJobs(ostime_t time); +#endif + +#ifndef os_rlsbf4 +//! Read 32-bit quantity from given pointer in little endian byte order. +u4_t os_rlsbf4 (xref2cu1_t buf); +#endif +#ifndef os_wlsbf4 +//! Write 32-bit quntity into buffer in little endian byte order. +void os_wlsbf4 (xref2u1_t buf, u4_t value); +#endif +#ifndef os_rmsbf4 +//! Read 32-bit quantity from given pointer in big endian byte order. +u4_t os_rmsbf4 (xref2cu1_t buf); +#endif +#ifndef os_wmsbf4 +//! Write 32-bit quntity into buffer in big endian byte order. +void os_wmsbf4 (xref2u1_t buf, u4_t value); +#endif +#ifndef os_rlsbf2 +//! Read 16-bit quantity from given pointer in little endian byte order. +u2_t os_rlsbf2 (xref2cu1_t buf); +#endif +#ifndef os_wlsbf2 +//! Write 16-bit quntity into buffer in little endian byte order. +void os_wlsbf2 (xref2u1_t buf, u2_t value); +#endif + +//! Get random number (default impl for u2_t). +#ifndef os_getRndU2 +#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1())) +#endif +#ifndef os_crc16 +u2_t os_crc16 (xref2cu1_t d, uint len); +#endif + +#endif // !HAS_os_calls + +// ====================================================================== +// Table support +// These macros for defining a table of constants and retrieving values +// from it makes it easier for other platforms (like AVR) to optimize +// table accesses. +// Use CONST_TABLE() whenever declaring or defining a table, and +// TABLE_GET_xx whenever accessing its values. The actual name of the +// declared variable will be modified to prevent accidental direct +// access. The accessor macros forward to an inline function to allow +// proper type checking of the array element type. + +// Helper to add a prefix to the table name +#define RESOLVE_TABLE(table) constant_table_ ## table + +// get number of entries in table +#define LENOF_TABLE(table) (sizeof(RESOLVE_TABLE(table)) / sizeof(RESOLVE_TABLE(table)[0])) + +// Accessors for table elements +#define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) +#define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) +#define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) +#define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index) +#define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) +#define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) +#define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index) +#define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2) + +#if defined(__AVR__) + #include + // Macro to define the getter functions. This loads data from + // progmem using pgm_read_xx, or accesses memory directly when the + // index is a constant so gcc can optimize it away; + #define TABLE_GETTER(postfix, type, pgm_type) \ + static inline type table_get ## postfix(const type *table, size_t index) { \ + if (__builtin_constant_p(table[index])) \ + return table[index]; \ + return pgm_read_ ## pgm_type(&table[index]); \ + } + + TABLE_GETTER(_u1, u1_t, byte); + TABLE_GETTER(_s1, s1_t, byte); + TABLE_GETTER(_u2, u2_t, word); + TABLE_GETTER(_s2, s2_t, word); + TABLE_GETTER(_u4, u4_t, dword); + TABLE_GETTER(_s4, s4_t, dword); + + // This assumes ostime_t is 4 bytes, so error out if it is not + typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1]; + TABLE_GETTER(_ostime, ostime_t, dword); + + // For AVR, store constants in PROGMEM, saving on RAM usage + #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) +#else + static inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; } + static inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; } + static inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; } + static inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; } + static inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; } + static inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; } + static inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; } + + // Declare a table + #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) +#endif + +// ====================================================================== +// AES support +// !!Keep in sync with lorabase.hpp!! + +#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too +#define AES_ENC 0x00 +#define AES_DEC 0x01 +#define AES_MIC 0x02 +#define AES_CTR 0x04 +#define AES_MICNOAUX 0x08 +#endif +#ifndef AESkey // if AESkey is defined as macro all other values must be too +extern xref2u1_t AESkey; +extern xref2u1_t AESaux; +#endif +#ifndef os_aes +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); +#endif + +// ====================================================================== +// Simple logging support. Vanishes unless enabled. + +#if LMIC_ENABLE_event_logging +extern void LMICOS_logEvent(const char *pMessage); +extern void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); +#else // ! LMIC_ENABLE_event_logging +# define LMICOS_logEvent(m) do { ; } while (0) +# define LMICOS_logEventUint32(m, d) do { ; } while (0) +#endif // ! LMIC_ENABLE_event_logging + + +LMIC_END_DECLS + +#endif // _oslmic_h_ diff --git a/libraries/arduino-lmic-master/src/lmic/oslmic_types.h b/libraries/arduino-lmic-master/src/lmic/oslmic_types.h new file mode 100644 index 0000000..d790a37 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/oslmic_types.h @@ -0,0 +1,47 @@ +/* + +Module: oslmic_types.h + +Function: + Basic types from oslmic.h, shared by all layers. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + (based on oslmic.h from IBM). + +*/ + +#ifndef _oslmic_types_h_ +# define _oslmic_types_h_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//================================================================================ +//================================================================================ +// Target platform as C library +typedef uint8_t bit_t; +typedef uint8_t u1_t; +typedef int8_t s1_t; +typedef uint16_t u2_t; +typedef int16_t s2_t; +typedef uint32_t u4_t; +typedef int32_t s4_t; +typedef unsigned int uint; +typedef const char* str_t; + +// the HAL needs to give us ticks, so it ought to know the right type. +typedef s4_t ostime_t; + +#ifdef __cplusplus +} +#endif + +/* end of oslmic_types.h */ +#endif /* _oslmic_types_h_ */ diff --git a/libraries/arduino-lmic-master/src/lmic/radio.c b/libraries/arduino-lmic-master/src/lmic/radio.c new file mode 100644 index 0000000..d6cc908 --- /dev/null +++ b/libraries/arduino-lmic-master/src/lmic/radio.c @@ -0,0 +1,1443 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016-2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file + +#define LMIC_DR_LEGACY 0 + +#include "lmic.h" + +// ---------------------------------------- +// Registers Mapping +// // -type- 1272 vs 1276 +#define RegFifo 0x00 // common +#define RegOpMode 0x01 // common see below +#define FSKRegBitrateMsb 0x02 // - +#define FSKRegBitrateLsb 0x03 // - +#define FSKRegFdevMsb 0x04 // - +#define FSKRegFdevLsb 0x05 // - +#define RegFrfMsb 0x06 // common FSK: 1272: 915; 1276: 434 MHz +#define RegFrfMid 0x07 // common ditto +#define RegFrfLsb 0x08 // common ditto +#define RegPaConfig 0x09 // common see below, many diffs +#define RegPaRamp 0x0A // common see below: bits 6..4 are diff +#define RegOcp 0x0B // common - +#define RegLna 0x0C // common bits 4..0 are diff. +#define FSKRegRxConfig 0x0D // - +#define LORARegFifoAddrPtr 0x0D +#define FSKRegRssiConfig 0x0E // - +#define LORARegFifoTxBaseAddr 0x0E +#define FSKRegRssiCollision 0x0F // - +#define LORARegFifoRxBaseAddr 0x0F +#define FSKRegRssiThresh 0x10 // - +#define LORARegFifoRxCurrentAddr 0x10 +#define FSKRegRssiValue 0x11 // - +#define LORARegIrqFlagsMask 0x11 +#define FSKRegRxBw 0x12 // - +#define LORARegIrqFlags 0x12 +#define FSKRegAfcBw 0x13 // - +#define LORARegRxNbBytes 0x13 +#define FSKRegOokPeak 0x14 // - +#define LORARegRxHeaderCntValueMsb 0x14 +#define FSKRegOokFix 0x15 // - +#define LORARegRxHeaderCntValueLsb 0x15 +#define FSKRegOokAvg 0x16 // - +#define LORARegRxPacketCntValueMsb 0x16 +#define LORARegRxpacketCntValueLsb 0x17 +#define LORARegModemStat 0x18 +#define LORARegPktSnrValue 0x19 +#define FSKRegAfcFei 0x1A // - +#define LORARegPktRssiValue 0x1A +#define FSKRegAfcMsb 0x1B // - +#define LORARegRssiValue 0x1B +#define FSKRegAfcLsb 0x1C // - +#define LORARegHopChannel 0x1C +#define FSKRegFeiMsb 0x1D // - +#define LORARegModemConfig1 0x1D +#define FSKRegFeiLsb 0x1E // - +#define LORARegModemConfig2 0x1E +#define FSKRegPreambleDetect 0x1F // - +#define LORARegSymbTimeoutLsb 0x1F +#define FSKRegRxTimeout1 0x20 // - +#define LORARegPreambleMsb 0x20 +#define FSKRegRxTimeout2 0x21 // - +#define LORARegPreambleLsb 0x21 +#define FSKRegRxTimeout3 0x22 // - +#define LORARegPayloadLength 0x22 +#define FSKRegRxDelay 0x23 // - +#define LORARegPayloadMaxLength 0x23 +#define FSKRegOsc 0x24 // - +#define LORARegHopPeriod 0x24 +#define FSKRegPreambleMsb 0x25 // - +#define LORARegFifoRxByteAddr 0x25 +#define FSKRegPreambleLsb 0x26 // - +#define LORARegModemConfig3 0x26 +#define FSKRegSyncConfig 0x27 // - +#define LORARegFeiMsb 0x28 +#define FSKRegSyncValue1 0x28 // - +#define LORAFeiMib 0x29 +#define FSKRegSyncValue2 0x29 // - +#define LORARegFeiLsb 0x2A +#define FSKRegSyncValue3 0x2A // - +#define FSKRegSyncValue4 0x2B // - +#define LORARegRssiWideband 0x2C +#define FSKRegSyncValue5 0x2C // - +#define FSKRegSyncValue6 0x2D // - +#define FSKRegSyncValue7 0x2E // - +#define FSKRegSyncValue8 0x2F // - +#define LORARegIffReq1 0x2F +#define FSKRegPacketConfig1 0x30 // - +#define LORARegIffReq2 0x30 +#define FSKRegPacketConfig2 0x31 // - +#define LORARegDetectOptimize 0x31 +#define FSKRegPayloadLength 0x32 // - +#define FSKRegNodeAdrs 0x33 // - +#define LORARegInvertIQ 0x33 +#define FSKRegBroadcastAdrs 0x34 // - +#define FSKRegFifoThresh 0x35 // - +#define FSKRegSeqConfig1 0x36 // - +#define LORARegHighBwOptimize1 0x36 +#define FSKRegSeqConfig2 0x37 // - +#define LORARegDetectionThreshold 0x37 +#define FSKRegTimerResol 0x38 // - +#define FSKRegTimer1Coef 0x39 // - +#define LORARegSyncWord 0x39 +#define FSKRegTimer2Coef 0x3A // - +#define LORARegHighBwOptimize2 0x3A +#define FSKRegImageCal 0x3B // - +#define FSKRegTemp 0x3C // - +#define FSKRegLowBat 0x3D // - +#define FSKRegIrqFlags1 0x3E // - +#define FSKRegIrqFlags2 0x3F // - +#define RegDioMapping1 0x40 // common +#define RegDioMapping2 0x41 // common +#define RegVersion 0x42 // common +// #define RegAgcRef 0x43 // common +// #define RegAgcThresh1 0x44 // common +// #define RegAgcThresh2 0x45 // common +// #define RegAgcThresh3 0x46 // common +// #define RegPllHop 0x4B // common +// #define RegTcxo 0x58 // common +// #define RegPll 0x5C // common +// #define RegPllLowPn 0x5E // common +// #define RegFormerTemp 0x6C // common +// #define RegBitRateFrac 0x70 // common + +#if defined(CFG_sx1276_radio) +#define RegTcxo 0x4B // common different addresses, same bits +#define RegPaDac 0x4D // common differnet addresses, same bits +#elif defined(CFG_sx1272_radio) +#define RegTcxo 0x58 // common +#define RegPaDac 0x5A // common +#endif + +#define RegTcxo_TcxoInputOn (1u << 4) + +// ---------------------------------------- +// spread factors and mode for RegModemConfig2 +#define SX1272_MC2_FSK 0x00 +#define SX1272_MC2_SF7 0x70 +#define SX1272_MC2_SF8 0x80 +#define SX1272_MC2_SF9 0x90 +#define SX1272_MC2_SF10 0xA0 +#define SX1272_MC2_SF11 0xB0 +#define SX1272_MC2_SF12 0xC0 +// bandwidth for RegModemConfig1 +#define SX1272_MC1_BW_125 0x00 +#define SX1272_MC1_BW_250 0x40 +#define SX1272_MC1_BW_500 0x80 +// coding rate for RegModemConfig1 +#define SX1272_MC1_CR_4_5 0x08 +#define SX1272_MC1_CR_4_6 0x10 +#define SX1272_MC1_CR_4_7 0x18 +#define SX1272_MC1_CR_4_8 0x20 +#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive +#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02 +#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 +// transmit power configuration for RegPaConfig +#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80 +#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00 + + +// sx1276 RegModemConfig1 +#define SX1276_MC1_BW_125 0x70 +#define SX1276_MC1_BW_250 0x80 +#define SX1276_MC1_BW_500 0x90 +#define SX1276_MC1_CR_4_5 0x02 +#define SX1276_MC1_CR_4_6 0x04 +#define SX1276_MC1_CR_4_7 0x06 +#define SX1276_MC1_CR_4_8 0x08 + +#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 + +#ifdef CFG_sx1276_radio +# define SX127X_MC1_IMPLICIT_HEADER_MODE_ON SX1276_MC1_IMPLICIT_HEADER_MODE_ON +#else +# define SX127X_MC1_IMPLICIT_HEADER_MODE_ON SX1272_MC1_IMPLICIT_HEADER_MODE_ON +#endif + +// transmit power configuration for RegPaConfig +#define SX1276_PAC_PA_SELECT_PA_BOOST 0x80 +#define SX1276_PAC_PA_SELECT_RFIO_PIN 0x00 +#define SX1276_PAC_MAX_POWER_MASK 0x70 + +// the bits to change for max power. +#define SX127X_PADAC_POWER_MASK 0x07 +#define SX127X_PADAC_POWER_NORMAL 0x04 +#define SX127X_PADAC_POWER_20dBm 0x07 + +// convert milliamperes to equivalent value for +// RegOcp; delivers conservative value. +#define SX127X_OCP_MAtoBITS(mA) \ + ((mA) < 45 ? 0 : \ + (mA) <= 120 ? ((mA) - 45) / 5 : \ + (mA) < 130 ? 0xF : \ + (mA) < 240 ? ((mA) - 130) / 10 + 0x10 : \ + 27) + +// bit in RegOcp that enables overcurrent protect. +#define SX127X_OCP_ENA 0x20 + +// sx1276 RegModemConfig2 +#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04 + +// sx1276 RegModemConfig3 +#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08 +#define SX1276_MC3_AGCAUTO 0x04 + +// preamble for lora networks (nibbles swapped) +#define LORA_MAC_PREAMBLE 0x34 + +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A +#ifdef CFG_sx1276_radio +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70 +#elif CFG_sx1272_radio +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74 +#endif + +//----------------------------------------- +// Parameters for RSSI monitoring +#define SX127X_FREQ_LF_MAX 525000000 // per datasheet 6.3 + +// per datasheet 5.5.3 and 5.5.5: +#define SX1272_RSSI_ADJUST -139 // add to rssi value to get dB (LF) + +// per datasheet 5.5.3 and 5.5.5: +#define SX1276_RSSI_ADJUST_LF -164 // add to rssi value to get dB (LF) +#define SX1276_RSSI_ADJUST_HF -157 // add to rssi value to get dB (HF) + +#ifdef CFG_sx1276_radio +# define SX127X_RSSI_ADJUST_LF SX1276_RSSI_ADJUST_LF +# define SX127X_RSSI_ADJUST_HF SX1276_RSSI_ADJUST_HF +#else +# define SX127X_RSSI_ADJUST_LF SX1272_RSSI_ADJUST +# define SX127X_RSSI_ADJUST_HF SX1272_RSSI_ADJUST +#endif + +// per datasheet 2.5.2 (but note that we ought to ask Semtech to confirm, because +// datasheet is unclear). +#define SX127X_RX_POWER_UP us2osticks(500) // delay this long to let the receiver power up. + +// ---------------------------------------- +// Constants for radio registers +#define OPMODE_LORA 0x80 +#define OPMODE_MASK 0x07 +#define OPMODE_SLEEP 0x00 +#define OPMODE_STANDBY 0x01 +#define OPMODE_FSTX 0x02 +#define OPMODE_TX 0x03 +#define OPMODE_FSRX 0x04 +#define OPMODE_RX 0x05 +#define OPMODE_RX_SINGLE 0x06 +#define OPMODE_CAD 0x07 + +// ---------------------------------------- +// FSK opmode bits +// bits 6:5 are the same for 1272 and 1276 +#define OPMODE_FSK_SX127x_ModulationType_FSK (0u << 5) +#define OPMODE_FSK_SX127x_ModulationType_OOK (1u << 5) +#define OPMODE_FSK_SX127x_ModulationType_MASK (3u << 5) + +// bits 4:3 are different for 1272 +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_None (0u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT1_0 (1u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_5 (2u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_3 (3u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_None (0u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_BR (1u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_2BR (2u << 3) + +#define OPMODE_FSK_SX1272_ModulationShaping_MASK (3u << 3) + +// SX1276 +#define OPMODE_FSK_SX1276_LowFrequencyModeOn (1u << 3) + +// define the opmode bits apporpriate for the 127x in use. +#if defined(CFG_sx1272_radio) +# define OPMODE_FSK_SX127X_SETUP (OPMODE_FSK_SX127x_ModulationType_FSK | \ + OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_5) +#elif defined(CFG_sx1276_radio) +# define OPMODE_FSK_SX127X_SETUP (OPMODE_FSK_SX127x_ModulationType_FSK) +#endif + +// ---------------------------------------- +// LoRa opmode bits +#define OPMODE_LORA_SX127x_AccessSharedReg (1u << 6) +#define OPMODE_LORA_SX1276_LowFrequencyModeOn (1u << 3) + +// ---------------------------------------- +// Bits masking the corresponding IRQs from the radio +#define IRQ_LORA_RXTOUT_MASK 0x80 +#define IRQ_LORA_RXDONE_MASK 0x40 +#define IRQ_LORA_CRCERR_MASK 0x20 +#define IRQ_LORA_HEADER_MASK 0x10 +#define IRQ_LORA_TXDONE_MASK 0x08 +#define IRQ_LORA_CDDONE_MASK 0x04 +#define IRQ_LORA_FHSSCH_MASK 0x02 +#define IRQ_LORA_CDDETD_MASK 0x01 + +#define IRQ_FSK1_MODEREADY_MASK 0x80 +#define IRQ_FSK1_RXREADY_MASK 0x40 +#define IRQ_FSK1_TXREADY_MASK 0x20 +#define IRQ_FSK1_PLLLOCK_MASK 0x10 +#define IRQ_FSK1_RSSI_MASK 0x08 +#define IRQ_FSK1_TIMEOUT_MASK 0x04 +#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02 +#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01 +#define IRQ_FSK2_FIFOFULL_MASK 0x80 +#define IRQ_FSK2_FIFOEMPTY_MASK 0x40 +#define IRQ_FSK2_FIFOLEVEL_MASK 0x20 +#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10 +#define IRQ_FSK2_PACKETSENT_MASK 0x08 +#define IRQ_FSK2_PAYLOADREADY_MASK 0x04 +#define IRQ_FSK2_CRCOK_MASK 0x02 +#define IRQ_FSK2_LOWBAT_MASK 0x01 + +// ---------------------------------------- +// DIO function mappings D0D1D2D3 +#define MAP_DIO0_LORA_RXDONE 0x00 // 00------ +#define MAP_DIO0_LORA_TXDONE 0x40 // 01------ +#define MAP_DIO1_LORA_RXTOUT 0x00 // --00---- +#define MAP_DIO1_LORA_NOP 0x30 // --11---- +#define MAP_DIO2_LORA_NOP 0x0C // ----11-- + +#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) +#define MAP_DIO1_FSK_NOP 0x30 // --11---- +#define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- +#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- + + +// FSK IMAGECAL defines +#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RF_IMAGECAL_IMAGECAL_MASK 0xBF +#define RF_IMAGECAL_IMAGECAL_START 0x40 + +#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 +#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default + +// LNA gain constant. Bits 4..0 have different meaning for 1272 and 1276, but +// by chance, the bit patterns we use are the same. +#ifdef CFG_sx1276_radio +#define LNA_RX_GAIN (0x20|0x3) +#elif CFG_sx1272_radio +#define LNA_RX_GAIN (0x20|0x03) +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif + +// RADIO STATE +// (initialized by radio_init(), used by radio_rand1()) +static u1_t randbuf[16]; + + +static void writeReg (u1_t addr, u1_t data ) { + hal_spi_write(addr | 0x80, &data, 1); +} + +static u1_t readReg (u1_t addr) { + u1_t buf[1]; + hal_spi_read(addr & 0x7f, buf, 1); + return buf[0]; +} + +static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) { + hal_spi_write(addr | 0x80, buf, len); +} + +static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) { + hal_spi_read(addr & 0x7f, buf, len); +} + +static void requestModuleActive(bit_t state) { + ostime_t const ticks = hal_setModuleActive(state); + + if (ticks) + hal_waitUntil(os_getTime() + ticks);; +} + +static void writeOpmode(u1_t mode) { + u1_t const maskedMode = mode & OPMODE_MASK; + if (maskedMode != OPMODE_SLEEP) + requestModuleActive(1); + writeReg(RegOpMode, mode); + if (maskedMode == OPMODE_SLEEP) + requestModuleActive(0); +} + +static void opmode (u1_t mode) { + writeOpmode((readReg(RegOpMode) & ~OPMODE_MASK) | mode); +} + +static void opmodeLora() { + u1_t u = OPMODE_LORA; +#ifdef CFG_sx1276_radio + if (LMIC.freq <= SX127X_FREQ_LF_MAX) { + u |= OPMODE_FSK_SX1276_LowFrequencyModeOn; + } +#endif + writeOpmode(u); +} + +static void opmodeFSK() { + u1_t u = OPMODE_FSK_SX127X_SETUP; + +#ifdef CFG_sx1276_radio + if (LMIC.freq <= SX127X_FREQ_LF_MAX) { + u |= OPMODE_FSK_SX1276_LowFrequencyModeOn; + } +#endif + writeOpmode(u); +} + +// configure LoRa modem (cfg1, cfg2) +static void configLoraModem () { + sf_t sf = getSf(LMIC.rps); + +#ifdef CFG_sx1276_radio + u1_t mc1 = 0, mc2 = 0, mc3 = 0; + + bw_t const bw = getBw(LMIC.rps); + + switch (bw) { + case BW125: mc1 |= SX1276_MC1_BW_125; break; + case BW250: mc1 |= SX1276_MC1_BW_250; break; + case BW500: mc1 |= SX1276_MC1_BW_500; break; + default: + ASSERT(0); + } + switch( getCr(LMIC.rps) ) { + case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break; + case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break; + case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break; + case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break; + default: + ASSERT(0); + } + + if (getIh(LMIC.rps)) { + mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON; + writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length + } + // set ModemConfig1 + writeReg(LORARegModemConfig1, mc1); + + mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4) + ((LMIC.rxsyms >> 8) & 0x3) ); + if (getNocrc(LMIC.rps) == 0) { + mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON; + } +#if CFG_TxContinuousMode + // Only for testing + // set ModemConfig2 (sf, TxContinuousMode=1, AgcAutoOn=1 SymbTimeoutHi=00) + mc2 |= 0x8; +#endif + writeReg(LORARegModemConfig2, mc2); + + mc3 = SX1276_MC3_AGCAUTO; + + if ( ((sf == SF11 || sf == SF12) && bw == BW125) || + ((sf == SF12) && bw == BW250) ) { + mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE; + } + writeReg(LORARegModemConfig3, mc3); + + // Errata 2.1: Sensitivity optimization with 500 kHz bandwidth + u1_t rHighBwOptimize1; + u1_t rHighBwOptimize2; + + rHighBwOptimize1 = 0x03; + rHighBwOptimize2 = 0; + + if (bw == BW500) { + if (LMIC.freq > SX127X_FREQ_LF_MAX) { + rHighBwOptimize1 = 0x02; + rHighBwOptimize2 = 0x64; + } else { + rHighBwOptimize1 = 0x02; + rHighBwOptimize2 = 0x7F; + } + } + + writeReg(LORARegHighBwOptimize1, rHighBwOptimize1); + if (rHighBwOptimize2 != 0) + writeReg(LORARegHighBwOptimize2, rHighBwOptimize2); + +#elif CFG_sx1272_radio + u1_t mc1 = (getBw(LMIC.rps)<<6); + + switch( getCr(LMIC.rps) ) { + case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break; + case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break; + case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break; + case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break; + } + + if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) { + mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE; + } + + if (getNocrc(LMIC.rps) == 0) { + mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON; + } + + if (getIh(LMIC.rps)) { + mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON; + writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length + } + // set ModemConfig1 + writeReg(LORARegModemConfig1, mc1); + + // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi) + u1_t mc2; + mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04 | ((LMIC.rxsyms >> 8) & 0x3); + +#if CFG_TxContinuousMode + // Only for testing + // set ModemConfig2 (sf, TxContinuousMode=1, AgcAutoOn=1 SymbTimeoutHi=00) + mc2 |= 0x8; +#endif + + writeReg(LORARegModemConfig2, mc2); + +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif /* CFG_sx1272_radio */ +} + +static void configChannel () { + // set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19) + uint64_t frf = ((uint64_t)LMIC.freq << 19) / 32000000; + writeReg(RegFrfMsb, (u1_t)(frf>>16)); + writeReg(RegFrfMid, (u1_t)(frf>> 8)); + writeReg(RegFrfLsb, (u1_t)(frf>> 0)); +} + +// On the SX1276, we have several possible configs. +// 1) using RFO, MaxPower==0: in that case power is -4 to 11 dBm +// 2) using RFO, MaxPower==7: in that case, power is 0 to 14 dBm +// (can't select 15 dBm). +// note we can use -4..11 w/o Max and then 12..14 w/Max, and +// we really don't need to ask anybody. +// 3) using PA_BOOST, PaDac = 4: in that case power range is 2 to 17 dBm; +// use this for 15..17 if authorized. +// 4) using PA_BOOST, PaDac = 7, OutputPower=0xF: in that case, power is 20 dBm +// (and perhaps 0xE is 19, 0xD is 18 dBm, but datasheet isn't clear.) +// and duty cycle must be <= 1%. +// +// In addition, there are some boards for which PA_BOOST can only be used if the +// channel frequency is greater than SX127X_FREQ_LF_MAX. +// +// The SX1272 is similar but has no MaxPower bit: +// 1) using RFO: power is -1 to 13 dBm (datasheet implies max OutputPower value is 14 for 13 dBm) +// 2) using PA_BOOST, PaDac = 0x84: power is 2 to 17 dBm; +// use this for 14..17 if authorized +// 3) using PA_BOOST, PaDac = 0x87, OutputPower = 0xF: power is 20dBm +// and duty cycle must be <= 1% +// +// The general policy is to use the lowest power variant that will get us where we +// need to be. +// + +static void configPower () { + // our input paramter -- might be different than LMIC.txpow! + s1_t const req_pw = (s1_t)LMIC.radio_txpow; + // the effective power + s1_t eff_pw; + // the policy; we're going to compute this. + u1_t policy; + // what we'll write to RegPaConfig + u1_t rPaConfig; + // what we'll write to RegPaDac + u1_t rPaDac; + // what we'll write to RegOcp + u1_t rOcp; + +#ifdef CFG_sx1276_radio + if (req_pw >= 20) { + policy = LMICHAL_radio_tx_power_policy_20dBm; + eff_pw = 20; + } else if (req_pw >= 14) { + policy = LMICHAL_radio_tx_power_policy_paboost; + if (req_pw > 17) { + eff_pw = 17; + } else { + eff_pw = req_pw; + } + } else { + policy = LMICHAL_radio_tx_power_policy_rfo; + if (req_pw < -4) { + eff_pw = -4; + } else { + eff_pw = req_pw; + } + } + + policy = hal_getTxPowerPolicy(policy, eff_pw, LMIC.freq); + + switch (policy) { + default: + case LMICHAL_radio_tx_power_policy_rfo: + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(80); + + if (eff_pw > 14) + eff_pw = 14; + if (eff_pw > 11) { + // some Semtech code uses this down to eff_pw == 0. + rPaConfig = eff_pw | SX1276_PAC_MAX_POWER_MASK; + } else { + if (eff_pw < -4) + eff_pw = -4; + rPaConfig = eff_pw + 4; + } + break; + + // some radios (HopeRF RFM95W) don't support RFO well, + // so the policy might *raise* rfo to paboost. That means + // we have to re-check eff_pw, which might be too small. + // (And, of course, it might also be too large.) + case LMICHAL_radio_tx_power_policy_paboost: + // It seems that SX127x doesn't like eff_pw 10 when in FSK mode. + if (getSf(LMIC.rps) == FSK && eff_pw < 11) { + eff_pw = 11; + } + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(100); + if (eff_pw > 17) + eff_pw = 17; + else if (eff_pw < 2) + eff_pw = 2; + rPaConfig = (eff_pw - 2) | SX1276_PAC_PA_SELECT_PA_BOOST; + break; + + case LMICHAL_radio_tx_power_policy_20dBm: + rPaDac = SX127X_PADAC_POWER_20dBm; + rOcp = SX127X_OCP_MAtoBITS(130); + rPaConfig = 0xF | SX1276_PAC_PA_SELECT_PA_BOOST; + break; + } + +#elif CFG_sx1272_radio + if (req_pw >= 20) { + policy = LMICHAL_radio_tx_power_policy_20dBm; + eff_pw = 20; + } else if (eff_pw >= 14) { + policy = LMICHAL_radio_tx_power_policy_paboost; + if (eff_pw > 17) { + eff_pw = 17; + } else { + eff_pw = req_pw; + } + } else { + policy = LMICHAL_radio_tx_power_policy_rfo; + if (req_pw < -1) { + eff_pw = -1; + } else { + eff_pw = req_pw; + } + } + + policy = hal_getTxPowerPolicy(policy, eff_pw, LMIC.freq); + + switch (policy) { + default: + case LMICHAL_radio_tx_power_policy_rfo: + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(50); + + if (eff_pw > 13) + eff_pw = 13; + + rPaConfig = eff_pw + 1; + break; + + case LMICHAL_radio_tx_power_policy_paboost: + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(100); + + if (eff_pw > 17) + eff_pw = 17; + + rPaConfig = (eff_pw - 2) | SX1272_PAC_PA_SELECT_PA_BOOST; + break; + + case LMICHAL_radio_tx_power_policy_20dBm: + rPaDac = SX127X_PADAC_POWER_20dBm; + rOcp = SX127X_OCP_MAtoBITS(130); + + rPaConfig = 0xF | SX1276_PAC_PA_SELECT_PA_BOOST; + break; + } +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif /* CFG_sx1272_radio */ + + writeReg(RegPaConfig, rPaConfig); + writeReg(RegPaDac, (readReg(RegPaDac) & ~SX127X_PADAC_POWER_MASK) | rPaDac); + writeReg(RegOcp, rOcp | SX127X_OCP_ENA); +} + +static void setupFskRxTx(bit_t fDisableAutoClear) { + // set bitrate + writeReg(FSKRegBitrateMsb, 0x02); // 50kbps + writeReg(FSKRegBitrateLsb, 0x80); + // set frequency deviation + writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz + writeReg(FSKRegFdevLsb, 0x99); + + // set sync config + writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync + + // set packet config + writeReg(FSKRegPacketConfig1, fDisableAutoClear ? 0xD8 : 0xD0); // var-length, whitening, crc, no auto-clear, no adr filter + writeReg(FSKRegPacketConfig2, 0x40); // packet mode + + // set sync value + writeReg(FSKRegSyncValue1, 0xC1); + writeReg(FSKRegSyncValue2, 0x94); + writeReg(FSKRegSyncValue3, 0xC1); +} + +static void txfsk () { + // select FSK modem (from sleep mode) + opmodeFSK(); + + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // set bitrate etc + setupFskRxTx(/* don't autoclear CRC */ 0); + + // frame and packet handler settings + writeReg(FSKRegPreambleMsb, 0x00); + writeReg(FSKRegPreambleLsb, 0x05); + + // configure frequency + configChannel(); + // configure output power + configPower(); + +#ifdef CFG_sx1276_radio + // select Gausian filter BT=0.5, default ramp. + writeReg(RegPaRamp, 0x29); +#endif + + // set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP); + + // initialize the payload size and address pointers + // TODO(tmm@mcci.com): datasheet says this is not used in variable packet length mode + writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) + + // download length byte and buffer to the radio FIFO + writeReg(RegFifo, LMIC.dataLen); + writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); + + // enable antenna switch for TX + hal_pin_rxtx(1); + + // now we actually start the transmission + if (LMIC.txend) { + u4_t nLate = hal_waitUntil(LMIC.txend); // busy wait until exact tx time + if (nLate > 0) { + LMIC.radio.txlate_ticks += nLate; + ++LMIC.radio.txlate_count; + } + } + LMICOS_logEventUint32("+Tx FSK", LMIC.dataLen); + opmode(OPMODE_TX); +} + +static void txlora () { + // select LoRa modem (from sleep mode) + //writeReg(RegOpMode, OPMODE_LORA); + opmodeLora(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); + + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // configure LoRa modem (cfg1, cfg2) + configLoraModem(); + // configure frequency + configChannel(); + // configure output power +#ifdef CFG_sx1272_radio + writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec +#elif defined(CFG_sx1276_radio) + writeReg(RegPaRamp, 0x08); // set PA ramp-up time 50 uSec, clear FSK bits +#endif + configPower(); + // set sync word + writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); + + // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP); + // clear all radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + // mask all IRQs but TxDone + writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK); + + // initialize the payload size and address pointers + writeReg(LORARegFifoTxBaseAddr, 0x00); + writeReg(LORARegFifoAddrPtr, 0x00); + writeReg(LORARegPayloadLength, LMIC.dataLen); + + // download buffer to the radio FIFO + writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); + + // enable antenna switch for TX + hal_pin_rxtx(1); + + // now we actually start the transmission + if (LMIC.txend) { + u4_t nLate = hal_waitUntil(LMIC.txend); // busy wait until exact tx time + if (nLate) { + LMIC.radio.txlate_ticks += nLate; + ++LMIC.radio.txlate_count; + } + } + LMICOS_logEventUint32("+Tx LoRa", LMIC.dataLen); + opmode(OPMODE_TX); + +#if LMIC_DEBUG_LEVEL > 0 + u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 + u1_t bw = getBw(LMIC.rps); + u1_t cr = getCr(LMIC.rps); + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": TXMODE, freq=%"PRIu32", len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n", + os_getTime(), LMIC.freq, LMIC.dataLen, sf, + bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), + cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), + getIh(LMIC.rps) + ); +#endif +} + +// start transmitter (buf=LMIC.frame, len=LMIC.dataLen) +static void starttx () { + u1_t const rOpMode = readReg(RegOpMode); + + // originally, this code ASSERT()ed, but asserts are both bad and + // blunt instruments. If we see that we're not in sleep mode, + // force sleep (because we might have to switch modes) + if ((rOpMode & OPMODE_MASK) != OPMODE_SLEEP) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("?%s: OPMODE != OPMODE_SLEEP: %#02x\n", __func__, rOpMode); +#endif + opmode(OPMODE_SLEEP); + hal_waitUntil(os_getTime() + ms2osticks(1)); + } + + if (LMIC.lbt_ticks > 0) { + oslmic_radio_rssi_t rssi; + radio_monitor_rssi(LMIC.lbt_ticks, &rssi); +#if LMIC_X_DEBUG_LEVEL > 0 + LMIC_X_DEBUG_PRINTF("LBT rssi max:min=%d:%d %d times in %d\n", rssi.max_rssi, rssi.min_rssi, rssi.n_rssi, LMIC.lbt_ticks); +#endif + + if (rssi.max_rssi >= LMIC.lbt_dbmax) { + // complete the request by scheduling the job + os_setCallback(&LMIC.osjob, LMIC.osjob.func); + return; + } + } + + if(getSf(LMIC.rps) == FSK) { // FSK modem + txfsk(); + } else { // LoRa modem + txlora(); + } + // the radio will go back to STANDBY mode as soon as the TX is finished + // the corresponding IRQ will inform us about completion. +} + +enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI }; + +static CONST_TABLE(u1_t, rxlorairqmask)[] = { + [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK, + [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK, + [RXMODE_RSSI] = 0x00, +}; + +//! \brief handle late RX events. +//! \param nLate is the number of `ostime_t` ticks that the event was late. +//! \details If nLate is non-zero, increment the count of events, totalize +//! the number of ticks late, and (if implemented) adjust the estimate of +//! what would be best to return from `os_getRadioRxRampup()`. +static void rxlate (u4_t nLate) { + if (nLate) { + LMIC.radio.rxlate_ticks += nLate; + ++LMIC.radio.rxlate_count; + } +} + +// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen]) +static void rxlora (u1_t rxmode) { + // select LoRa modem (from sleep mode) + opmodeLora(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); + // enter standby mode (warm up)) + opmode(OPMODE_STANDBY); + // don't use MAC settings at startup + if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan + writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1); + writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2); + } else { // single or continuous rx mode + // configure LoRa modem (cfg1, cfg2) + configLoraModem(); + // configure frequency + configChannel(); + } + // set LNA gain + writeReg(RegLna, LNA_RX_GAIN); + // set max payload size + writeReg(LORARegPayloadMaxLength, MAX_LEN_FRAME); +#if !defined(DISABLE_INVERT_IQ_ON_RX) /* DEPRECATED(tmm@mcci.com); #250. remove test, always include code in V3 */ + // use inverted I/Q signal (prevent mote-to-mote communication) + + // XXX: use flag to switch on/off inversion + if (LMIC.noRXIQinversion) { + writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6)); + } else { + writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6)); + } +#endif + + // Errata 2.3 - receiver spurious reception of a LoRa signal + bw_t const bw = getBw(LMIC.rps); + u1_t const rDetectOptimize = (readReg(LORARegDetectOptimize) & 0x78) | 0x03; + if (bw < BW500) { + writeReg(LORARegDetectOptimize, rDetectOptimize); + writeReg(LORARegIffReq1, 0x40); + writeReg(LORARegIffReq2, 0x40); + } else { + writeReg(LORARegDetectOptimize, rDetectOptimize | 0x80); + } + + // set symbol timeout (for single rx) + writeReg(LORARegSymbTimeoutLsb, (uint8_t) LMIC.rxsyms); + // set sync word + writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); + + // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP); + // clear all radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + // enable required radio IRQs + writeReg(LORARegIrqFlagsMask, ~TABLE_GET_U1(rxlorairqmask, rxmode)); + + // enable antenna switch for RX + hal_pin_rxtx(0); + + writeReg(LORARegFifoAddrPtr, 0); + writeReg(LORARegFifoRxBaseAddr, 0); + + // now instruct the radio to receive + if (rxmode == RXMODE_SINGLE) { // single rx + u4_t nLate = hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time + opmode(OPMODE_RX_SINGLE); + LMICOS_logEventUint32("+Rx LoRa Single", nLate); + rxlate(nLate); +#if LMIC_DEBUG_LEVEL > 0 + ostime_t now = os_getTime(); + LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %"LMIC_PRId_ostime_t"\n", now - LMIC.rxtime); +#endif + } else { // continous rx (scan or rssi) + LMICOS_logEventUint32("+Rx LoRa Continuous", rxmode); + opmode(OPMODE_RX); + } + +#if LMIC_DEBUG_LEVEL > 0 + if (rxmode == RXMODE_RSSI) { + LMIC_DEBUG_PRINTF("RXMODE_RSSI\n"); + } else { + u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 + u1_t bw = getBw(LMIC.rps); + u1_t cr = getCr(LMIC.rps); + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s, freq=%"PRIu32", SF=%d, BW=%d, CR=4/%d, IH=%d\n", + os_getTime(), + rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"), + LMIC.freq, sf, + bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), + cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), + getIh(LMIC.rps) + ); + } +#endif +} + +static void rxfsk (u1_t rxmode) { + // only single or continuous rx (no noise sampling) + if (rxmode == RXMODE_SCAN) { + // indicate no bytes received. + LMIC.dataLen = 0; + // complete the request by scheduling the job. + os_setCallback(&LMIC.osjob, LMIC.osjob.func); + } + + // select FSK modem (from sleep mode) + //writeReg(RegOpMode, 0x00); // (not LoRa) + opmodeFSK(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0); + // enter standby mode (warm up)) + opmode(OPMODE_STANDBY); + // configure frequency + configChannel(); + // set LNA gain + writeReg(RegLna, LNA_RX_GAIN); // max gain, boost enable. + // configure receiver + writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? + // set receiver bandwidth + writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb + // set AFC bandwidth + writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB + // set preamble detection + writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors + // set preamble timeout + writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2); + // set bitrate, autoclear CRC + setupFskRxTx(1); + + // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut + writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); + + // enable antenna switch for RX + hal_pin_rxtx(0); + + // now instruct the radio to receive + if (rxmode == RXMODE_SINGLE) { + u4_t nLate = hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time + opmode(OPMODE_RX); // no single rx mode available in FSK + LMICOS_logEventUint32("+Rx FSK", nLate); + rxlate(nLate); + } else { + LMICOS_logEvent("+Rx FSK Continuous"); + opmode(OPMODE_RX); + } +} + +static void startrx (u1_t rxmode) { + ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); + if(getSf(LMIC.rps) == FSK) { // FSK modem + rxfsk(rxmode); + } else { // LoRa modem + rxlora(rxmode); + } + // the radio will go back to STANDBY mode as soon as the RX is finished + // or timed out, and the corresponding IRQ will inform us about completion. +} + +//! \brief Initialize radio at system startup. +//! +//! \details This procedure is called during initialization by the `os_init()` +//! routine. It does a hardware reset of the radio, checks the version and confirms +//! that we're operating a suitable chip, and gets a random seed from wideband +//! noise rssi. It then puts the radio to sleep. +//! +//! \result True if successful, false if it doesn't look like the right radio is attached. +//! +//! \pre +//! Preconditions must be observed, or you'll get hangs during initialization. +//! +//! - The `hal_pin_..()` functions must be ready for use. +//! - The `hal_waitUntl()` function must be ready for use. This may mean that interrupts +//! are enabled. +//! - The `hal_spi_..()` functions must be ready for use. +//! +//! Generally, all these are satisfied by a call to `hal_init_with_pinmap()`. +//! +int radio_init () { + requestModuleActive(1); + + // manually reset radio +#ifdef CFG_sx1276_radio + hal_pin_rst(0); // drive RST pin low +#else + hal_pin_rst(1); // drive RST pin high +#endif + hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us + hal_pin_rst(2); // configure RST pin floating! + hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms + + opmode(OPMODE_SLEEP); + + // some sanity checks, e.g., read version number + u1_t v = readReg(RegVersion); +#ifdef CFG_sx1276_radio + if(v != 0x12 ) + return 0; +#elif CFG_sx1272_radio + if(v != 0x22) + return 0; +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif + // set the tcxo input, if needed + if (hal_queryUsingTcxo()) + writeReg(RegTcxo, readReg(RegTcxo) | RegTcxo_TcxoInputOn); + + // seed 15-byte randomness via noise rssi + rxlora(RXMODE_RSSI); + while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx + for(int i=1; i<16; i++) { + for(int j=0; j<8; j++) { + u1_t b; // wait for two non-identical subsequent least-significant bits + while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) ); + randbuf[i] = (randbuf[i] << 1) | b; + } + } + randbuf[0] = 16; // set initial index + +#ifdef CFG_sx1276mb1_board + // chain calibration + writeReg(RegPaConfig, 0); + + // Launch Rx chain calibration for LF band + writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); + while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; } + + // Sets a Frequency in HF band + u4_t frf = 868000000; + writeReg(RegFrfMsb, (u1_t)(frf>>16)); + writeReg(RegFrfMid, (u1_t)(frf>> 8)); + writeReg(RegFrfLsb, (u1_t)(frf>> 0)); + + // Launch Rx chain calibration for HF band + writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); + while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; } +#endif /* CFG_sx1276mb1_board */ + + opmode(OPMODE_SLEEP); + + return 1; +} + +// return next random byte derived from seed buffer +// (buf[0] holds index of next byte to be returned) +u1_t radio_rand1 () { + u1_t i = randbuf[0]; + ASSERT( i != 0 ); + if( i==16 ) { + os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key + i = 0; + } + u1_t v = randbuf[i++]; + randbuf[0] = i; + return v; +} + +u1_t radio_rssi () { + u1_t r = readReg(LORARegRssiValue); + return r; +} + +/// \brief get the current RSSI on the current channel. +/// +/// monitor rssi for specified number of ostime_t ticks, and return statistics +/// This puts the radio into RX continuous mode, waits long enough for the +/// oscillators to start and the PLL to lock, and then measures for the specified +/// period of time. The radio is then returned to idle. +/// +/// RSSI returned is expressed in units of dB, and is offset according to the +/// current radio setting per section 5.5.5 of Semtech 1276 datasheet. +/// +/// \param nTicks How long to monitor +/// \param pRssi pointer to structure to fill in with RSSI data. +/// +void radio_monitor_rssi(ostime_t nTicks, oslmic_radio_rssi_t *pRssi) { + uint8_t rssiMax, rssiMin; + uint16_t rssiSum; + uint16_t rssiN; + + int rssiAdjust; + ostime_t tBegin; + int notDone; + + rxlora(RXMODE_SCAN); + + // while we're waiting for the PLLs to spin up, determine which + // band we're in and choose the base RSSI. +#if defined(CFG_sx1276_radio) + if (LMIC.freq > SX127X_FREQ_LF_MAX) { + rssiAdjust = SX1276_RSSI_ADJUST_HF; + } else { + rssiAdjust = SX1276_RSSI_ADJUST_LF; + } +#elif defined(CFG_sx1272_radio) + rssiAdjust = SX1272_RSSI_ADJUST; +#endif + rssiAdjust += hal_getRssiCal(); + + // zero the results + rssiMax = 255; + rssiMin = 0; + rssiSum = 0; + rssiN = 0; + + // wait for PLLs + hal_waitUntil(os_getTime() + SX127X_RX_POWER_UP); + + // scan for the desired time. + tBegin = os_getTime(); + rssiMax = 0; + + /* Per bug report from tanupoo, it's critical that interrupts be enabled + * in the loop below so that `os_getTime()` always advances. + */ + do { + ostime_t now; + + u1_t rssiNow = readReg(LORARegRssiValue); + + if (rssiMax < rssiNow) + rssiMax = rssiNow; + if (rssiNow < rssiMin) + rssiMin = rssiNow; + rssiSum += rssiNow; + ++rssiN; + now = os_getTime(); + notDone = now - (tBegin + nTicks) < 0; + } while (notDone); + + // put radio back to sleep + opmode(OPMODE_SLEEP); + + // compute the results + pRssi->max_rssi = (s2_t) (rssiMax + rssiAdjust); + pRssi->min_rssi = (s2_t) (rssiMin + rssiAdjust); + pRssi->mean_rssi = (s2_t) (rssiAdjust + ((rssiSum + (rssiN >> 1)) / rssiN)); + pRssi->n_rssi = rssiN; +} + +static CONST_TABLE(u2_t, LORA_RXDONE_FIXUP)[] = { + [FSK] = us2osticks(0), // ( 0 ticks) + [SF7] = us2osticks(0), // ( 0 ticks) + [SF8] = us2osticks(1648), // ( 54 ticks) + [SF9] = us2osticks(3265), // ( 107 ticks) + [SF10] = us2osticks(7049), // ( 231 ticks) + [SF11] = us2osticks(13641), // ( 447 ticks) + [SF12] = us2osticks(31189), // (1022 ticks) +}; + +// called by hal ext IRQ handler +// (radio goes to stanby mode after tx/rx operations) +void radio_irq_handler (u1_t dio) { + radio_irq_handler_v2(dio, os_getTime()); +} + +void radio_irq_handler_v2 (u1_t dio, ostime_t now) { + LMIC_API_PARAMETER(dio); + +#if CFG_TxContinuousMode + // in continuous mode, we don't use the now parameter. + LMIC_UNREFERENCED_PARAMETER(now); + + // clear radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + u1_t p = readReg(LORARegFifoAddrPtr); + writeReg(LORARegFifoAddrPtr, 0x00); + u1_t s = readReg(RegOpMode); + u1_t c = readReg(LORARegModemConfig2); + LMICOS_logEventUint32("+Tx LoRa Continuous", (r << 8) + c); + opmode(OPMODE_TX); + return; +#else /* ! CFG_TxContinuousMode */ + +#if LMIC_DEBUG_LEVEL > 0 + ostime_t const entry = now; +#endif + if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem + u1_t flags = readReg(LORARegIrqFlags); + LMIC.saveIrqFlags = flags; + LMICOS_logEventUint32("radio_irq_handler_v2: LoRa", flags); + LMIC_X_DEBUG_PRINTF("IRQ=%02x\n", flags); + if( flags & IRQ_LORA_TXDONE_MASK ) { + // save exact tx time + LMIC.txend = now - us2osticks(43); // TXDONE FIXUP + } else if( flags & IRQ_LORA_RXDONE_MASK ) { + // save exact rx time + if(getBw(LMIC.rps) == BW125) { + now -= TABLE_GET_U2(LORA_RXDONE_FIXUP, getSf(LMIC.rps)); + } + LMIC.rxtime = now; + // read the PDU and inform the MAC that we received something + LMIC.dataLen = (readReg(LORARegModemConfig1) & SX127X_MC1_IMPLICIT_HEADER_MODE_ON) ? + readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes); + // set FIFO read address pointer + writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); + // now read the FIFO + readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read rx quality parameters + LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 + u1_t const rRssi = readReg(LORARegPktRssiValue); + s2_t rssi = rRssi; + if (LMIC.freq > SX127X_FREQ_LF_MAX) + rssi += SX127X_RSSI_ADJUST_HF; + else + rssi += SX127X_RSSI_ADJUST_LF; + if (LMIC.snr < 0) + rssi = rssi - (-LMIC.snr >> 2); + else if (rssi > -100) { + // correct nonlinearity -- this is the same as multiplying rRssi * 16/15 initially. + rssi += (rRssi / 15); + } + + LMIC_X_DEBUG_PRINTF("RX snr=%u rssi=%d\n", LMIC.snr/4, rssi); + // ugh compatibility requires a biased range. RSSI + LMIC.rssi = (s1_t) (RSSI_OFF + (rssi < -196 ? -196 : rssi > 63 ? 63 : rssi)); // RSSI [dBm] (-196...+63) + } else if( flags & IRQ_LORA_RXTOUT_MASK ) { + // indicate timeout + LMIC.dataLen = 0; +#if LMIC_DEBUG_LEVEL > 0 + ostime_t now2 = os_getTime(); + LMIC_DEBUG_PRINTF("rxtimeout: entry: %"LMIC_PRId_ostime_t" rxtime: %"LMIC_PRId_ostime_t" entry-rxtime: %"LMIC_PRId_ostime_t" now-entry: %"LMIC_PRId_ostime_t" rxtime-txend: %"LMIC_PRId_ostime_t"\n", entry, + LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend); +#endif + } + // mask all radio IRQs + writeReg(LORARegIrqFlagsMask, 0xFF); + // clear radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + } else { // FSK modem + u1_t flags1 = readReg(FSKRegIrqFlags1); + u1_t flags2 = readReg(FSKRegIrqFlags2); + + LMICOS_logEventUint32("*radio_irq_handler_v2: FSK", ((u2_t)flags2 << 8) | flags1); + + if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) { + // save exact tx time + LMIC.txend = now; + } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) { + // save exact rx time + LMIC.rxtime = now; + // read the PDU and inform the MAC that we received something + LMIC.dataLen = readReg(FSKRegPayloadLength); + // now read the FIFO + readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read rx quality parameters + LMIC.snr = 0; // SX127x doesn't give SNR for FSK. + LMIC.rssi = -64 + RSSI_OFF; // SX127x doesn't give packet RSSI for FSK, + // so substitute a dummy value. + } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) { + // indicate timeout + LMIC.dataLen = 0; + } else { + // ASSERT(0); + // we're not sure why we're here... treat as timeout. + LMIC.dataLen = 0; + } + + // in FSK, we need to put the radio in standby first. + opmode(OPMODE_STANDBY); + } + // go from standby to sleep + opmode(OPMODE_SLEEP); + // run os job (use preset func ptr) + os_setCallback(&LMIC.osjob, LMIC.osjob.func); +#endif /* ! CFG_TxContinuousMode */ +} + +/*! + +\brief Initiate a radio operation. + +\param mode Selects the operation to be performed. + +The requested radio operation is initiated. Some operations complete +immediately; others require hardware to do work, and don't complete until +an interrupt occurs. In that case, `LMIC.osjob` is scheduled. Because the +interrupt may occur right away, it's important that the caller initialize +`LMIC.osjob` before calling this routine. + +- `RADIO_RST` causes the radio to be put to sleep. No interrupt follows; +when control returns, the radio is ready for the next operation. + +- `RADIO_TX` and `RADIO_TX_AT` launch the transmission of a frame. An interrupt will +occur, which will cause `LMIC.osjob` to be scheduled with its current +function. + +- `RADIO_RX` and `RADIO_RX_ON` launch either single or continuous receives. +An interrupt will occur when a packet is recieved or the receive times out, +which will cause `LMIC.osjob` to be scheduled with its current function. + +*/ + +void os_radio (u1_t mode) { + switch (mode) { + case RADIO_RST: + // put radio to sleep + opmode(OPMODE_SLEEP); + break; + + case RADIO_TX: + // transmit frame now + LMIC.txend = 0; + starttx(); // buf=LMIC.frame, len=LMIC.dataLen + break; + + case RADIO_TX_AT: + if (LMIC.txend == 0) + LMIC.txend = 1; + starttx(); + break; + + case RADIO_RX: + // receive frame now (exactly at rxtime) + startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms + break; + + case RADIO_RXON: + // start scanning for beacon now + startrx(RXMODE_SCAN); // buf=LMIC.frame + break; + } +} + +ostime_t os_getRadioRxRampup (void) { + return RX_RAMPUP_DEFAULT; +} diff --git a/lora-test-receiver/lora-test-receiver.ino b/lora-test-receiver/lora-test-receiver.ino new file mode 100644 index 0000000..72daed8 --- /dev/null +++ b/lora-test-receiver/lora-test-receiver.ino @@ -0,0 +1,138 @@ +/********* + Rui Santos + Complete project details at https://RandomNerdTutorials.com/ttgo-lora32-sx1276-arduino-ide/ +*********/ +#define WIFI +//Libraries for LoRa +#include +#include + +//Libraries for OLED Display +#include +#include +#include + +//define the pins used by the LoRa transceiver module +#define SCK 5 +#define MISO 19 +#define MOSI 27 +#define SS 18 +#define RST 14 +#define DIO0 26 + +//433E6 for Asia +//866E6 for Europe +//915E6 for North America +#define BAND 866E6 + +//OLED pins +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST); + + +#include + + +const char* ssid = "pipanet"; // WiFi SSID +const char* password = "passatvr6"; // WiFi Password + + + + + +String LoRaData; + +void setup() { + //initialize Serial Monitor + Serial.begin(115200); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(100); + } + + Serial.println(); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.println( WiFi.localIP().toString()); + //reset OLED display via software + pinMode(OLED_RST, OUTPUT); + digitalWrite(OLED_RST, LOW); + delay(20); + digitalWrite(OLED_RST, HIGH); + + //initialize OLED + Wire.begin(OLED_SDA, OLED_SCL); + if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) { // Address 0x3C for 128x32 + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + + + + display.clearDisplay(); + display.setTextColor(WHITE); + display.setTextSize(1); + display.setCursor(0,0); + display.print("LORA RECEIVER "); + display.display(); + + + + Serial.println("LoRa Receiver Test"); + + //SPI LoRa pins + SPI.begin(SCK, MISO, MOSI, SS); + //setup LoRa transceiver module + LoRa.setPins(SS, RST, DIO0); + + if (!LoRa.begin(BAND)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + Serial.println("LoRa Initializing OK!"); + display.setCursor(0,10); + display.println("LoRa Initializing OK!"); + display.display(); +} + +void loop() { + + //try to parse packet + int packetSize = LoRa.parsePacket(); + if (packetSize) { + //received a packet + Serial.print("Received packet "); + + //read packet + while (LoRa.available()) { + LoRaData = LoRa.readString(); + Serial.print(LoRaData); + } + + //print RSSI of packet + int rssi = LoRa.packetRssi(); + Serial.print(" with RSSI "); + Serial.println(rssi); + + // Dsiplay information + display.clearDisplay(); + display.setCursor(0,0); + display.print("LORA RECEIVER"); + display.setCursor(0,20); + display.print("Received packet:"); + display.setCursor(0,30); + display.print(LoRaData); + display.setCursor(0,40); + display.print("RSSI:"); + display.setCursor(30,40); + display.print(rssi); + display.display(); + } +} diff --git a/lora-test-sender/lora-test-sender.ino b/lora-test-sender/lora-test-sender.ino new file mode 100644 index 0000000..4ee34df --- /dev/null +++ b/lora-test-sender/lora-test-sender.ino @@ -0,0 +1,109 @@ +/********* + Rui Santos + Complete project details at https://RandomNerdTutorials.com/ttgo-lora32-sx1276-arduino-ide/ +*********/ + +//Libraries for LoRa +#include +#include + +//Libraries for OLED Display +#include +#include +#include + +//define the pins used by the LoRa transceiver module +#define SCK 5 +#define MISO 19 +#define MOSI 27 +#define SS 18 +#define RST 14 +#define DIO0 26 + +//433E6 for Asia +//866E6 for Europe +//915E6 for North America +#define BAND 866E6 + +//OLED pins +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +//packet counter +int counter = 0; + +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST); + +void setup() { + + //reset OLED display via software + pinMode(OLED_RST, OUTPUT); + digitalWrite(OLED_RST, LOW); + delay(20); + digitalWrite(OLED_RST, HIGH); + + //initialize OLED + Wire.begin(OLED_SDA, OLED_SCL); + if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) { // Address 0x3C for 128x32 + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + display.clearDisplay(); + display.setTextColor(WHITE); + display.setTextSize(1); + display.setCursor(0,0); + display.print("LORA SENDER "); + display.display(); + + //initialize Serial Monitor + Serial.begin(115200); + + Serial.println("LoRa Sender Test"); + + //SPI LoRa pins + SPI.begin(SCK, MISO, MOSI, SS); + //setup LoRa transceiver module + LoRa.setPins(SS, RST, DIO0); + + if (!LoRa.begin(BAND)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + Serial.println("LoRa Initializing OK!"); + display.setCursor(0,10); + display.print("LoRa Initializing OK!"); + display.display(); + delay(2000); +} + +void loop() { + + Serial.print("Sending packet: "); + Serial.println(counter); + + //Send LoRa packet to receiver + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + display.clearDisplay(); + display.setCursor(0,0); + display.println("LORA SENDER"); + display.setCursor(0,20); + display.setTextSize(1); + display.print("LoRa packet sent."); + display.setCursor(0,30); + display.print("Counter:"); + display.setCursor(50,30); + display.print(counter); + display.display(); + + counter++; + + delay(1000); +}