LoRa, MQTT and Oled
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
__pycache__/
|
||||||
Vendored
+4
-2
@@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
"python.analysis.extraPaths": [
|
"python.analysis.extraPaths": [
|
||||||
"${workspaceFolder}",
|
"${workspaceFolder}",
|
||||||
"${workspaceFolder}/shared"
|
"${workspaceFolder}/shared",
|
||||||
|
"${workspaceFolder}/micro_ondes/esp_lora/lib"
|
||||||
],
|
],
|
||||||
"python.autoComplete.extraPaths": [
|
"python.autoComplete.extraPaths": [
|
||||||
"${workspaceFolder}",
|
"${workspaceFolder}",
|
||||||
"${workspaceFolder}/shared"
|
"${workspaceFolder}/shared",
|
||||||
|
"${workspaceFolder}/micro_ondes/esp_lora/lib"
|
||||||
],
|
],
|
||||||
"python.defaultInterpreterPath": "${workspaceFolder}/venv/bin/python",
|
"python.defaultInterpreterPath": "${workspaceFolder}/venv/bin/python",
|
||||||
"r.lsp.promptToInstall": false,
|
"r.lsp.promptToInstall": false,
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
|
|
||||||
`export PYTHONPATH=/home/pi/SmartWave`
|
`export PYTHONPATH=/home/pi/SmartWave`
|
||||||
|
|
||||||
|
`export PYTHONPATH=/home/ninluc/Documents/school/IoT/smartWave`
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def hello_world():
|
||||||
|
return "<p>Hello, World!</p>"
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
# Configuration des ports (À remplacer par tes propres chemins by-id)
|
# Configuration des ports (À remplacer par tes propres chemins by-id)
|
||||||
# Pour trouver tes chemins, branche tes ESP et tape : ls -l /dev/serial/by-id/
|
# Pour trouver tes chemins, branche tes ESP et tape : ls -l /dev/serial/by-id/
|
||||||
PORT_ESP_WIFI="/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0"
|
PORT_ESP_WIFI="/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port1"
|
||||||
PORT_ESP_LORA="/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0002-if00-port0"
|
PORT_ESP_LORA="/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0"
|
||||||
# Ajoute les autres si besoin...
|
# Ajoute les autres si besoin...
|
||||||
|
|
||||||
# Configuration Raspberry Pi
|
# Configuration Raspberry Pi
|
||||||
RPI_HOST="10.85.213.190"
|
RPI_HOST="192.168.50.1"
|
||||||
RPI_USER="pi"
|
RPI_USER="pi"
|
||||||
RPI_DEST="/home/pi/SmartWave"
|
RPI_DEST="/home/pi/SmartWave"
|
||||||
RPI_SYSTEMD_SERVICE="smartwave.service"
|
RPI_SYSTEMD_SERVICE="smartwave.service"
|
||||||
@@ -44,10 +44,10 @@ deploy_to_esp() {
|
|||||||
mpremote connect "$PORT" cp -r "$TARGET_DIR"/* :
|
mpremote connect "$PORT" cp -r "$TARGET_DIR"/* :
|
||||||
|
|
||||||
echo "📦 Copie des certificats MQTT..."
|
echo "📦 Copie des certificats MQTT..."
|
||||||
mpremote connect "$PORT" cp -r orchestrateur/mqtt/certs :certs
|
mpremote connect "$PORT" cp -r orchestrateur/mqtt/certs/ :
|
||||||
|
|
||||||
echo "🔄 Redémarrage de l'ESP..."
|
echo "🔄 Redémarrage de l'ESP..."
|
||||||
mpremote connect "$PORT" soft-reset
|
mpremote connect "$PORT" reset
|
||||||
|
|
||||||
echo "✅ [$NAME] mis à jour avec succès !"
|
echo "✅ [$NAME] mis à jour avec succès !"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
# LoRa
|
||||||
|
|
||||||
|
`mpremote connect /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 repl`
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# This file is executed on every boot (including wake-boot from deepsleep)
|
||||||
|
#import esp
|
||||||
|
#esp.osdebug(None)
|
||||||
|
#import webrepl
|
||||||
|
#webrepl.start()
|
||||||
|
|
||||||
|
from machine import Pin
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
|
print("--- Démarrage de l'ESP32 (boot.py) ---")
|
||||||
|
|
||||||
|
# On s'assure que la ligne d'alimentation Vext est allumée dès le boot
|
||||||
|
vext = Pin(36, Pin.OUT)
|
||||||
|
vext.value(0) # Allume l'alimentation écran/LoRa
|
||||||
|
|
||||||
|
# On initialise le reset de l'OLED pour qu'il ne reste pas figé
|
||||||
|
oled_rst = Pin(21, Pin.OUT)
|
||||||
|
oled_rst.value(0)
|
||||||
|
time.sleep(0.05)
|
||||||
|
oled_rst.value(1)
|
||||||
|
|
||||||
|
print("Matériel de base initialisé. Lancement de main.py...")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
from sys import implementation
|
||||||
|
|
||||||
|
if implementation.name == 'micropython':
|
||||||
|
from utime import sleep_ms
|
||||||
|
|
||||||
|
if implementation.name == 'circuitpython':
|
||||||
|
from time import sleep
|
||||||
|
def sleep_ms(ms):
|
||||||
|
sleep(ms/1000)
|
||||||
|
|
||||||
|
def ASSERT(state):
|
||||||
|
assert state == ERR_NONE, ERROR[state]
|
||||||
|
|
||||||
|
def yield_():
|
||||||
|
sleep_ms(1)
|
||||||
|
|
||||||
|
SX126X_FREQUENCY_STEP_SIZE = 0.9536743164
|
||||||
|
SX126X_MAX_PACKET_LENGTH = const(255)
|
||||||
|
SX126X_CRYSTAL_FREQ = 32.0
|
||||||
|
SX126X_DIV_EXPONENT = const(25)
|
||||||
|
SX126X_CMD_NOP = const(0x00)
|
||||||
|
SX126X_CMD_SET_SLEEP = const(0x84)
|
||||||
|
SX126X_CMD_SET_STANDBY = const(0x80)
|
||||||
|
SX126X_CMD_SET_FS = const(0xC1)
|
||||||
|
SX126X_CMD_SET_TX = const(0x83)
|
||||||
|
SX126X_CMD_SET_RX = const(0x82)
|
||||||
|
SX126X_CMD_STOP_TIMER_ON_PREAMBLE = const(0x9F)
|
||||||
|
SX126X_CMD_SET_RX_DUTY_CYCLE = const(0x94)
|
||||||
|
SX126X_CMD_SET_CAD = const(0xC5)
|
||||||
|
SX126X_CMD_SET_TX_CONTINUOUS_WAVE = const(0xD1)
|
||||||
|
SX126X_CMD_SET_TX_INFINITE_PREAMBLE = const(0xD2)
|
||||||
|
SX126X_CMD_SET_REGULATOR_MODE = const(0x96)
|
||||||
|
SX126X_CMD_CALIBRATE = const(0x89)
|
||||||
|
SX126X_CMD_CALIBRATE_IMAGE = const(0x98)
|
||||||
|
SX126X_CMD_SET_PA_CONFIG = const(0x95)
|
||||||
|
SX126X_CMD_SET_RX_TX_FALLBACK_MODE = const(0x93)
|
||||||
|
SX126X_CMD_WRITE_REGISTER = const(0x0D)
|
||||||
|
SX126X_CMD_READ_REGISTER = const(0x1D)
|
||||||
|
SX126X_CMD_WRITE_BUFFER = const(0x0E)
|
||||||
|
SX126X_CMD_READ_BUFFER = const(0x1E)
|
||||||
|
SX126X_CMD_SET_DIO_IRQ_PARAMS = const(0x08)
|
||||||
|
SX126X_CMD_GET_IRQ_STATUS = const(0x12)
|
||||||
|
SX126X_CMD_CLEAR_IRQ_STATUS = const(0x02)
|
||||||
|
SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL = const(0x9D)
|
||||||
|
SX126X_CMD_SET_DIO3_AS_TCXO_CTRL = const(0x97)
|
||||||
|
SX126X_CMD_SET_RF_FREQUENCY = const(0x86)
|
||||||
|
SX126X_CMD_SET_PACKET_TYPE = const(0x8A)
|
||||||
|
SX126X_CMD_GET_PACKET_TYPE = const(0x11)
|
||||||
|
SX126X_CMD_SET_TX_PARAMS = const(0x8E)
|
||||||
|
SX126X_CMD_SET_MODULATION_PARAMS = const(0x8B)
|
||||||
|
SX126X_CMD_SET_PACKET_PARAMS = const(0x8C)
|
||||||
|
SX126X_CMD_SET_CAD_PARAMS = const(0x88)
|
||||||
|
SX126X_CMD_SET_BUFFER_BASE_ADDRESS = const(0x8F)
|
||||||
|
SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT = const(0x0A)
|
||||||
|
SX126X_CMD_GET_STATUS = const(0xC0)
|
||||||
|
SX126X_CMD_GET_RSSI_INST = const(0x15)
|
||||||
|
SX126X_CMD_GET_RX_BUFFER_STATUS = const(0x13)
|
||||||
|
SX126X_CMD_GET_PACKET_STATUS = const(0x14)
|
||||||
|
SX126X_CMD_GET_DEVICE_ERRORS = const(0x17)
|
||||||
|
SX126X_CMD_CLEAR_DEVICE_ERRORS = const(0x07)
|
||||||
|
SX126X_CMD_GET_STATS = const(0x10)
|
||||||
|
SX126X_CMD_RESET_STATS = const(0x00)
|
||||||
|
SX126X_REG_WHITENING_INITIAL_MSB = const(0x06B8)
|
||||||
|
SX126X_REG_WHITENING_INITIAL_LSB = const(0x06B9)
|
||||||
|
SX126X_REG_CRC_INITIAL_MSB = const(0x06BC)
|
||||||
|
SX126X_REG_CRC_INITIAL_LSB = const(0x06BD)
|
||||||
|
SX126X_REG_CRC_POLYNOMIAL_MSB = const(0x06BE)
|
||||||
|
SX126X_REG_CRC_POLYNOMIAL_LSB = const(0x06BF)
|
||||||
|
SX126X_REG_SYNC_WORD_0 = const(0x06C0)
|
||||||
|
SX126X_REG_SYNC_WORD_1 = const(0x06C1)
|
||||||
|
SX126X_REG_SYNC_WORD_2 = const(0x06C2)
|
||||||
|
SX126X_REG_SYNC_WORD_3 = const(0x06C3)
|
||||||
|
SX126X_REG_SYNC_WORD_4 = const(0x06C4)
|
||||||
|
SX126X_REG_SYNC_WORD_5 = const(0x06C5)
|
||||||
|
SX126X_REG_SYNC_WORD_6 = const(0x06C6)
|
||||||
|
SX126X_REG_SYNC_WORD_7 = const(0x06C7)
|
||||||
|
SX126X_REG_NODE_ADDRESS = const(0x06CD)
|
||||||
|
SX126X_REG_BROADCAST_ADDRESS = const(0x06CE)
|
||||||
|
SX126X_REG_LORA_SYNC_WORD_MSB = const(0x0740)
|
||||||
|
SX126X_REG_LORA_SYNC_WORD_LSB = const(0x0741)
|
||||||
|
SX126X_REG_RANDOM_NUMBER_0 = const(0x0819)
|
||||||
|
SX126X_REG_RANDOM_NUMBER_1 = const(0x081A)
|
||||||
|
SX126X_REG_RANDOM_NUMBER_2 = const(0x081B)
|
||||||
|
SX126X_REG_RANDOM_NUMBER_3 = const(0x081C)
|
||||||
|
SX126X_REG_RX_GAIN = const(0x08AC)
|
||||||
|
SX126X_REG_OCP_CONFIGURATION = const(0x08E7)
|
||||||
|
SX126X_REG_XTA_TRIM = const(0x0911)
|
||||||
|
SX126X_REG_XTB_TRIM = const(0x0912)
|
||||||
|
SX126X_REG_SENSITIVITY_CONFIG = const(0x0889)
|
||||||
|
SX126X_REG_TX_CLAMP_CONFIG = const(0x08D8)
|
||||||
|
SX126X_REG_RTC_STOP = const(0x0920)
|
||||||
|
SX126X_REG_RTC_EVENT = const(0x0944)
|
||||||
|
SX126X_REG_IQ_CONFIG = const(0x0736)
|
||||||
|
SX126X_REG_RX_GAIN_RETENTION_0 = const(0x029F)
|
||||||
|
SX126X_REG_RX_GAIN_RETENTION_1 = const(0x02A0)
|
||||||
|
SX126X_REG_RX_GAIN_RETENTION_2 = const(0x02A1)
|
||||||
|
SX126X_SLEEP_START_COLD = const(0b00000000)
|
||||||
|
SX126X_SLEEP_START_WARM = const(0b00000100)
|
||||||
|
SX126X_SLEEP_RTC_OFF = const(0b00000000)
|
||||||
|
SX126X_SLEEP_RTC_ON = const(0b00000001)
|
||||||
|
SX126X_STANDBY_RC = const(0x00)
|
||||||
|
SX126X_STANDBY_XOSC = const(0x01)
|
||||||
|
SX126X_RX_TIMEOUT_NONE = const(0x000000)
|
||||||
|
SX126X_RX_TIMEOUT_INF = const(0xFFFFFF)
|
||||||
|
SX126X_TX_TIMEOUT_NONE = const(0x000000)
|
||||||
|
SX126X_STOP_ON_PREAMBLE_OFF = const(0x00)
|
||||||
|
SX126X_STOP_ON_PREAMBLE_ON = const(0x01)
|
||||||
|
SX126X_REGULATOR_LDO = const(0x00)
|
||||||
|
SX126X_REGULATOR_DC_DC = const(0x01)
|
||||||
|
SX126X_CALIBRATE_IMAGE_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_IMAGE_ON = const(0b01000000)
|
||||||
|
SX126X_CALIBRATE_ADC_BULK_P_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_ADC_BULK_P_ON = const(0b00100000)
|
||||||
|
SX126X_CALIBRATE_ADC_BULK_N_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_ADC_BULK_N_ON = const(0b00010000)
|
||||||
|
SX126X_CALIBRATE_ADC_PULSE_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_ADC_PULSE_ON = const(0b00001000)
|
||||||
|
SX126X_CALIBRATE_PLL_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_PLL_ON = const(0b00000100)
|
||||||
|
SX126X_CALIBRATE_RC13M_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_RC13M_ON = const(0b00000010)
|
||||||
|
SX126X_CALIBRATE_RC64K_OFF = const(0b00000000)
|
||||||
|
SX126X_CALIBRATE_RC64K_ON = const(0b00000001)
|
||||||
|
SX126X_CALIBRATE_ALL = const(0b01111111)
|
||||||
|
SX126X_CAL_IMG_430_MHZ_1 = const(0x6B)
|
||||||
|
SX126X_CAL_IMG_430_MHZ_2 = const(0x6F)
|
||||||
|
SX126X_CAL_IMG_470_MHZ_1 = const(0x75)
|
||||||
|
SX126X_CAL_IMG_470_MHZ_2 = const(0x81)
|
||||||
|
SX126X_CAL_IMG_779_MHZ_1 = const(0xC1)
|
||||||
|
SX126X_CAL_IMG_779_MHZ_2 = const(0xC5)
|
||||||
|
SX126X_CAL_IMG_863_MHZ_1 = const(0xD7)
|
||||||
|
SX126X_CAL_IMG_863_MHZ_2 = const(0xDB)
|
||||||
|
SX126X_CAL_IMG_902_MHZ_1 = const(0xE1)
|
||||||
|
SX126X_CAL_IMG_902_MHZ_2 = const(0xE9)
|
||||||
|
SX126X_PA_CONFIG_HP_MAX = const(0x07)
|
||||||
|
SX126X_PA_CONFIG_PA_LUT = const(0x01)
|
||||||
|
SX126X_PA_CONFIG_SX1262_8 = const(0x00)
|
||||||
|
SX126X_RX_TX_FALLBACK_MODE_FS = const(0x40)
|
||||||
|
SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC = const(0x30)
|
||||||
|
SX126X_RX_TX_FALLBACK_MODE_STDBY_RC = const(0x20)
|
||||||
|
SX126X_IRQ_TIMEOUT = const(0b1000000000)
|
||||||
|
SX126X_IRQ_CAD_DETECTED = const(0b0100000000)
|
||||||
|
SX126X_IRQ_CAD_DONE = const(0b0010000000)
|
||||||
|
SX126X_IRQ_CRC_ERR = const(0b0001000000)
|
||||||
|
SX126X_IRQ_HEADER_ERR = const(0b0000100000)
|
||||||
|
SX126X_IRQ_HEADER_VALID = const(0b0000010000)
|
||||||
|
SX126X_IRQ_SYNC_WORD_VALID = const(0b0000001000)
|
||||||
|
SX126X_IRQ_PREAMBLE_DETECTED = const(0b0000000100)
|
||||||
|
SX126X_IRQ_RX_DONE = const(0b0000000010)
|
||||||
|
SX126X_IRQ_TX_DONE = const(0b0000000001)
|
||||||
|
SX126X_IRQ_ALL = const(0b1111111111)
|
||||||
|
SX126X_IRQ_NONE = const(0b0000000000)
|
||||||
|
SX126X_DIO2_AS_IRQ = const(0x00)
|
||||||
|
SX126X_DIO2_AS_RF_SWITCH = const(0x01)
|
||||||
|
SX126X_DIO3_OUTPUT_1_6 = const(0x00)
|
||||||
|
SX126X_DIO3_OUTPUT_1_7 = const(0x01)
|
||||||
|
SX126X_DIO3_OUTPUT_1_8 = const(0x02)
|
||||||
|
SX126X_DIO3_OUTPUT_2_2 = const(0x03)
|
||||||
|
SX126X_DIO3_OUTPUT_2_4 = const(0x04)
|
||||||
|
SX126X_DIO3_OUTPUT_2_7 = const(0x05)
|
||||||
|
SX126X_DIO3_OUTPUT_3_0 = const(0x06)
|
||||||
|
SX126X_DIO3_OUTPUT_3_3 = const(0x07)
|
||||||
|
SX126X_PACKET_TYPE_GFSK = const(0x00)
|
||||||
|
SX126X_PACKET_TYPE_LORA = const(0x01)
|
||||||
|
SX126X_PA_RAMP_10U = const(0x00)
|
||||||
|
SX126X_PA_RAMP_20U = const(0x01)
|
||||||
|
SX126X_PA_RAMP_40U = const(0x02)
|
||||||
|
SX126X_PA_RAMP_80U = const(0x03)
|
||||||
|
SX126X_PA_RAMP_200U = const(0x04)
|
||||||
|
SX126X_PA_RAMP_800U = const(0x05)
|
||||||
|
SX126X_PA_RAMP_1700U = const(0x06)
|
||||||
|
SX126X_PA_RAMP_3400U = const(0x07)
|
||||||
|
SX126X_GFSK_FILTER_NONE = const(0x00)
|
||||||
|
SX126X_GFSK_FILTER_GAUSS_0_3 = const(0x08)
|
||||||
|
SX126X_GFSK_FILTER_GAUSS_0_5 = const(0x09)
|
||||||
|
SX126X_GFSK_FILTER_GAUSS_0_7 = const(0x0A)
|
||||||
|
SX126X_GFSK_FILTER_GAUSS_1 = const(0x0B)
|
||||||
|
SX126X_GFSK_RX_BW_4_8 = const(0x1F)
|
||||||
|
SX126X_GFSK_RX_BW_5_8 = const(0x17)
|
||||||
|
SX126X_GFSK_RX_BW_7_3 = const(0x0F)
|
||||||
|
SX126X_GFSK_RX_BW_9_7 = const(0x1E)
|
||||||
|
SX126X_GFSK_RX_BW_11_7 = const(0x16)
|
||||||
|
SX126X_GFSK_RX_BW_14_6 = const(0x0E)
|
||||||
|
SX126X_GFSK_RX_BW_19_5 = const(0x1D)
|
||||||
|
SX126X_GFSK_RX_BW_23_4 = const(0x15)
|
||||||
|
SX126X_GFSK_RX_BW_29_3 = const(0x0D)
|
||||||
|
SX126X_GFSK_RX_BW_39_0 = const(0x1C)
|
||||||
|
SX126X_GFSK_RX_BW_46_9 = const(0x14)
|
||||||
|
SX126X_GFSK_RX_BW_58_6 = const(0x0C)
|
||||||
|
SX126X_GFSK_RX_BW_78_2 = const(0x1B)
|
||||||
|
SX126X_GFSK_RX_BW_93_8 = const(0x13)
|
||||||
|
SX126X_GFSK_RX_BW_117_3 = const(0x0B)
|
||||||
|
SX126X_GFSK_RX_BW_156_2 = const(0x1A)
|
||||||
|
SX126X_GFSK_RX_BW_187_2 = const(0x12)
|
||||||
|
SX126X_GFSK_RX_BW_234_3 = const(0x0A)
|
||||||
|
SX126X_GFSK_RX_BW_312_0 = const(0x19)
|
||||||
|
SX126X_GFSK_RX_BW_373_6 = const(0x11)
|
||||||
|
SX126X_GFSK_RX_BW_467_0 = const(0x09)
|
||||||
|
SX126X_LORA_BW_7_8 = const(0x00)
|
||||||
|
SX126X_LORA_BW_10_4 = const(0x08)
|
||||||
|
SX126X_LORA_BW_15_6 = const(0x01)
|
||||||
|
SX126X_LORA_BW_20_8 = const(0x09)
|
||||||
|
SX126X_LORA_BW_31_25 = const(0x02)
|
||||||
|
SX126X_LORA_BW_41_7 = const(0x0A)
|
||||||
|
SX126X_LORA_BW_62_5 = const(0x03)
|
||||||
|
SX126X_LORA_BW_125_0 = const(0x04)
|
||||||
|
SX126X_LORA_BW_250_0 = const(0x05)
|
||||||
|
SX126X_LORA_BW_500_0 = const(0x06)
|
||||||
|
SX126X_LORA_CR_4_5 = const(0x01)
|
||||||
|
SX126X_LORA_CR_4_6 = const(0x02)
|
||||||
|
SX126X_LORA_CR_4_7 = const(0x03)
|
||||||
|
SX126X_LORA_CR_4_8 = const(0x04)
|
||||||
|
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF = const(0x00)
|
||||||
|
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON = const(0x01)
|
||||||
|
SX126X_GFSK_PREAMBLE_DETECT_OFF = const(0x00)
|
||||||
|
SX126X_GFSK_PREAMBLE_DETECT_8 = const(0x04)
|
||||||
|
SX126X_GFSK_PREAMBLE_DETECT_16 = const(0x05)
|
||||||
|
SX126X_GFSK_PREAMBLE_DETECT_24 = const(0x06)
|
||||||
|
SX126X_GFSK_PREAMBLE_DETECT_32 = const(0x07)
|
||||||
|
SX126X_GFSK_ADDRESS_FILT_OFF = const(0x00)
|
||||||
|
SX126X_GFSK_ADDRESS_FILT_NODE = const(0x01)
|
||||||
|
SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST = const(0x02)
|
||||||
|
SX126X_GFSK_PACKET_FIXED = const(0x00)
|
||||||
|
SX126X_GFSK_PACKET_VARIABLE = const(0x01)
|
||||||
|
SX126X_GFSK_CRC_OFF = const(0x01)
|
||||||
|
SX126X_GFSK_CRC_1_BYTE = const(0x00)
|
||||||
|
SX126X_GFSK_CRC_2_BYTE = const(0x02)
|
||||||
|
SX126X_GFSK_CRC_1_BYTE_INV = const(0x04)
|
||||||
|
SX126X_GFSK_CRC_2_BYTE_INV = const(0x06)
|
||||||
|
SX126X_GFSK_WHITENING_OFF = const(0x00)
|
||||||
|
SX126X_GFSK_WHITENING_ON = const(0x01)
|
||||||
|
SX126X_LORA_HEADER_EXPLICIT = const(0x00)
|
||||||
|
SX126X_LORA_HEADER_IMPLICIT = const(0x01)
|
||||||
|
SX126X_LORA_CRC_OFF = const(0x00)
|
||||||
|
SX126X_LORA_CRC_ON = const(0x01)
|
||||||
|
SX126X_LORA_IQ_STANDARD = const(0x00)
|
||||||
|
SX126X_LORA_IQ_INVERTED = const(0x01)
|
||||||
|
SX126X_CAD_ON_1_SYMB = const(0x00)
|
||||||
|
SX126X_CAD_ON_2_SYMB = const(0x01)
|
||||||
|
SX126X_CAD_ON_4_SYMB = const(0x02)
|
||||||
|
SX126X_CAD_ON_8_SYMB = const(0x03)
|
||||||
|
SX126X_CAD_ON_16_SYMB = const(0x04)
|
||||||
|
SX126X_CAD_GOTO_STDBY = const(0x00)
|
||||||
|
SX126X_CAD_GOTO_RX = const(0x01)
|
||||||
|
SX126X_STATUS_MODE_STDBY_RC = const(0b00100000)
|
||||||
|
SX126X_STATUS_MODE_STDBY_XOSC = const(0b00110000)
|
||||||
|
SX126X_STATUS_MODE_FS = const(0b01000000)
|
||||||
|
SX126X_STATUS_MODE_RX = const(0b01010000)
|
||||||
|
SX126X_STATUS_MODE_TX = const(0b01100000)
|
||||||
|
SX126X_STATUS_DATA_AVAILABLE = const(0b00000100)
|
||||||
|
SX126X_STATUS_CMD_TIMEOUT = const(0b00000110)
|
||||||
|
SX126X_STATUS_CMD_INVALID = const(0b00001000)
|
||||||
|
SX126X_STATUS_CMD_FAILED = const(0b00001010)
|
||||||
|
SX126X_STATUS_TX_DONE = const(0b00001100)
|
||||||
|
SX126X_STATUS_SPI_FAILED = const(0b11111111)
|
||||||
|
SX126X_GFSK_RX_STATUS_PREAMBLE_ERR = const(0b10000000)
|
||||||
|
SX126X_GFSK_RX_STATUS_SYNC_ERR = const(0b01000000)
|
||||||
|
SX126X_GFSK_RX_STATUS_ADRS_ERR = const(0b00100000)
|
||||||
|
SX126X_GFSK_RX_STATUS_CRC_ERR = const(0b00010000)
|
||||||
|
SX126X_GFSK_RX_STATUS_LENGTH_ERR = const(0b00001000)
|
||||||
|
SX126X_GFSK_RX_STATUS_ABORT_ERR = const(0b00000100)
|
||||||
|
SX126X_GFSK_RX_STATUS_PACKET_RECEIVED = const(0b00000010)
|
||||||
|
SX126X_GFSK_RX_STATUS_PACKET_SENT = const(0b00000001)
|
||||||
|
SX126X_PA_RAMP_ERR = const(0b100000000)
|
||||||
|
SX126X_PLL_LOCK_ERR = const(0b001000000)
|
||||||
|
SX126X_XOSC_START_ERR = const(0b000100000)
|
||||||
|
SX126X_IMG_CALIB_ERR = const(0b000010000)
|
||||||
|
SX126X_ADC_CALIB_ERR = const(0b000001000)
|
||||||
|
SX126X_PLL_CALIB_ERR = const(0b000000100)
|
||||||
|
SX126X_RC13M_CALIB_ERR = const(0b000000010)
|
||||||
|
SX126X_RC64K_CALIB_ERR = const(0b000000001)
|
||||||
|
SX126X_SYNC_WORD_PUBLIC = const(0x34)
|
||||||
|
SX126X_SYNC_WORD_PRIVATE = const(0x12)
|
||||||
|
|
||||||
|
ERR_NONE = const(0)
|
||||||
|
ERR_UNKNOWN = const(-1)
|
||||||
|
ERR_CHIP_NOT_FOUND = const(-2)
|
||||||
|
ERR_MEMORY_ALLOCATION_FAILED = const(-3)
|
||||||
|
ERR_PACKET_TOO_LONG = const(-4)
|
||||||
|
ERR_TX_TIMEOUT = const(-5)
|
||||||
|
ERR_RX_TIMEOUT = const(-6)
|
||||||
|
ERR_CRC_MISMATCH = const(-7)
|
||||||
|
ERR_INVALID_BANDWIDTH = const(-8)
|
||||||
|
ERR_INVALID_SPREADING_FACTOR = const(-9)
|
||||||
|
ERR_INVALID_CODING_RATE = const(-10)
|
||||||
|
ERR_INVALID_BIT_RANGE = const(-11)
|
||||||
|
ERR_INVALID_FREQUENCY = const(-12)
|
||||||
|
ERR_INVALID_OUTPUT_POWER = const(-13)
|
||||||
|
PREAMBLE_DETECTED = const(-14)
|
||||||
|
CHANNEL_FREE = const(-15)
|
||||||
|
ERR_SPI_WRITE_FAILED = const(-16)
|
||||||
|
ERR_INVALID_CURRENT_LIMIT = const(-17)
|
||||||
|
ERR_INVALID_PREAMBLE_LENGTH = const(-18)
|
||||||
|
ERR_INVALID_GAIN = const(-19)
|
||||||
|
ERR_WRONG_MODEM = const(-20)
|
||||||
|
ERR_INVALID_NUM_SAMPLES = const(-21)
|
||||||
|
ERR_INVALID_RSSI_OFFSET = const(-22)
|
||||||
|
ERR_INVALID_ENCODING = const(-23)
|
||||||
|
ERR_INVALID_BIT_RATE = const(-101)
|
||||||
|
ERR_INVALID_FREQUENCY_DEVIATION = const(-102)
|
||||||
|
ERR_INVALID_BIT_RATE_BW_RATIO = const(-103)
|
||||||
|
ERR_INVALID_RX_BANDWIDTH = const(-104)
|
||||||
|
ERR_INVALID_SYNC_WORD = const(-105)
|
||||||
|
ERR_INVALID_DATA_SHAPING = const(-106)
|
||||||
|
ERR_INVALID_MODULATION = const(-107)
|
||||||
|
ERR_AT_FAILED = const(-201)
|
||||||
|
ERR_URL_MALFORMED = const(-202)
|
||||||
|
ERR_RESPONSE_MALFORMED_AT = const(-203)
|
||||||
|
ERR_RESPONSE_MALFORMED = const(-204)
|
||||||
|
ERR_MQTT_CONN_VERSION_REJECTED = const(-205)
|
||||||
|
ERR_MQTT_CONN_ID_REJECTED = const(-206)
|
||||||
|
ERR_MQTT_CONN_SERVER_UNAVAILABLE = const(-207)
|
||||||
|
ERR_MQTT_CONN_BAD_USERNAME_PASSWORD = const(-208)
|
||||||
|
ERR_MQTT_CONN_NOT_AUTHORIZED = const(-208)
|
||||||
|
ERR_MQTT_UNEXPECTED_PACKET_ID = const(-209)
|
||||||
|
ERR_MQTT_NO_NEW_PACKET_AVAILABLE = const(-210)
|
||||||
|
ERR_CMD_MODE_FAILED = const(-301)
|
||||||
|
ERR_FRAME_MALFORMED = const(-302)
|
||||||
|
ERR_FRAME_INCORRECT_CHECKSUM = const(-303)
|
||||||
|
ERR_FRAME_UNEXPECTED_ID = const(-304)
|
||||||
|
ERR_FRAME_NO_RESPONSE = const(-305)
|
||||||
|
ERR_INVALID_RTTY_SHIFT = const(-401)
|
||||||
|
ERR_UNSUPPORTED_ENCODING = const(-402)
|
||||||
|
ERR_INVALID_DATA_RATE = const(-501)
|
||||||
|
ERR_INVALID_ADDRESS_WIDTH = const(-502)
|
||||||
|
ERR_INVALID_PIPE_NUMBER = const(-503)
|
||||||
|
ERR_ACK_NOT_RECEIVED = const(-504)
|
||||||
|
ERR_INVALID_NUM_BROAD_ADDRS = const(-601)
|
||||||
|
ERR_INVALID_CRC_CONFIGURATION = const(-701)
|
||||||
|
LORA_DETECTED = const(-702)
|
||||||
|
ERR_INVALID_TCXO_VOLTAGE = const(-703)
|
||||||
|
ERR_INVALID_MODULATION_PARAMETERS = const(-704)
|
||||||
|
ERR_SPI_CMD_TIMEOUT = const(-705)
|
||||||
|
ERR_SPI_CMD_INVALID = const(-706)
|
||||||
|
ERR_SPI_CMD_FAILED = const(-707)
|
||||||
|
ERR_INVALID_SLEEP_PERIOD = const(-708)
|
||||||
|
ERR_INVALID_RX_PERIOD = const(-709)
|
||||||
|
ERR_INVALID_CALLSIGN = const(-801)
|
||||||
|
ERR_INVALID_NUM_REPEATERS = const(-802)
|
||||||
|
ERR_INVALID_REPEATER_CALLSIGN = const(-803)
|
||||||
|
ERR_INVALID_PACKET_TYPE = const(-804)
|
||||||
|
ERR_INVALID_PACKET_LENGTH = const(-805)
|
||||||
|
|
||||||
|
ERROR = {
|
||||||
|
0: 'ERR_NONE',
|
||||||
|
-1: 'ERR_UNKNOWN',
|
||||||
|
-2: 'ERR_CHIP_NOT_FOUND',
|
||||||
|
-3: 'ERR_MEMORY_ALLOCATION_FAILED',
|
||||||
|
-4: 'ERR_PACKET_TOO_LONG',
|
||||||
|
-5: 'ERR_TX_TIMEOUT',
|
||||||
|
-6: 'ERR_RX_TIMEOUT',
|
||||||
|
-7: 'ERR_CRC_MISMATCH',
|
||||||
|
-8: 'ERR_INVALID_BANDWIDTH',
|
||||||
|
-9: 'ERR_INVALID_SPREADING_FACTOR',
|
||||||
|
-10: 'ERR_INVALID_CODING_RATE',
|
||||||
|
-11: 'ERR_INVALID_BIT_RANGE',
|
||||||
|
-12: 'ERR_INVALID_FREQUENCY',
|
||||||
|
-13: 'ERR_INVALID_OUTPUT_POWER',
|
||||||
|
-14: 'PREAMBLE_DETECTED',
|
||||||
|
-15: 'CHANNEL_FREE',
|
||||||
|
-16: 'ERR_SPI_WRITE_FAILED',
|
||||||
|
-17: 'ERR_INVALID_CURRENT_LIMIT',
|
||||||
|
-18: 'ERR_INVALID_PREAMBLE_LENGTH',
|
||||||
|
-19: 'ERR_INVALID_GAIN',
|
||||||
|
-20: 'ERR_WRONG_MODEM',
|
||||||
|
-21: 'ERR_INVALID_NUM_SAMPLES',
|
||||||
|
-22: 'ERR_INVALID_RSSI_OFFSET',
|
||||||
|
-23: 'ERR_INVALID_ENCODING',
|
||||||
|
-101: 'ERR_INVALID_BIT_RATE',
|
||||||
|
-102: 'ERR_INVALID_FREQUENCY_DEVIATION',
|
||||||
|
-103: 'ERR_INVALID_BIT_RATE_BW_RATIO',
|
||||||
|
-104: 'ERR_INVALID_RX_BANDWIDTH',
|
||||||
|
-105: 'ERR_INVALID_SYNC_WORD',
|
||||||
|
-106: 'ERR_INVALID_DATA_SHAPING',
|
||||||
|
-107: 'ERR_INVALID_MODULATION',
|
||||||
|
-201: 'ERR_AT_FAILED',
|
||||||
|
-202: 'ERR_URL_MALFORMED',
|
||||||
|
-203: 'ERR_RESPONSE_MALFORMED_AT',
|
||||||
|
-204: 'ERR_RESPONSE_MALFORMED',
|
||||||
|
-205: 'ERR_MQTT_CONN_VERSION_REJECTED',
|
||||||
|
-206: 'ERR_MQTT_CONN_ID_REJECTED',
|
||||||
|
-207: 'ERR_MQTT_CONN_SERVER_UNAVAILABLE',
|
||||||
|
-208: 'ERR_MQTT_CONN_BAD_USERNAME_PASSWORD',
|
||||||
|
-208: 'ERR_MQTT_CONN_NOT_AUTHORIZED',
|
||||||
|
-209: 'ERR_MQTT_UNEXPECTED_PACKET_ID',
|
||||||
|
-210: 'ERR_MQTT_NO_NEW_PACKET_AVAILABLE',
|
||||||
|
-301: 'ERR_CMD_MODE_FAILED',
|
||||||
|
-302: 'ERR_FRAME_MALFORMED',
|
||||||
|
-303: 'ERR_FRAME_INCORRECT_CHECKSUM',
|
||||||
|
-304: 'ERR_FRAME_UNEXPECTED_ID',
|
||||||
|
-305: 'ERR_FRAME_NO_RESPONSE',
|
||||||
|
-401: 'ERR_INVALID_RTTY_SHIFT',
|
||||||
|
-402: 'ERR_UNSUPPORTED_ENCODING',
|
||||||
|
-501: 'ERR_INVALID_DATA_RATE',
|
||||||
|
-502: 'ERR_INVALID_ADDRESS_WIDTH',
|
||||||
|
-503: 'ERR_INVALID_PIPE_NUMBER',
|
||||||
|
-504: 'ERR_ACK_NOT_RECEIVED',
|
||||||
|
-601: 'ERR_INVALID_NUM_BROAD_ADDRS',
|
||||||
|
-701: 'ERR_INVALID_CRC_CONFIGURATION',
|
||||||
|
-702: 'LORA_DETECTED',
|
||||||
|
-703: 'ERR_INVALID_TCXO_VOLTAGE',
|
||||||
|
-704: 'ERR_INVALID_MODULATION_PARAMETERS',
|
||||||
|
-705: 'ERR_SPI_CMD_TIMEOUT',
|
||||||
|
-706: 'ERR_SPI_CMD_INVALID',
|
||||||
|
-707: 'ERR_SPI_CMD_FAILED',
|
||||||
|
-708: 'ERR_INVALID_SLEEP_PERIOD',
|
||||||
|
-709: 'ERR_INVALID_RX_PERIOD',
|
||||||
|
-801: 'ERR_INVALID_CALLSIGN',
|
||||||
|
-802: 'ERR_INVALID_NUM_REPEATERS',
|
||||||
|
-803: 'ERR_INVALID_REPEATER_CALLSIGN',
|
||||||
|
-804: 'ERR_INVALID_PACKET_TYPE',
|
||||||
|
-805: 'ERR_INVALID_PACKET_LENGTH'
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
|
||||||
|
|
||||||
|
from micropython import const
|
||||||
|
import framebuf
|
||||||
|
|
||||||
|
|
||||||
|
# register definitions
|
||||||
|
SET_CONTRAST = const(0x81)
|
||||||
|
SET_ENTIRE_ON = const(0xA4)
|
||||||
|
SET_NORM_INV = const(0xA6)
|
||||||
|
SET_DISP = const(0xAE)
|
||||||
|
SET_MEM_ADDR = const(0x20)
|
||||||
|
SET_COL_ADDR = const(0x21)
|
||||||
|
SET_PAGE_ADDR = const(0x22)
|
||||||
|
SET_DISP_START_LINE = const(0x40)
|
||||||
|
SET_SEG_REMAP = const(0xA0)
|
||||||
|
SET_MUX_RATIO = const(0xA8)
|
||||||
|
SET_COM_OUT_DIR = const(0xC0)
|
||||||
|
SET_DISP_OFFSET = const(0xD3)
|
||||||
|
SET_COM_PIN_CFG = const(0xDA)
|
||||||
|
SET_DISP_CLK_DIV = const(0xD5)
|
||||||
|
SET_PRECHARGE = const(0xD9)
|
||||||
|
SET_VCOM_DESEL = const(0xDB)
|
||||||
|
SET_CHARGE_PUMP = const(0x8D)
|
||||||
|
|
||||||
|
# Subclassing FrameBuffer provides support for graphics primitives
|
||||||
|
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
|
||||||
|
class SSD1306(framebuf.FrameBuffer):
|
||||||
|
def __init__(self, width, height, external_vcc):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.external_vcc = external_vcc
|
||||||
|
self.pages = self.height // 8
|
||||||
|
self.buffer = bytearray(self.pages * self.width)
|
||||||
|
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
|
||||||
|
self.init_display()
|
||||||
|
|
||||||
|
def init_display(self):
|
||||||
|
for cmd in (
|
||||||
|
SET_DISP | 0x00, # off
|
||||||
|
# address setting
|
||||||
|
SET_MEM_ADDR,
|
||||||
|
0x00, # horizontal
|
||||||
|
# resolution and layout
|
||||||
|
SET_DISP_START_LINE | 0x00,
|
||||||
|
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
|
||||||
|
SET_MUX_RATIO,
|
||||||
|
self.height - 1,
|
||||||
|
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
|
||||||
|
SET_DISP_OFFSET,
|
||||||
|
0x00,
|
||||||
|
SET_COM_PIN_CFG,
|
||||||
|
0x02 if self.width > 2 * self.height else 0x12,
|
||||||
|
# timing and driving scheme
|
||||||
|
SET_DISP_CLK_DIV,
|
||||||
|
0x80,
|
||||||
|
SET_PRECHARGE,
|
||||||
|
0x22 if self.external_vcc else 0xF1,
|
||||||
|
SET_VCOM_DESEL,
|
||||||
|
0x30, # 0.83*Vcc
|
||||||
|
# display
|
||||||
|
SET_CONTRAST,
|
||||||
|
0xFF, # maximum
|
||||||
|
SET_ENTIRE_ON, # output follows RAM contents
|
||||||
|
SET_NORM_INV, # not inverted
|
||||||
|
# charge pump
|
||||||
|
SET_CHARGE_PUMP,
|
||||||
|
0x10 if self.external_vcc else 0x14,
|
||||||
|
SET_DISP | 0x01,
|
||||||
|
): # on
|
||||||
|
self.write_cmd(cmd)
|
||||||
|
self.fill(0)
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def poweroff(self):
|
||||||
|
self.write_cmd(SET_DISP | 0x00)
|
||||||
|
|
||||||
|
def poweron(self):
|
||||||
|
self.write_cmd(SET_DISP | 0x01)
|
||||||
|
|
||||||
|
def contrast(self, contrast):
|
||||||
|
self.write_cmd(SET_CONTRAST)
|
||||||
|
self.write_cmd(contrast)
|
||||||
|
|
||||||
|
def invert(self, invert):
|
||||||
|
self.write_cmd(SET_NORM_INV | (invert & 1))
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
x0 = 0
|
||||||
|
x1 = self.width - 1
|
||||||
|
if self.width == 64:
|
||||||
|
# displays with width of 64 pixels are shifted by 32
|
||||||
|
x0 += 32
|
||||||
|
x1 += 32
|
||||||
|
self.write_cmd(SET_COL_ADDR)
|
||||||
|
self.write_cmd(x0)
|
||||||
|
self.write_cmd(x1)
|
||||||
|
self.write_cmd(SET_PAGE_ADDR)
|
||||||
|
self.write_cmd(0)
|
||||||
|
self.write_cmd(self.pages - 1)
|
||||||
|
self.write_data(self.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
class SSD1306_I2C(SSD1306):
|
||||||
|
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
|
||||||
|
self.i2c = i2c
|
||||||
|
self.addr = addr
|
||||||
|
self.temp = bytearray(2)
|
||||||
|
self.write_list = [b"\x40", None] # Co=0, D/C#=1
|
||||||
|
super().__init__(width, height, external_vcc)
|
||||||
|
|
||||||
|
def write_cmd(self, cmd):
|
||||||
|
self.temp[0] = 0x80 # Co=1, D/C#=0
|
||||||
|
self.temp[1] = cmd
|
||||||
|
self.i2c.writeto(self.addr, self.temp)
|
||||||
|
|
||||||
|
def write_data(self, buf):
|
||||||
|
self.write_list[1] = buf
|
||||||
|
self.i2c.writevto(self.addr, self.write_list)
|
||||||
|
|
||||||
|
|
||||||
|
class SSD1306_SPI(SSD1306):
|
||||||
|
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
|
||||||
|
self.rate = 10 * 1024 * 1024
|
||||||
|
dc.init(dc.OUT, value=0)
|
||||||
|
res.init(res.OUT, value=0)
|
||||||
|
cs.init(cs.OUT, value=1)
|
||||||
|
self.spi = spi
|
||||||
|
self.dc = dc
|
||||||
|
self.res = res
|
||||||
|
self.cs = cs
|
||||||
|
import time
|
||||||
|
|
||||||
|
self.res(1)
|
||||||
|
time.sleep_ms(1)
|
||||||
|
self.res(0)
|
||||||
|
time.sleep_ms(10)
|
||||||
|
self.res(1)
|
||||||
|
super().__init__(width, height, external_vcc)
|
||||||
|
|
||||||
|
def write_cmd(self, cmd):
|
||||||
|
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
|
||||||
|
self.cs(1)
|
||||||
|
self.dc(0)
|
||||||
|
self.cs(0)
|
||||||
|
self.spi.write(bytearray([cmd]))
|
||||||
|
self.cs(1)
|
||||||
|
|
||||||
|
def write_data(self, buf):
|
||||||
|
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
|
||||||
|
self.cs(1)
|
||||||
|
self.dc(1)
|
||||||
|
self.cs(0)
|
||||||
|
self.spi.write(buf)
|
||||||
|
self.cs(1)
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
from _sx126x import *
|
||||||
|
from sx126x import SX126X
|
||||||
|
|
||||||
|
_SX126X_PA_CONFIG_SX1261 = const(0x01)
|
||||||
|
|
||||||
|
class SX1261(SX126X):
|
||||||
|
TX_DONE = SX126X_IRQ_TX_DONE
|
||||||
|
RX_DONE = SX126X_IRQ_RX_DONE
|
||||||
|
ADDR_FILT_OFF = SX126X_GFSK_ADDRESS_FILT_OFF
|
||||||
|
ADDR_FILT_NODE = SX126X_GFSK_ADDRESS_FILT_NODE
|
||||||
|
ADDR_FILT_NODE_BROAD = SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST
|
||||||
|
PREAMBLE_DETECT_OFF = SX126X_GFSK_PREAMBLE_DETECT_OFF
|
||||||
|
PREAMBLE_DETECT_8 = SX126X_GFSK_PREAMBLE_DETECT_8
|
||||||
|
PREAMBLE_DETECT_16 = SX126X_GFSK_PREAMBLE_DETECT_16
|
||||||
|
PREAMBLE_DETECT_24 = SX126X_GFSK_PREAMBLE_DETECT_24
|
||||||
|
PREAMBLE_DETECT_32 = SX126X_GFSK_PREAMBLE_DETECT_32
|
||||||
|
STATUS = ERROR
|
||||||
|
|
||||||
|
def __init__(self, spi_bus, clk, mosi, miso, cs, irq, rst, gpio):
|
||||||
|
super().__init__(spi_bus, clk, mosi, miso, cs, irq, rst, gpio)
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
|
||||||
|
def begin(self, freq=434.0, bw=125.0, sf=9, cr=7, syncWord=SX126X_SYNC_WORD_PRIVATE,
|
||||||
|
power=14, currentLimit=60.0, preambleLength=8, implicit=False, implicitLen=0xFF,
|
||||||
|
crcOn=True, txIq=False, rxIq=False, tcxoVoltage=1.6, useRegulatorLDO=False,
|
||||||
|
blocking=True):
|
||||||
|
state = super().begin(bw, sf, cr, syncWord, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO, txIq, rxIq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if not implicit:
|
||||||
|
state = super().explicitHeader()
|
||||||
|
else:
|
||||||
|
state = super().implicitHeader(implicitLen)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setCRC(crcOn)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setFrequency(freq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setOutputPower(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().fixPaClamping()
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setBlockingCallback(blocking)
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def beginFSK(self, freq=434.0, br=48.0, freqDev=50.0, rxBw=156.2, power=14, currentLimit=60.0,
|
||||||
|
preambleLength=16, dataShaping=0.5, syncWord=[0x2D, 0x01], syncBitsLength=16,
|
||||||
|
addrFilter=SX126X_GFSK_ADDRESS_FILT_OFF, addr=0x00, crcLength=2, crcInitial=0x1D0F, crcPolynomial=0x1021,
|
||||||
|
crcInverted=True, whiteningOn=True, whiteningInitial=0x0100,
|
||||||
|
fixedPacketLength=False, packetLength=0xFF, preambleDetectorLength=SX126X_GFSK_PREAMBLE_DETECT_16,
|
||||||
|
tcxoVoltage=1.6, useRegulatorLDO=False,
|
||||||
|
blocking=True):
|
||||||
|
state = super().beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping, preambleDetectorLength, tcxoVoltage, useRegulatorLDO)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setSyncBits(syncWord, syncBitsLength)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if addrFilter == SX126X_GFSK_ADDRESS_FILT_OFF:
|
||||||
|
state = super().disableAddressFiltering()
|
||||||
|
elif addrFilter == SX126X_GFSK_ADDRESS_FILT_NODE:
|
||||||
|
state = super().setNodeAddress(addr)
|
||||||
|
elif addrFilter == SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST:
|
||||||
|
state = super().setBroadcastAddress(addr)
|
||||||
|
else:
|
||||||
|
state = ERR_UNKNOWN
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setCRC(crcLength, crcInitial, crcPolynomial, crcInverted)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setWhitening(whiteningOn, whiteningInitial)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if fixedPacketLength:
|
||||||
|
state = super().fixedPacketLengthMode(packetLength)
|
||||||
|
else:
|
||||||
|
state = super().variablePacketLengthMode(packetLength)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setFrequency(freq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setOutputPower(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().fixPaClamping()
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setBlockingCallback(blocking)
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def setFrequency(self, freq, calibrate=True):
|
||||||
|
if freq < 150.0 or freq > 960.0:
|
||||||
|
return ERR_INVALID_FREQUENCY
|
||||||
|
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
if calibrate:
|
||||||
|
data = bytearray(2)
|
||||||
|
if freq > 900.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_902_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_902_MHZ_2
|
||||||
|
elif freq > 850.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_863_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_863_MHZ_2
|
||||||
|
elif freq > 770.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_779_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_779_MHZ_2
|
||||||
|
elif freq > 460.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_470_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_470_MHZ_2
|
||||||
|
else:
|
||||||
|
data[0] = SX126X_CAL_IMG_430_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_430_MHZ_2
|
||||||
|
state = super().calibrateImage(data)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
return super().setFrequencyRaw(freq)
|
||||||
|
|
||||||
|
def setOutputPower(self, power):
|
||||||
|
if not ((power >= -17) and (power <= 14)):
|
||||||
|
return ERR_INVALID_OUTPUT_POWER
|
||||||
|
|
||||||
|
ocp = bytearray(1)
|
||||||
|
ocp_mv = memoryview(ocp)
|
||||||
|
state = super().readRegister(SX126X_REG_OCP_CONFIGURATION, ocp_mv, 1)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setPaConfig(0x04, _SX126X_PA_CONFIG_SX1261, 0x00)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setTxParams(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
return super().writeRegister(SX126X_REG_OCP_CONFIGURATION, ocp, 1)
|
||||||
|
|
||||||
|
def setTxIq(self, txIq):
|
||||||
|
self._txIq = txIq
|
||||||
|
|
||||||
|
def setRxIq(self, rxIq):
|
||||||
|
self._rxIq = rxIq
|
||||||
|
if not self.blocking:
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
def setPreambleDetectorLength(self, preambleDetectorLength):
|
||||||
|
self._preambleDetectorLength = preambleDetectorLength
|
||||||
|
if not self.blocking:
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
def setBlockingCallback(self, blocking, callback=None):
|
||||||
|
self.blocking = blocking
|
||||||
|
if not self.blocking:
|
||||||
|
state = super().startReceive()
|
||||||
|
ASSERT(state)
|
||||||
|
if callback != None:
|
||||||
|
self._callbackFunction = callback
|
||||||
|
super().setDio1Action(self._onIRQ)
|
||||||
|
else:
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
super().clearDio1Action()
|
||||||
|
return state
|
||||||
|
else:
|
||||||
|
state = super().standby()
|
||||||
|
ASSERT(state)
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
super().clearDio1Action()
|
||||||
|
return state
|
||||||
|
|
||||||
|
def recv(self, len=0, timeout_en=False, timeout_ms=0):
|
||||||
|
if not self.blocking:
|
||||||
|
return self._readData(len)
|
||||||
|
else:
|
||||||
|
return self._receive(len, timeout_en, timeout_ms)
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
if not self.blocking:
|
||||||
|
return self._startTransmit(data)
|
||||||
|
else:
|
||||||
|
return self._transmit(data)
|
||||||
|
|
||||||
|
def _events(self):
|
||||||
|
return super().getIrqStatus()
|
||||||
|
|
||||||
|
def _receive(self, len_=0, timeout_en=False, timeout_ms=0):
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
length = len_
|
||||||
|
|
||||||
|
if len_ == 0:
|
||||||
|
length = SX126X_MAX_PACKET_LENGTH
|
||||||
|
|
||||||
|
data = bytearray(length)
|
||||||
|
data_mv = memoryview(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = super().receive(data_mv, length, timeout_en, timeout_ms)
|
||||||
|
except AssertionError as e:
|
||||||
|
state = list(ERROR.keys())[list(ERROR.values()).index(str(e))]
|
||||||
|
|
||||||
|
if state == ERR_NONE or state == ERR_CRC_MISMATCH:
|
||||||
|
if len_ == 0:
|
||||||
|
length = super().getPacketLength(False)
|
||||||
|
data = data[:length]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b'', state
|
||||||
|
|
||||||
|
return bytes(data), state
|
||||||
|
|
||||||
|
def _transmit(self, data):
|
||||||
|
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return 0, ERR_INVALID_PACKET_TYPE
|
||||||
|
|
||||||
|
# --- AJOUT : FORÇAGE DU CRC À OFF AVANT L'ENVOI ---
|
||||||
|
super().setCRC(False)
|
||||||
|
|
||||||
|
state = super().transmit(data, len(data))
|
||||||
|
return len(data), state
|
||||||
|
|
||||||
|
def _readData(self, len_=0):
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
length = super().getPacketLength()
|
||||||
|
|
||||||
|
if len_ < length and len_ != 0:
|
||||||
|
length = len_
|
||||||
|
|
||||||
|
data = bytearray(length)
|
||||||
|
data_mv = memoryview(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = super().readData(data_mv, length)
|
||||||
|
except AssertionError as e:
|
||||||
|
state = list(ERROR.keys())[list(ERROR.values()).index(str(e))]
|
||||||
|
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
if state == ERR_NONE or state == ERR_CRC_MISMATCH:
|
||||||
|
return bytes(data), state
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b'', state
|
||||||
|
|
||||||
|
def _startTransmit(self, data):
|
||||||
|
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return 0, ERR_INVALID_PACKET_TYPE
|
||||||
|
|
||||||
|
# --- AJOUT : FORÇAGE DU CRC À OFF AVANT L'ENVOI ---
|
||||||
|
super().setCRC(False)
|
||||||
|
|
||||||
|
state = super().startTransmit(data, len(data))
|
||||||
|
return len(data), state
|
||||||
|
|
||||||
|
def _dummyFunction(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _onIRQ(self, callback):
|
||||||
|
events = self._events()
|
||||||
|
if events & SX126X_IRQ_TX_DONE:
|
||||||
|
super().startReceive()
|
||||||
|
self._callbackFunction(events)
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
from _sx126x import *
|
||||||
|
from sx126x import SX126X
|
||||||
|
|
||||||
|
_SX126X_PA_CONFIG_SX1262 = const(0x00)
|
||||||
|
|
||||||
|
class SX1262(SX126X):
|
||||||
|
TX_DONE = SX126X_IRQ_TX_DONE
|
||||||
|
RX_DONE = SX126X_IRQ_RX_DONE
|
||||||
|
ADDR_FILT_OFF = SX126X_GFSK_ADDRESS_FILT_OFF
|
||||||
|
ADDR_FILT_NODE = SX126X_GFSK_ADDRESS_FILT_NODE
|
||||||
|
ADDR_FILT_NODE_BROAD = SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST
|
||||||
|
PREAMBLE_DETECT_OFF = SX126X_GFSK_PREAMBLE_DETECT_OFF
|
||||||
|
PREAMBLE_DETECT_8 = SX126X_GFSK_PREAMBLE_DETECT_8
|
||||||
|
PREAMBLE_DETECT_16 = SX126X_GFSK_PREAMBLE_DETECT_16
|
||||||
|
PREAMBLE_DETECT_24 = SX126X_GFSK_PREAMBLE_DETECT_24
|
||||||
|
PREAMBLE_DETECT_32 = SX126X_GFSK_PREAMBLE_DETECT_32
|
||||||
|
STATUS = ERROR
|
||||||
|
|
||||||
|
def __init__(self, spi_bus, clk, mosi, miso, cs, irq, rst, gpio):
|
||||||
|
super().__init__(spi_bus, clk, mosi, miso, cs, irq, rst, gpio)
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
|
||||||
|
def begin(self, freq=434.0, bw=125.0, sf=9, cr=7, syncWord=SX126X_SYNC_WORD_PRIVATE,
|
||||||
|
power=14, currentLimit=60.0, preambleLength=8, implicit=False, implicitLen=0xFF,
|
||||||
|
crcOn=True, txIq=False, rxIq=False, tcxoVoltage=1.6, useRegulatorLDO=False,
|
||||||
|
blocking=True):
|
||||||
|
state = super().begin(bw, sf, cr, syncWord, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO, txIq, rxIq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if not implicit:
|
||||||
|
state = super().explicitHeader()
|
||||||
|
else:
|
||||||
|
state = super().implicitHeader(implicitLen)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setCRC(crcOn)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setFrequency(freq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setOutputPower(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().fixPaClamping()
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setBlockingCallback(blocking)
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def beginFSK(self, freq=434.0, br=48.0, freqDev=50.0, rxBw=156.2, power=14, currentLimit=60.0,
|
||||||
|
preambleLength=16, dataShaping=0.5, syncWord=[0x2D, 0x01], syncBitsLength=16,
|
||||||
|
addrFilter=SX126X_GFSK_ADDRESS_FILT_OFF, addr=0x00, crcLength=2, crcInitial=0x1D0F, crcPolynomial=0x1021,
|
||||||
|
crcInverted=True, whiteningOn=True, whiteningInitial=0x0100,
|
||||||
|
fixedPacketLength=False, packetLength=0xFF, preambleDetectorLength=SX126X_GFSK_PREAMBLE_DETECT_16,
|
||||||
|
tcxoVoltage=1.6, useRegulatorLDO=False,
|
||||||
|
blocking=True):
|
||||||
|
state = super().beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping, preambleDetectorLength, tcxoVoltage, useRegulatorLDO)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setSyncBits(syncWord, syncBitsLength)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if addrFilter == SX126X_GFSK_ADDRESS_FILT_OFF:
|
||||||
|
state = super().disableAddressFiltering()
|
||||||
|
elif addrFilter == SX126X_GFSK_ADDRESS_FILT_NODE:
|
||||||
|
state = super().setNodeAddress(addr)
|
||||||
|
elif addrFilter == SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST:
|
||||||
|
state = super().setBroadcastAddress(addr)
|
||||||
|
else:
|
||||||
|
state = ERR_UNKNOWN
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setCRC(crcLength, crcInitial, crcPolynomial, crcInverted)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setWhitening(whiteningOn, whiteningInitial)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if fixedPacketLength:
|
||||||
|
state = super().fixedPacketLengthMode(packetLength)
|
||||||
|
else:
|
||||||
|
state = super().variablePacketLengthMode(packetLength)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setFrequency(freq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setOutputPower(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().fixPaClamping()
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setBlockingCallback(blocking)
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def setFrequency(self, freq, calibrate=True):
|
||||||
|
if freq < 150.0 or freq > 960.0:
|
||||||
|
return ERR_INVALID_FREQUENCY
|
||||||
|
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
if calibrate:
|
||||||
|
data = bytearray(2)
|
||||||
|
if freq > 900.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_902_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_902_MHZ_2
|
||||||
|
elif freq > 850.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_863_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_863_MHZ_2
|
||||||
|
elif freq > 770.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_779_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_779_MHZ_2
|
||||||
|
elif freq > 460.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_470_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_470_MHZ_2
|
||||||
|
else:
|
||||||
|
data[0] = SX126X_CAL_IMG_430_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_430_MHZ_2
|
||||||
|
state = super().calibrateImage(data)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
return super().setFrequencyRaw(freq)
|
||||||
|
|
||||||
|
def setOutputPower(self, power):
|
||||||
|
if not ((power >= -9) and (power <= 22)):
|
||||||
|
return ERR_INVALID_OUTPUT_POWER
|
||||||
|
|
||||||
|
ocp = bytearray(1)
|
||||||
|
ocp_mv = memoryview(ocp)
|
||||||
|
state = super().readRegister(SX126X_REG_OCP_CONFIGURATION, ocp_mv, 1)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setPaConfig(0x04, _SX126X_PA_CONFIG_SX1262)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setTxParams(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
return super().writeRegister(SX126X_REG_OCP_CONFIGURATION, ocp, 1)
|
||||||
|
|
||||||
|
def setTxIq(self, txIq):
|
||||||
|
self._txIq = txIq
|
||||||
|
|
||||||
|
def setRxIq(self, rxIq):
|
||||||
|
self._rxIq = rxIq
|
||||||
|
if not self.blocking:
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
def setPreambleDetectorLength(self, preambleDetectorLength):
|
||||||
|
self._preambleDetectorLength = preambleDetectorLength
|
||||||
|
if not self.blocking:
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
def setBlockingCallback(self, blocking, callback=None):
|
||||||
|
self.blocking = blocking
|
||||||
|
if not self.blocking:
|
||||||
|
state = super().startReceive()
|
||||||
|
ASSERT(state)
|
||||||
|
if callback != None:
|
||||||
|
self._callbackFunction = callback
|
||||||
|
super().setDio1Action(self._onIRQ)
|
||||||
|
else:
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
super().clearDio1Action()
|
||||||
|
return state
|
||||||
|
else:
|
||||||
|
state = super().standby()
|
||||||
|
ASSERT(state)
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
super().clearDio1Action()
|
||||||
|
return state
|
||||||
|
|
||||||
|
def recv(self, len=0, timeout_en=False, timeout_ms=0):
|
||||||
|
if not self.blocking:
|
||||||
|
return self._readData(len)
|
||||||
|
else:
|
||||||
|
return self._receive(len, timeout_en, timeout_ms)
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
if not self.blocking:
|
||||||
|
return self._startTransmit(data)
|
||||||
|
else:
|
||||||
|
return self._transmit(data)
|
||||||
|
|
||||||
|
def _events(self):
|
||||||
|
return super().getIrqStatus()
|
||||||
|
|
||||||
|
def _receive(self, len_=0, timeout_en=False, timeout_ms=0):
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
length = len_
|
||||||
|
|
||||||
|
if len_ == 0:
|
||||||
|
length = SX126X_MAX_PACKET_LENGTH
|
||||||
|
|
||||||
|
data = bytearray(length)
|
||||||
|
data_mv = memoryview(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = super().receive(data_mv, length, timeout_en, timeout_ms)
|
||||||
|
except AssertionError as e:
|
||||||
|
state = list(ERROR.keys())[list(ERROR.values()).index(str(e))]
|
||||||
|
|
||||||
|
if state == ERR_NONE or state == ERR_CRC_MISMATCH:
|
||||||
|
if len_ == 0:
|
||||||
|
length = super().getPacketLength(False)
|
||||||
|
data = data[:length]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b'', state
|
||||||
|
|
||||||
|
return bytes(data), state
|
||||||
|
|
||||||
|
def _transmit(self, data):
|
||||||
|
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return 0, ERR_INVALID_PACKET_TYPE
|
||||||
|
|
||||||
|
state = super().transmit(data, len(data))
|
||||||
|
return len(data), state
|
||||||
|
|
||||||
|
def _readData(self, len_=0):
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
length = super().getPacketLength()
|
||||||
|
|
||||||
|
if len_ < length and len_ != 0:
|
||||||
|
length = len_
|
||||||
|
|
||||||
|
data = bytearray(length)
|
||||||
|
data_mv = memoryview(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = super().readData(data_mv, length)
|
||||||
|
except AssertionError as e:
|
||||||
|
state = list(ERROR.keys())[list(ERROR.values()).index(str(e))]
|
||||||
|
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
if state == ERR_NONE or state == ERR_CRC_MISMATCH:
|
||||||
|
return bytes(data), state
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b'', state
|
||||||
|
|
||||||
|
def _startTransmit(self, data):
|
||||||
|
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return 0, ERR_INVALID_PACKET_TYPE
|
||||||
|
|
||||||
|
state = super().startTransmit(data, len(data))
|
||||||
|
return len(data), state
|
||||||
|
|
||||||
|
def _dummyFunction(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _onIRQ(self, callback):
|
||||||
|
events = self._events()
|
||||||
|
if events & SX126X_IRQ_TX_DONE:
|
||||||
|
super().startReceive()
|
||||||
|
self._callbackFunction(events)
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
from _sx126x import *
|
||||||
|
from sx126x import SX126X
|
||||||
|
|
||||||
|
_SX126X_PA_CONFIG_SX1268 = const(0x00)
|
||||||
|
|
||||||
|
class SX1268(SX126X):
|
||||||
|
TX_DONE = SX126X_IRQ_TX_DONE
|
||||||
|
RX_DONE = SX126X_IRQ_RX_DONE
|
||||||
|
ADDR_FILT_OFF = SX126X_GFSK_ADDRESS_FILT_OFF
|
||||||
|
ADDR_FILT_NODE = SX126X_GFSK_ADDRESS_FILT_NODE
|
||||||
|
ADDR_FILT_NODE_BROAD = SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST
|
||||||
|
PREAMBLE_DETECT_OFF = SX126X_GFSK_PREAMBLE_DETECT_OFF
|
||||||
|
PREAMBLE_DETECT_8 = SX126X_GFSK_PREAMBLE_DETECT_8
|
||||||
|
PREAMBLE_DETECT_16 = SX126X_GFSK_PREAMBLE_DETECT_16
|
||||||
|
PREAMBLE_DETECT_24 = SX126X_GFSK_PREAMBLE_DETECT_24
|
||||||
|
PREAMBLE_DETECT_32 = SX126X_GFSK_PREAMBLE_DETECT_32
|
||||||
|
STATUS = ERROR
|
||||||
|
|
||||||
|
def __init__(self, spi_bus, clk, mosi, miso, cs, irq, rst, gpio):
|
||||||
|
super().__init__(spi_bus, clk, mosi, miso, cs, irq, rst, gpio)
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
|
||||||
|
def begin(self, freq=434.0, bw=125.0, sf=9, cr=7, syncWord=SX126X_SYNC_WORD_PRIVATE,
|
||||||
|
power=14, currentLimit=60.0, preambleLength=8, implicit=False, implicitLen=0xFF,
|
||||||
|
crcOn=True, txIq=False, rxIq=False, tcxoVoltage=1.6, useRegulatorLDO=False,
|
||||||
|
blocking=True):
|
||||||
|
state = super().begin(bw, sf, cr, syncWord, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO, txIq, rxIq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if not implicit:
|
||||||
|
state = super().explicitHeader()
|
||||||
|
else:
|
||||||
|
state = super().implicitHeader(implicitLen)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setCRC(crcOn)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setFrequency(freq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setOutputPower(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().fixPaClamping()
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setBlockingCallback(blocking)
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def beginFSK(self, freq=434.0, br=48.0, freqDev=50.0, rxBw=156.2, power=14, currentLimit=60.0,
|
||||||
|
preambleLength=16, dataShaping=0.5, syncWord=[0x2D, 0x01], syncBitsLength=16,
|
||||||
|
addrFilter=SX126X_GFSK_ADDRESS_FILT_OFF, addr=0x00, crcLength=2, crcInitial=0x1D0F, crcPolynomial=0x1021,
|
||||||
|
crcInverted=True, whiteningOn=True, whiteningInitial=0x0100,
|
||||||
|
fixedPacketLength=False, packetLength=0xFF, preambleDetectorLength=SX126X_GFSK_PREAMBLE_DETECT_16,
|
||||||
|
tcxoVoltage=1.6, useRegulatorLDO=False,
|
||||||
|
blocking=True):
|
||||||
|
state = super().beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping, preambleDetectorLength, tcxoVoltage, useRegulatorLDO)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setSyncBits(syncWord, syncBitsLength)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if addrFilter == SX126X_GFSK_ADDRESS_FILT_OFF:
|
||||||
|
state = super().disableAddressFiltering()
|
||||||
|
elif addrFilter == SX126X_GFSK_ADDRESS_FILT_NODE:
|
||||||
|
state = super().setNodeAddress(addr)
|
||||||
|
elif addrFilter == SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST:
|
||||||
|
state = super().setBroadcastAddress(addr)
|
||||||
|
else:
|
||||||
|
state = ERR_UNKNOWN
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setCRC(crcLength, crcInitial, crcPolynomial, crcInverted)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setWhitening(whiteningOn, whiteningInitial)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
if fixedPacketLength:
|
||||||
|
state = super().fixedPacketLengthMode(packetLength)
|
||||||
|
else:
|
||||||
|
state = super().variablePacketLengthMode(packetLength)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setFrequency(freq)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setOutputPower(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().fixPaClamping()
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = self.setBlockingCallback(blocking)
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def setFrequency(self, freq, calibrate=True):
|
||||||
|
if freq < 410.0 or freq > 810.0:
|
||||||
|
return ERR_INVALID_FREQUENCY
|
||||||
|
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
if calibrate:
|
||||||
|
data = bytearray(2)
|
||||||
|
if freq > 770.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_779_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_779_MHZ_2
|
||||||
|
elif freq > 460.0:
|
||||||
|
data[0] = SX126X_CAL_IMG_470_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_470_MHZ_2
|
||||||
|
else:
|
||||||
|
data[0] = SX126X_CAL_IMG_430_MHZ_1
|
||||||
|
data[1] = SX126X_CAL_IMG_430_MHZ_2
|
||||||
|
state = super().calibrateImage(data)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
return super().setFrequencyRaw(freq)
|
||||||
|
|
||||||
|
def setOutputPower(self, power):
|
||||||
|
if not ((power >= -9) and (power <= 22)):
|
||||||
|
return ERR_INVALID_OUTPUT_POWER
|
||||||
|
|
||||||
|
ocp = bytearray(1)
|
||||||
|
ocp_mv = memoryview(ocp)
|
||||||
|
state = super().readRegister(SX126X_REG_OCP_CONFIGURATION, ocp_mv, 1)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setPaConfig(0x04, _SX126X_PA_CONFIG_SX1268)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
state = super().setTxParams(power)
|
||||||
|
ASSERT(state)
|
||||||
|
|
||||||
|
return super().writeRegister(SX126X_REG_OCP_CONFIGURATION, ocp, 1)
|
||||||
|
|
||||||
|
def setTxIq(self, txIq):
|
||||||
|
self._txIq = txIq
|
||||||
|
|
||||||
|
def setRxIq(self, rxIq):
|
||||||
|
self._rxIq = rxIq
|
||||||
|
if not self.blocking:
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
def setPreambleDetectorLength(self, preambleDetectorLength):
|
||||||
|
self._preambleDetectorLength = preambleDetectorLength
|
||||||
|
if not self.blocking:
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
def setBlockingCallback(self, blocking, callback=None):
|
||||||
|
self.blocking = blocking
|
||||||
|
if not self.blocking:
|
||||||
|
state = super().startReceive()
|
||||||
|
ASSERT(state)
|
||||||
|
if callback != None:
|
||||||
|
self._callbackFunction = callback
|
||||||
|
super().setDio1Action(self._onIRQ)
|
||||||
|
else:
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
super().clearDio1Action()
|
||||||
|
return state
|
||||||
|
else:
|
||||||
|
state = super().standby()
|
||||||
|
ASSERT(state)
|
||||||
|
self._callbackFunction = self._dummyFunction
|
||||||
|
super().clearDio1Action()
|
||||||
|
return state
|
||||||
|
|
||||||
|
def recv(self, len=0, timeout_en=False, timeout_ms=0):
|
||||||
|
if not self.blocking:
|
||||||
|
return self._readData(len)
|
||||||
|
else:
|
||||||
|
return self._receive(len, timeout_en, timeout_ms)
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
if not self.blocking:
|
||||||
|
return self._startTransmit(data)
|
||||||
|
else:
|
||||||
|
return self._transmit(data)
|
||||||
|
|
||||||
|
def _events(self):
|
||||||
|
return super().getIrqStatus()
|
||||||
|
|
||||||
|
def _receive(self, len_=0, timeout_en=False, timeout_ms=0):
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
length = len_
|
||||||
|
|
||||||
|
if len_ == 0:
|
||||||
|
length = SX126X_MAX_PACKET_LENGTH
|
||||||
|
|
||||||
|
data = bytearray(length)
|
||||||
|
data_mv = memoryview(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = super().receive(data_mv, length, timeout_en, timeout_ms)
|
||||||
|
except AssertionError as e:
|
||||||
|
state = list(ERROR.keys())[list(ERROR.values()).index(str(e))]
|
||||||
|
|
||||||
|
if state == ERR_NONE or state == ERR_CRC_MISMATCH:
|
||||||
|
if len_ == 0:
|
||||||
|
length = super().getPacketLength(False)
|
||||||
|
data = data[:length]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b'', state
|
||||||
|
|
||||||
|
return bytes(data), state
|
||||||
|
|
||||||
|
def _transmit(self, data):
|
||||||
|
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return 0, ERR_INVALID_PACKET_TYPE
|
||||||
|
|
||||||
|
state = super().transmit(data, len(data))
|
||||||
|
return len(data), state
|
||||||
|
|
||||||
|
def _readData(self, len_=0):
|
||||||
|
state = ERR_NONE
|
||||||
|
|
||||||
|
length = super().getPacketLength()
|
||||||
|
|
||||||
|
if len_ < length and len_ != 0:
|
||||||
|
length = len_
|
||||||
|
|
||||||
|
data = bytearray(length)
|
||||||
|
data_mv = memoryview(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = super().readData(data_mv, length)
|
||||||
|
except AssertionError as e:
|
||||||
|
state = list(ERROR.keys())[list(ERROR.values()).index(str(e))]
|
||||||
|
|
||||||
|
ASSERT(super().startReceive())
|
||||||
|
|
||||||
|
if state == ERR_NONE or state == ERR_CRC_MISMATCH:
|
||||||
|
return bytes(data), state
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b'', state
|
||||||
|
|
||||||
|
def _startTransmit(self, data):
|
||||||
|
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return 0, ERR_INVALID_PACKET_TYPE
|
||||||
|
|
||||||
|
state = super().startTransmit(data, len(data))
|
||||||
|
return len(data), state
|
||||||
|
|
||||||
|
def _dummyFunction(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _onIRQ(self, callback):
|
||||||
|
events = self._events()
|
||||||
|
if events & SX126X_IRQ_TX_DONE:
|
||||||
|
super().startReceive()
|
||||||
|
self._callbackFunction(events)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
|||||||
|
import _thread
|
||||||
|
from machine import Pin
|
||||||
|
from shared import get_lora
|
||||||
|
from shared import deviceTypes
|
||||||
|
from shared import config
|
||||||
|
import time
|
||||||
|
|
||||||
|
# --- Configuration Matérielle ---
|
||||||
|
vext = Pin(19, Pin.OUT)
|
||||||
|
vext.value(0)
|
||||||
|
time.sleep_ms(100)
|
||||||
|
|
||||||
|
# --- Lecture de l'ID unique de l'ESP ---
|
||||||
|
try:
|
||||||
|
with open("device_id.txt", "r") as f:
|
||||||
|
DEVICE_ID = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
DEVICE_ID = "ESP32_Inconnu"
|
||||||
|
|
||||||
|
# --- Initialisation LoRa ---
|
||||||
|
lora = get_lora()
|
||||||
|
lora.configure(freq=868.1, sf=7)
|
||||||
|
|
||||||
|
print(f"ESP32 initialisé avec l'ID : '{DEVICE_ID}' (Type : {deviceTypes.DEVICE_TYPES['MICROWAVE']})")
|
||||||
|
|
||||||
|
def heartbeat_loop():
|
||||||
|
while True:
|
||||||
|
print(f"\nESP32 : Envoi du Heartbeat...")
|
||||||
|
# Envoi périodique
|
||||||
|
ping_payload = {
|
||||||
|
"id": DEVICE_ID,
|
||||||
|
"type": deviceTypes.DEVICE_TYPES["MICROWAVE"]
|
||||||
|
}
|
||||||
|
lora.send(ping_payload)
|
||||||
|
|
||||||
|
# Le receive_packet est maintenant protégé par le lock dans lora_device
|
||||||
|
# Si le main thread utilise la radio, ce thread attendra son tour
|
||||||
|
paquet = lora.receive_packet(timeout_ms=2000)
|
||||||
|
|
||||||
|
if paquet and not paquet["raw"]:
|
||||||
|
donnees = paquet["data"]
|
||||||
|
# Vérification si le paquet reçu est bien la réponse attendue de l'orchestrateur
|
||||||
|
if donnees.get("type") == deviceTypes.DEVICE_TYPES["ORCHESTRATOR"]:
|
||||||
|
print(f"ESP32 : Réponse reçue de l'orchestrateur '{donnees.get('id')}' ! [Statut: ALIVE]")
|
||||||
|
else:
|
||||||
|
print(f"ESP32 : Paquet reçu d'un type inattendu : {donnees.get('type')}")
|
||||||
|
else:
|
||||||
|
print("ESP32 : Pas de réponse de l'orchestrateur (Le RPI est-il éteint ?)")
|
||||||
|
|
||||||
|
time.sleep(config.HEARTBEAT_INTERVAL)
|
||||||
|
|
||||||
|
# Lancer la boucle de heartbeat dans un thread séparé
|
||||||
|
_thread.start_new_thread(heartbeat_loop, ())
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Fait rien pour l'instant
|
||||||
|
time.sleep(1)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from machine import Pin
|
||||||
|
from shared import get_lora
|
||||||
|
import time
|
||||||
|
|
||||||
|
vext = Pin(19, Pin.OUT)
|
||||||
|
vext.value(0)
|
||||||
|
time.sleep_ms(100)
|
||||||
|
|
||||||
|
lora = get_lora()
|
||||||
|
lora.configure(freq=868.1, sf=7)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print("\nESP32 : Envoi des mesures...")
|
||||||
|
mesures = {"id": "ESP32_Salon", "temp": 22.4, "hum": 55.2}
|
||||||
|
|
||||||
|
# Envoi direct (le pilote s'occupe de mettre le groupe \x02)
|
||||||
|
lora.send(b'\x02' + lora.send_json_bytes_helper if False else bytes([2]) + lora.send_helper if False else b'\x02' + __import__('ujson').dumps(mesures).encode('utf-8'))
|
||||||
|
|
||||||
|
# Réception propre
|
||||||
|
paquet = lora.receive_packet(3000)
|
||||||
|
if paquet:
|
||||||
|
# paquet est un dict : {"group": 2, "data": {...}, "raw": False}
|
||||||
|
print(f"ESP32 : Message reçu du groupe {paquet['group']}")
|
||||||
|
print(f"ESP32 : Données utiles -> {paquet['data']}")
|
||||||
|
else:
|
||||||
|
print("ESP32 : Pas de réponse.")
|
||||||
|
|
||||||
|
time.sleep(5)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
from machine import Pin, SoftI2C
|
||||||
|
import framebuf
|
||||||
|
import ssd1306
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
image_cards_hearts_bits = bytearray(b'\x00\x00\x00\x0088||\xfe\xfe\xff\xfe\xff\xfe\xff\xfe\x7f\xfc?\xf8\x1f\xf0\x0f\xe0\x07\xc0\x03\x80\x01\x00\x00\x00')
|
||||||
|
image_clock_solid_full_bits = bytearray(b'\x00\x1f\x00\x00\x00\xff\xe0\x00\x03\xff\xf8\x00\x07\xff\xfc\x00\x0f\xff\xfe\x00\x1f\xff\xff\x00?\xf3\xff\x00?\xf3\xff\x80\x7f\xf3\xff\x80\x7f\xf3\xff\xc0\x7f\xf3\xff\xc0\xff\xf3\xff\xc0\xff\xf3\xff\xc0\xff\xf1\xff\xc0\xff\xf8\x7f\xc0\xff\xfe?\xc0\x7f\xff\x1f\xc0\x7f\xff\xdf\xc0\x7f\xff\xff\x80?\xff\xff\x80?\xff\xff\x00\x1f\xff\xff\x00\x0f\xff\xfe\x00\x07\xff\xfc\x00\x03\xff\xf0\x00\x00\x7f\xc0\x00')
|
||||||
|
|
||||||
|
def draw():
|
||||||
|
# Layer 1
|
||||||
|
display.rect(0, 0, 128, 64, 1)
|
||||||
|
|
||||||
|
# Layer 2
|
||||||
|
display.text("HELP", 1, 2, 1)
|
||||||
|
|
||||||
|
# cards_hearts
|
||||||
|
fb_image_cards_hearts_bits = framebuf.FrameBuffer(image_cards_hearts_bits, 15, 16, framebuf.MONO_HLSB)
|
||||||
|
display.blit(fb_image_cards_hearts_bits, 111, 47)
|
||||||
|
|
||||||
|
# clock_solid_full
|
||||||
|
fb_image_clock_solid_full_bits = framebuf.FrameBuffer(image_clock_solid_full_bits, 26, 26, framebuf.MONO_HLSB)
|
||||||
|
display.blit(fb_image_clock_solid_full_bits, 51, 19)
|
||||||
|
|
||||||
|
display.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 3. Initialize I2C (using Software I2C for better stability on ESP32-S3 pins)
|
||||||
|
# SDA is GPIO 17, SCL is GPIO 18
|
||||||
|
scl_pin = Pin(18, Pin.OUT, pull=Pin.PULL_UP)
|
||||||
|
sda_pin = Pin(17, Pin.OUT, pull=Pin.PULL_UP)
|
||||||
|
i2c = SoftI2C(scl=scl_pin, sda=sda_pin, freq=100000)
|
||||||
|
|
||||||
|
# 4. Bind the SSD1306 driver to our I2C bus (default address is 0x3C)
|
||||||
|
# Display is 128x64 pixels
|
||||||
|
display = ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)
|
||||||
|
|
||||||
|
# 5. Clear and write Hello World!
|
||||||
|
draw()
|
||||||
|
|
||||||
|
print("Hello World printed to OLED successfully!")
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
# IP Adresses
|
# IP Adresses
|
||||||
|
|
||||||
- AP (dynamic) : `10.85.213.190`
|
- AP (dynamic) : `10.110.0.190`
|
||||||
- Generated Wifi : `192.168.50.1`
|
- Generated Wifi : `192.168.50.1`
|
||||||
@@ -4,7 +4,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
MQTT_TLS_ENABLED: ${MQTT_TLS_ENABLED:-true}
|
MQTT_TLS_ENABLED: ${MQTT_TLS_ENABLED:-true}
|
||||||
ports:
|
ports:
|
||||||
- "8884:8884"
|
- "192.168.50.1:8884:8884"
|
||||||
volumes:
|
volumes:
|
||||||
- ./mqtt/mosquitto-tls.conf:/mosquitto/config/mosquitto-tls.conf:ro
|
- ./mqtt/mosquitto-tls.conf:/mosquitto/config/mosquitto-tls.conf:ro
|
||||||
- ./mqtt/mosquitto-plain.conf:/mosquitto/config/mosquitto-plain.conf:ro
|
- ./mqtt/mosquitto-plain.conf:/mosquitto/config/mosquitto-plain.conf:ro
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
+29
-4
@@ -12,14 +12,39 @@ if [ ! -f "$PYTHON_SCRIPT" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PYTHONPATH="$REPO_ROOT${PYTHONPATH:+:$PYTHONPATH}"
|
# 1. On définit proprement le PYTHONPATH pour tout le script
|
||||||
cd "$SCRIPT_DIR"
|
export PYTHONPATH="$REPO_ROOT"
|
||||||
docker compose pull
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# 2. On lance Docker en arrière-plan
|
||||||
|
echo "Démarrage des conteneurs Docker..."
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d --remove-orphans
|
||||||
|
|
||||||
|
# 3. Installation des dépendances (sans '--user' si on est déjà root sous systemd)
|
||||||
if [ -f "$REQUIREMENTS_FILE" ]; then
|
if [ -f "$REQUIREMENTS_FILE" ]; then
|
||||||
|
echo "Vérification des dépendances Python..."
|
||||||
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
|
"$PYTHON_BIN" -m pip install -r "$REQUIREMENTS_FILE"
|
||||||
|
else
|
||||||
"$PYTHON_BIN" -m pip install --user -r "$REQUIREMENTS_FILE"
|
"$PYTHON_BIN" -m pip install --user -r "$REQUIREMENTS_FILE"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Sécurité anti-conflit : On vérifie si Docker n'a pas verrouillé le port USB de la LA66
|
||||||
|
PORT="/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0"
|
||||||
|
if [ -e "$PORT" ]; then
|
||||||
|
echo "Vérification de la disponibilité du port série..."
|
||||||
|
# Si le port est occupé, on force sa libération en tuant le processus docker/screen persistant
|
||||||
|
if lsof "$PORT" >/dev/null 2>&1; then
|
||||||
|
echo "Le port série est occupé ! Libération en cours..."
|
||||||
|
fuser -k "$PORT" || true
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Lancement propre du script Python
|
||||||
|
echo "Launching Python script: $PYTHON_SCRIPT"
|
||||||
cd "$REPO_ROOT"
|
cd "$REPO_ROOT"
|
||||||
exec "$PYTHON_BIN" "$PYTHON_SCRIPT"
|
exec "$PYTHON_BIN" "$PYTHON_SCRIPT"
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import threading
|
||||||
|
import queue
|
||||||
|
import time
|
||||||
|
from shared import get_lora, deviceTypes
|
||||||
|
|
||||||
|
# --- Lecture de l'ID unique du Raspberry Pi ---
|
||||||
|
try:
|
||||||
|
with open("device_id.txt", "r") as f:
|
||||||
|
DEVICE_ID = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
# Alternative si le script est lancé depuis un autre dossier
|
||||||
|
try:
|
||||||
|
with open("/home/pi/SmartWave/orchestrateur/device_id.txt", "r") as f:
|
||||||
|
DEVICE_ID = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
DEVICE_ID = "RPI_Orchestrateur_Default"
|
||||||
|
|
||||||
|
# Création de la file d'attente pour les messages (thread-safe)
|
||||||
|
data_queue = queue.Queue()
|
||||||
|
|
||||||
|
lora = get_lora()
|
||||||
|
lora.configure()
|
||||||
|
|
||||||
|
def lora_listener():
|
||||||
|
"""Thread de fond : écoute en permanence et répond aux Heartbeats."""
|
||||||
|
print("Thread Écouteur démarré.")
|
||||||
|
while True:
|
||||||
|
# On attend un paquet (timeout court pour rester réactif)
|
||||||
|
paquet = lora.receive_packet(timeout_ms=1000)
|
||||||
|
|
||||||
|
if paquet:
|
||||||
|
donnees = paquet["data"]
|
||||||
|
expediteur_type = donnees.get("type")
|
||||||
|
|
||||||
|
# --- Cas 1 : Gestion automatique du Heartbeat ---
|
||||||
|
if expediteur_type == deviceTypes.DEVICE_TYPES["MICROWAVE"]:
|
||||||
|
print(f"\n[Thread Fond] Heartbeat reçu de {donnees.get('id')}")
|
||||||
|
|
||||||
|
reponse = {
|
||||||
|
"id": DEVICE_ID, # Ou lecture de ton fichier device_id.txt
|
||||||
|
"type": deviceTypes.DEVICE_TYPES["ORCHESTRATOR"]
|
||||||
|
}
|
||||||
|
lora.send(reponse)
|
||||||
|
|
||||||
|
# --- Cas 2 : Donnée applicative, on l'envoie vers le thread principal ---
|
||||||
|
else:
|
||||||
|
data_queue.put(paquet)
|
||||||
|
|
||||||
|
# 2. Lancement du thread d'écoute
|
||||||
|
listener_thread = threading.Thread(target=lora_listener, daemon=True)
|
||||||
|
listener_thread.start()
|
||||||
|
|
||||||
|
# 3. Boucle principale (Main Thread) : tu es libre de faire autre chose !
|
||||||
|
print("Orchestrateur prêt. Le main loop est libre.")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# On regarde si on a reçu des données applicatives (non-heartbeat)
|
||||||
|
# On utilise block=False pour ne pas bloquer si la queue est vide
|
||||||
|
try:
|
||||||
|
msg = data_queue.get(block=False)
|
||||||
|
print(f"\n[Main Loop] Données traitées : {msg['data']}")
|
||||||
|
except queue.Empty:
|
||||||
|
pass # Rien à traiter, on fait autre chose...
|
||||||
|
|
||||||
|
# Ici tu peux faire tes autres tâches
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
break
|
||||||
@@ -8,5 +8,5 @@ log_type notice
|
|||||||
log_type information
|
log_type information
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
listener 8884
|
listener 8884 192.168.50.1
|
||||||
protocol mqtt
|
protocol mqtt
|
||||||
@@ -8,7 +8,7 @@ log_type notice
|
|||||||
log_type information
|
log_type information
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
listener 8884
|
listener 8884 192.168.50.1
|
||||||
protocol mqtt
|
protocol mqtt
|
||||||
cafile /mosquitto/certs/ca.crt
|
cafile /mosquitto/certs/ca.crt
|
||||||
certfile /mosquitto/certs/server.crt
|
certfile /mosquitto/certs/server.crt
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from shared import get_lora
|
||||||
|
import time
|
||||||
|
|
||||||
|
lora = get_lora()
|
||||||
|
lora.configure()
|
||||||
|
|
||||||
|
print("Raspberry Pi : En attente active de JSON...")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
paquet = lora.receive_packet(timeout_ms=5000)
|
||||||
|
if paquet:
|
||||||
|
# Plus besoin de décoder du HEX ou de parser du JSON manuellement !
|
||||||
|
groupe = paquet['group']
|
||||||
|
donnees = paquet['data']
|
||||||
|
|
||||||
|
print(f"\nRPI : Reçu du Groupe {groupe} -> {donnees}")
|
||||||
|
if not paquet['raw']: # C'est bien du JSON (un dictionnaire Python)
|
||||||
|
print(f"RPI : Température du capteur = {donnees.get('temp')}°C")
|
||||||
|
|
||||||
|
# Préparation de la réponse (on passe directement un dictionnaire !)
|
||||||
|
reponse = {
|
||||||
|
"status": "ack",
|
||||||
|
"msg": "Donnees bien recues, merci !",
|
||||||
|
"time": int(time.time())
|
||||||
|
}
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
print("RPI : Envoi de la réponse...")
|
||||||
|
lora.send(reponse) # Le pilote sérialise et encode en HEX à ta place !
|
||||||
|
else:
|
||||||
|
print("RPI : Rien reçu.")
|
||||||
@@ -1 +1,2 @@
|
|||||||
paho-mqtt>=1.6,<3
|
paho-mqtt>=1.6,<3
|
||||||
|
pyserial>=3.5,<4
|
||||||
|
|||||||
+13
-2
@@ -1,6 +1,17 @@
|
|||||||
|
# shared/__init__.py
|
||||||
"""Shared helpers for the smartWave project."""
|
"""Shared helpers for the smartWave project."""
|
||||||
|
|
||||||
from .db import DRIVER_NAME, Database, connect, execute, fetchall, fetchone
|
import shared.deviceTypes as deviceTypes
|
||||||
from .mqtt import BACKEND_NAME as MQTT_BACKEND_NAME, BrokerClient, connect as mqtt_connect, publish as mqtt_publish
|
import shared.config as config
|
||||||
|
|
||||||
|
def get_lora(*args, **kwargs):
|
||||||
|
from .lora_device import get_lora_device
|
||||||
|
return get_lora_device(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_database(*args, **kwargs):
|
||||||
|
from .db import Database
|
||||||
|
return Database(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_mqtt_client(*args, **kwargs):
|
||||||
|
from .mqtt import BrokerClient
|
||||||
|
return BrokerClient(*args, **kwargs)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
HEARTBEAT_INTERVAL = 10
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
# Un simple dictionnaire servant de registre des types d'appareils
|
||||||
|
DEVICE_TYPES = {
|
||||||
|
"MICROWAVE": "microwave",
|
||||||
|
"ORCHESTRATOR": "orchestrator",
|
||||||
|
"EXTERNAL_DISPLAY": "externalDisplay"
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
IS_MICROPYTHON = sys.implementation.name == 'micropython'
|
||||||
|
|
||||||
|
if IS_MICROPYTHON:
|
||||||
|
import _thread
|
||||||
|
from machine import Pin, SPI
|
||||||
|
import ubinascii
|
||||||
|
import ujson as json
|
||||||
|
|
||||||
|
# --- PILOTE SPI DIRECT (ESP32 / Heltec V3) ---
|
||||||
|
class LoraHardwareSPI:
|
||||||
|
def __init__(self, spi_bus=1, clk=9, mosi=10, miso=11, cs=8, irq=14, rst=12, gpio=13):
|
||||||
|
from sx1262 import SX1262
|
||||||
|
self.lora = SX1262(
|
||||||
|
spi_bus=spi_bus, clk=clk, mosi=mosi, miso=miso,
|
||||||
|
cs=cs, irq=irq, rst=rst, gpio=gpio
|
||||||
|
)
|
||||||
|
self.default_group = 2 # On définit le groupe par défaut ici
|
||||||
|
self.lock = _thread.allocate_lock() # Création du verrou
|
||||||
|
|
||||||
|
def configure(self, freq=868.1, bw=125.0, sf=7, cr=5, power=14):
|
||||||
|
self.lora.begin(
|
||||||
|
freq=freq, bw=bw, sf=sf, cr=cr, power=power,
|
||||||
|
useRegulatorLDO=False, crcOn=True, preambleLength=8, implicit=False
|
||||||
|
)
|
||||||
|
self.lora.setSyncWord(0x14)
|
||||||
|
|
||||||
|
def send(self, payload, group=None):
|
||||||
|
"""Encode la payload en JSON si nécessaire, et injecte automatiquement l'octet de groupe."""
|
||||||
|
with self.lock:
|
||||||
|
if group is None:
|
||||||
|
group = self.default_group
|
||||||
|
|
||||||
|
# Si c'est un dictionnaire ou une liste, on le convertit en JSON textuel
|
||||||
|
if isinstance(payload, (dict, list)):
|
||||||
|
payload = json.dumps(payload)
|
||||||
|
|
||||||
|
if isinstance(payload, str):
|
||||||
|
payload = payload.encode('utf-8')
|
||||||
|
|
||||||
|
# Insertion automatique de l'octet de groupe au tout début de la trame physique
|
||||||
|
paquet_physique = bytes([group]) + payload
|
||||||
|
self.lora.send(paquet_physique)
|
||||||
|
|
||||||
|
def receive_packet(self, timeout_ms=1000):
|
||||||
|
"""Écoute, nettoie, extrait le groupe, gère le HEX et parse le JSON."""
|
||||||
|
with self.lock:
|
||||||
|
data, state = self.lora.recv(len=0, timeout_en=True, timeout_ms=timeout_ms)
|
||||||
|
if state == 0 and len(data) > 1:
|
||||||
|
group = data[0]
|
||||||
|
payload_brute = data[1:].strip(b'\x00 \r\n\t')
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = payload_brute.decode('utf-8').strip('\x00 \r\n\t')
|
||||||
|
except UnicodeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if text.startswith('{') or text.startswith('['):
|
||||||
|
decoded_text = text
|
||||||
|
elif text.lower().startswith('7b') or text.lower().startswith('5b'):
|
||||||
|
try:
|
||||||
|
decoded_text = ubinascii.unhexlify(text).decode('utf-8').strip('\x00 \r\n\t')
|
||||||
|
except Exception:
|
||||||
|
decoded_text = text
|
||||||
|
else:
|
||||||
|
decoded_text = text
|
||||||
|
|
||||||
|
try:
|
||||||
|
parsed_json = json.loads(decoded_text)
|
||||||
|
return {"group": group, "data": parsed_json, "raw": False}
|
||||||
|
except ValueError:
|
||||||
|
return {"group": group, "data": decoded_text, "raw": True}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
import threading
|
||||||
|
import serial
|
||||||
|
import json
|
||||||
|
|
||||||
|
# --- PILOTE SÉRIE (Raspberry Pi / Dragino LA66) ---
|
||||||
|
class LoraSerialAT:
|
||||||
|
def __init__(self, port):
|
||||||
|
self.port = port
|
||||||
|
self.ser = serial.Serial(
|
||||||
|
port=self.port,
|
||||||
|
baudrate=9600,
|
||||||
|
bytesize=serial.EIGHTBITS,
|
||||||
|
parity=serial.PARITY_NONE,
|
||||||
|
stopbits=serial.STOPBITS_ONE,
|
||||||
|
timeout=0.1
|
||||||
|
)
|
||||||
|
self.ser.reset_input_buffer()
|
||||||
|
self.ser.reset_output_buffer()
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
def configure(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send(self, payload):
|
||||||
|
"""Encode automatiquement la payload en HEX pour l'envoi via la clé."""
|
||||||
|
with self.lock:
|
||||||
|
if isinstance(payload, (dict, list)):
|
||||||
|
payload = json.dumps(payload)
|
||||||
|
|
||||||
|
if isinstance(payload, str):
|
||||||
|
payload = payload.encode('utf-8')
|
||||||
|
|
||||||
|
hex_payload = payload.hex()
|
||||||
|
self.ser.reset_input_buffer()
|
||||||
|
|
||||||
|
# La clé ajoute d'elle-même l'octet de groupe configuré dans ses registres
|
||||||
|
cmd = f"AT+SEND=1,{hex_payload},1,3\r\n"
|
||||||
|
print(f"RPI : Envoi de la commande HEX -> AT+SEND=1,[HEX_DATA],1,3")
|
||||||
|
self.ser.write(cmd.encode('utf-8'))
|
||||||
|
|
||||||
|
time.sleep(0.2)
|
||||||
|
response = ""
|
||||||
|
start_wait = time.time()
|
||||||
|
while (time.time() - start_wait) < 1.5:
|
||||||
|
if self.ser.in_waiting > 0:
|
||||||
|
response += self.ser.readline().decode('utf-8', errors='ignore')
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
print(f"[RPI LA66 TX STATUS] :\n{response.strip()}")
|
||||||
|
|
||||||
|
def receive_packet(self, timeout_ms=5000):
|
||||||
|
with self.lock:
|
||||||
|
start_time = time.time()
|
||||||
|
timeout_s = timeout_ms / 1000.0
|
||||||
|
|
||||||
|
while (time.time() - start_time) < timeout_s:
|
||||||
|
if self.ser.in_waiting > 0:
|
||||||
|
line = self.ser.readline().decode('utf-8', errors='ignore').strip()
|
||||||
|
if line:
|
||||||
|
payload_bytes = None
|
||||||
|
|
||||||
|
if "(HEX:)" in line:
|
||||||
|
hex_part = line.split("(HEX:)")[1].strip().replace(" ", "")
|
||||||
|
try:
|
||||||
|
payload_bytes = bytes.fromhex(hex_part)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
elif "Data:" in line:
|
||||||
|
payload_bytes = line.split("Data:")[1].strip().encode('utf-8')
|
||||||
|
|
||||||
|
if payload_bytes and len(payload_bytes) > 1:
|
||||||
|
group = payload_bytes[0]
|
||||||
|
payload_clean = payload_bytes[1:].strip(b'\x00 \r\n\t')
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = payload_clean.decode('utf-8').strip('\x00 \r\n\t')
|
||||||
|
except UnicodeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if text.startswith('{') or text.startswith('['):
|
||||||
|
decoded_text = text
|
||||||
|
elif text.lower().startswith('7b') or text.lower().startswith('5b'):
|
||||||
|
try:
|
||||||
|
decoded_text = bytes.fromhex(text).decode('utf-8').strip('\x00 \r\n\t')
|
||||||
|
except Exception:
|
||||||
|
decoded_text = text
|
||||||
|
else:
|
||||||
|
decoded_text = text
|
||||||
|
|
||||||
|
try:
|
||||||
|
parsed_json = json.loads(decoded_text)
|
||||||
|
return {"group": group, "data": parsed_json, "raw": False}
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return {"group": group, "data": decoded_text, "raw": True}
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_lora_device(port_or_pins=None):
|
||||||
|
if IS_MICROPYTHON:
|
||||||
|
pins = port_or_pins if port_or_pins else {}
|
||||||
|
return LoraHardwareSPI(**pins)
|
||||||
|
else:
|
||||||
|
port = port_or_pins if port_or_pins else "/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0"
|
||||||
|
return LoraSerialAT(port)
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
"""Small CPython MQTT example.
|
||||||
|
|
||||||
|
Listens and prints all received messages on the subscribed channel continuously.
|
||||||
|
Set MQTT_BROKER_HOST to the broker IP address, not localhost.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from shared.mqtt import BrokerClient
|
||||||
|
|
||||||
|
|
||||||
|
BROKER_HOST = os.getenv("MQTT_BROKER_HOST", "192.168.50.1")
|
||||||
|
TOPIC = "smartwave/demo"
|
||||||
|
USE_TLS = os.getenv("MQTT_CLIENT_TLS", "true").lower() in {"1", "true", "yes", "on"}
|
||||||
|
CA_FILE = os.getenv(
|
||||||
|
"MQTT_CA_FILE",
|
||||||
|
str(Path(__file__).resolve().parents[2] / "orchestrateur" / "mqtt" / "certs" / "ca.crt"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
client = BrokerClient(
|
||||||
|
host=BROKER_HOST,
|
||||||
|
client_id="zephyrus",
|
||||||
|
use_tls=USE_TLS,
|
||||||
|
cafile=CA_FILE,
|
||||||
|
keepalive=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Connexion au broker: {BROKER_HOST}...")
|
||||||
|
client.connect()
|
||||||
|
|
||||||
|
print(f"Souscription au topic: {TOPIC}...")
|
||||||
|
client.subscribe(TOPIC, qos=2)
|
||||||
|
|
||||||
|
print("En attente de messages... (Appuyez sur Ctrl+C pour quitter)")
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# Traite le réseau et récupère les paquets MQTT entrants
|
||||||
|
client.poll(0.1)
|
||||||
|
|
||||||
|
# Récupère le message dans la file d'attente de la librairie
|
||||||
|
message = client.get_message()
|
||||||
|
if message is not None:
|
||||||
|
# Décodage du payload pour un affichage plus propre en texte
|
||||||
|
try:
|
||||||
|
payload_str = message['payload'].decode('utf-8')
|
||||||
|
except Exception:
|
||||||
|
payload_str = message['payload']
|
||||||
|
|
||||||
|
print(f"[{message['topic']}] reçu : {payload_str}")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nArrêt utilisateur")
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
print("Connexion MQTT fermée.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user