diff --git a/esp32/Makefile b/esp32/Makefile index fefaced23..8a43e3429 100644 --- a/esp32/Makefile +++ b/esp32/Makefile @@ -92,6 +92,18 @@ INC += -I../../components/ed25519/include INC += -I../../components/redundancy INC += -I../../components/badge-first-run INC += -I../../components/png +INC += -I../../components/audio_pipeline/include +INC += -I../../components/audio_hal/board +INC += -I../../components/audio_sal/include +INC += -I../../components/audio_stream/include +INC += -I../../components/esp_http_client/include +INC += -I../../components/esp_http_client/lib/include +INC += -I../../components/esp_peripherals/include +INC += -I../../components/esp_peripherals/lib/blufi +INC += -I../../components/tcp_transport/include +INC += -I../../components/esp-adf-libs/esp_codec/include/codec +INC += -I../../components/esp-adf-libs/esp_codec/include/processing +INC += -I../../components/esp-tls INC += -I../../main INC += -I../../ugfx/src/gdisp/mcufont INC += -I../../ugfx @@ -190,6 +202,7 @@ SRC_C = \ modsocket.c \ modesp.c \ modbpp.c \ + modaudio.c \ modbadge.c \ modugfx.c \ modfreedomgfx.c \ @@ -323,6 +336,8 @@ ESPIDF_ESP32_O = $(addprefix $(ESPCOMP)/esp32/,\ ets_timer_legacy.o \ esp_err_to_name.o \ dbg_stubs.o \ + spiram.o \ + spiram_psram.o \ ) ESPIDF_PTHREAD_O = $(addprefix $(ESPCOMP)/pthread/,\ @@ -682,6 +697,28 @@ ESPIDF_WPA_SUPPLICANT_O = $(addprefix $(ESPCOMP)/wpa_supplicant/,\ port/os_xtensa.o \ ) +ESPADF_O = $(addprefix $(PROJECT_PATH)/components/,\ + audio_pipeline/audio_pipeline.o \ + audio_pipeline/audio_element.o \ + audio_pipeline/audio_event_iface.o \ + audio_pipeline/ringbuf.o \ + audio_sal/audio_mem.o \ + audio_stream/fatfs_stream.o \ + audio_stream/http_stream.o \ + audio_stream/i2s_stream.o \ + esp_http_client/esp_http_client.o \ + esp_http_client/lib/http_header.o \ + esp_http_client/lib/http_auth.o \ + esp_http_client/lib/http_utils.o \ + tcp_transport/transport.o \ + tcp_transport/transport_ssl.o \ + tcp_transport/transport_tcp.o \ + tcp_transport/transport_utils.o \ + esp-tls/esp_tls.o \ + esp_peripherals/periph_wifi.o \ + esp_peripherals/esp_peripherals.o \ + ) + OBJ_ESPIDF = OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NEWLIB_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_DRIVER_O)) @@ -712,6 +749,7 @@ OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_WPA_SUPPLICANT_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_APP_UPDATE_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_BOOTLOADER_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SDMMC_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPADF_O)) #################### # Badge magic @@ -721,6 +759,7 @@ BADGE_COMPONENTS_O = $(addprefix $(PROJECT_PATH)/components/,\ badge/badge_base.o \ badge/badge_eink.o \ badge/badge_i2c.o \ + badge/badge_spi.o \ badge/badge_leds.o \ badge/badge_mpr121.o \ badge/badge_disobey_samd.o \ @@ -779,9 +818,9 @@ $(BUILD)/firmware.bin: $(BUILD)/bootloader.bin $(BUILD)/partitions.bin $(BUILD)/ $(ECHO) "Create $@" $(Q)$(PYTHON) makeimg.py $^ $@ -deploy: $(BUILD)/firmware.bin - $(ECHO) "Writing $^ to the board" - $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) write_flash -z --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) 0x1000 $^ +deploy: $(BUILD)/application.bin + $(ECHO) "Writing full firmware to the board" + $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) 0x1000 ../../build/bootloader/bootloader.bin 0x8000 ../../build/partitions-16MB.bin 0x10000 build/application.bin flash: $(BUILD)/firmware.bin $(ECHO) "Writing $^ to the board" @@ -819,6 +858,7 @@ APP_LD_ARGS += -lgcov APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libc.a APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libm.a APP_LD_ARGS += $(ESPCOMP)/esp32/libhal.a +APP_LD_ARGS += $(PROJECT_PATH)/components/esp-adf-libs/esp_codec/lib/libesp_codec.a APP_LD_ARGS += $(BADGE_LIBS) APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lnet80211 -lmesh -lphy -lrtc -lpp -lwpa -lwpa2 -lwps -lsmartconfig -lcoexist APP_LD_ARGS += $(OBJ) diff --git a/esp32/modaudio.c b/esp32/modaudio.c new file mode 100644 index 000000000..b4f81f087 --- /dev/null +++ b/esp32/modaudio.c @@ -0,0 +1,386 @@ +#include + +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "esp_log.h" +#include "audio_element.h" +#include "audio_pipeline.h" +#include "audio_event_iface.h" +#include "audio_common.h" +#include "fatfs_stream.h" +#include "http_stream.h" +#include "i2s_stream.h" +#include "mp3_decoder.h" +#include "badge_pins.h" +#include "badge_power.h" + +#include "modaudio.h" + +#ifdef IIS_SCLK + +#define TAG "esp32/modaudio" + +STATIC mp_obj_t audio_volume(mp_uint_t n_args, const mp_obj_t *args) { + if (n_args > 0){ + int v = mp_obj_get_int(args[0]); + if (v < 0) v = 0; + else if (v > 128) v = 128; + i2s_stream_volume = v; + } + + return mp_obj_new_int(i2s_stream_volume); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audio_volume_obj, 0, 1, audio_volume); + +/* current active stream */ +static bool audio_stream_active = false; +static audio_event_iface_handle_t evt; +static audio_pipeline_handle_t pipeline = NULL; +static audio_element_handle_t fatfs_stream_reader = NULL; +static audio_element_handle_t http_stream_reader = NULL; +static audio_element_handle_t wav_decoder = NULL; +static audio_element_handle_t mp3_decoder = NULL; +static audio_element_handle_t i2s_stream_writer = NULL; + +static void +_modaudio_stream_cleanup(void) +{ + ESP_LOGI(TAG, "[ 6 ] Stop audio_pipeline"); + audio_pipeline_terminate(pipeline); + + /* Terminate the pipeline before removing the listener */ + if (fatfs_stream_reader != NULL) { + audio_pipeline_unregister(pipeline, fatfs_stream_reader); + } else if (http_stream_reader != NULL) { + audio_pipeline_unregister(pipeline, http_stream_reader); + } + + audio_pipeline_unregister(pipeline, i2s_stream_writer); + + if (wav_decoder != NULL) { + audio_pipeline_unregister(pipeline, wav_decoder); + } else if (mp3_decoder != NULL) { + audio_pipeline_unregister(pipeline, mp3_decoder); + } + + audio_pipeline_remove_listener(pipeline); + + /* Make sure audio_pipeline_remove_listener & audio_event_iface_remove_listener are called before destroying event_iface */ + audio_event_iface_destroy(evt); + evt = NULL; + + /* Release all resources */ + audio_pipeline_deinit(pipeline); + pipeline = NULL; + + if (fatfs_stream_reader != NULL) { + audio_element_deinit(fatfs_stream_reader); + fatfs_stream_reader = NULL; + } else if (http_stream_reader != NULL) { + audio_element_deinit(http_stream_reader); + http_stream_reader = NULL; + } + + audio_element_deinit(i2s_stream_writer); + i2s_stream_writer = NULL; + + if (wav_decoder != NULL) { + audio_element_deinit(wav_decoder); + wav_decoder = NULL; + } else if (mp3_decoder != NULL) { + audio_element_deinit(mp3_decoder); + mp3_decoder = NULL; + } + + audio_stream_active = false; +} + +static void +_modaudio_event_listener_task(void *arg) +{ + while (1) { + audio_event_iface_msg_t msg; + esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret); + continue; + } + + if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT + && msg.source == (void *) mp3_decoder + && msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) { + audio_element_info_t music_info = {0}; + audio_element_getinfo(mp3_decoder, &music_info); + + ESP_LOGI(TAG, "[ * ] Receive music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d", + music_info.sample_rates, music_info.bits, music_info.channels); + + audio_element_setinfo(i2s_stream_writer, &music_info); + i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels); + continue; + } + + /* Stop when the last pipeline element (i2s_stream_writer in this case) receives stop event */ + if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_writer + && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (int) msg.data == AEL_STATUS_STATE_STOPPED) { + ESP_LOGW(TAG, "[ * ] Stop event received"); + break; + } + } + + _modaudio_stream_cleanup(); + + vTaskDelete(NULL); +} + +static void _init_event(void) { + static const audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG(); + evt = audio_event_iface_init(&evt_cfg); + assert( evt != NULL ); +} + +static void _init_pipeline(void) { + static const audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG(); + pipeline = audio_pipeline_init(&pipeline_cfg); + assert( pipeline != NULL ); +} + +static void _init_fatfs_stream(void) { + static fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT(); + fatfs_cfg.type = AUDIO_STREAM_READER; // FIXME: to make const structure + fatfs_stream_reader = fatfs_stream_init(&fatfs_cfg); + assert( fatfs_stream_reader != NULL ); +} + +static void _init_http_stream(void) { + http_stream_cfg_t http_cfg = HTTP_STREAM_CFG_DEFAULT(); + http_stream_reader = http_stream_init(&http_cfg); + assert( http_stream_reader != NULL ); +} + +static void _init_mp3_decoder(void) { + static const mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG(); + mp3_decoder = mp3_decoder_init(&mp3_cfg); + assert( mp3_decoder != NULL ); +} + +static void _init_i2s_stream(void) { + static const i2s_stream_cfg_t i2s_cfg = { + .type = AUDIO_STREAM_WRITER, + .task_prio = I2S_STREAM_TASK_PRIO, + .task_core = I2S_STREAM_TASK_CORE, + .task_stack = I2S_STREAM_TASK_STACK, + .out_rb_size = I2S_STREAM_RINGBUFFER_SIZE, + .i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // write only + .sample_rate = 44100, // set to 48000 ? + .bits_per_sample = 16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_I2S, + .dma_buf_count = 3, + .dma_buf_len = 300, + .use_apll = 0, // real sample-rate is too far off when enabled + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, + }, + .i2s_pin_config = { + .bck_io_num = IIS_SCLK, + .ws_io_num = IIS_LCLK, + .data_out_num = IIS_DSIN, + .data_in_num = IIS_DOUT, + }, + .i2s_port = 0, + }; + + i2s_stream_writer = i2s_stream_init(&i2s_cfg); + assert( i2s_stream_writer != NULL ); +} + +STATIC mp_obj_t audio_play_mp3_file(mp_obj_t _file) { + const char *file = mp_obj_str_get_str(_file); + + if (audio_stream_active) { + ESP_LOGE(TAG, "another audio stream is already playing"); + return mp_const_none; + } + + audio_stream_active = true; + +#ifdef AUDIO_NEEDS_EXT_POWER + badge_power_sdcard_enable(); +#endif // AUDIO_NEEDS_EXT_POWER + + _init_pipeline(); + _init_fatfs_stream(); + _init_i2s_stream(); + _init_mp3_decoder(); + + // configure pipeline + audio_pipeline_register(pipeline, fatfs_stream_reader,"file"); + audio_pipeline_register(pipeline, mp3_decoder, "mp3"); + audio_pipeline_register(pipeline, i2s_stream_writer, "i2s"); + + audio_pipeline_link(pipeline, (const char *[]) {"file", "mp3", "i2s"}, 3); + + // start stream + if (*file == 0) { // empty string; keep as hack to test audio + audio_element_set_uri(fatfs_stream_reader, "/sdcard/audio/ff-16b-2c-44100hz.mp3"); + } else { + audio_element_set_uri(fatfs_stream_reader, file); + } + + _init_event(); + + audio_pipeline_set_listener(pipeline, evt); + + audio_pipeline_run(pipeline); + + while (1) { + audio_event_iface_msg_t msg; + esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret); + continue; + } + + if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT + && msg.source == (void *) mp3_decoder + && msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) { + audio_element_info_t music_info = {0}; + audio_element_getinfo(mp3_decoder, &music_info); + + ESP_LOGI(TAG, "[ * ] Receive music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d", + music_info.sample_rates, music_info.bits, music_info.channels); + + audio_element_setinfo(i2s_stream_writer, &music_info); + i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels); + + // stream is playing; return + xTaskCreate(&_modaudio_event_listener_task, "modaudio event-listener task", 4096, NULL, 10, NULL); + return mp_const_none; + } + + /* Stop when the last pipeline element (i2s_stream_writer in this case) receives stop event */ + if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_writer + && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (int) msg.data == AEL_STATUS_STATE_STOPPED) { + ESP_LOGW(TAG, "[ * ] Stop event received"); + break; + } + } + + _modaudio_stream_cleanup(); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audio_play_mp3_file_obj, audio_play_mp3_file); + +STATIC mp_obj_t audio_play_mp3_stream(mp_obj_t _url) { + const char *url = mp_obj_str_get_str(_url); + + if (audio_stream_active) { + ESP_LOGE(TAG, "another audio stream is already playing"); + return mp_const_none; + } + + audio_stream_active = true; + +#ifdef AUDIO_NEEDS_EXT_POWER + badge_power_sdcard_enable(); +#endif // AUDIO_NEEDS_EXT_POWER + + _init_pipeline(); + _init_http_stream(); + _init_i2s_stream(); + _init_mp3_decoder(); + + // configure pipeline + audio_pipeline_register(pipeline, http_stream_reader, "http"); + audio_pipeline_register(pipeline, mp3_decoder, "mp3"); + audio_pipeline_register(pipeline, i2s_stream_writer, "i2s"); + + audio_pipeline_link(pipeline, (const char *[]) {"http", "mp3", "i2s"}, 3); + + // start stream + if (*url == 0) { // empty string; keep as hack to test audio + audio_element_set_uri(http_stream_reader, "https://dl.espressif.com/dl/audio/ff-16b-2c-44100hz.mp3"); + } else { + audio_element_set_uri(http_stream_reader, url); + } + + _init_event(); + + audio_pipeline_set_listener(pipeline, evt); + + audio_pipeline_run(pipeline); + + while (1) { + audio_event_iface_msg_t msg; + esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret); + continue; + } + + if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT + && msg.source == (void *) mp3_decoder + && msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) { + audio_element_info_t music_info = {0}; + audio_element_getinfo(mp3_decoder, &music_info); + + ESP_LOGI(TAG, "[ * ] Receive music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d", + music_info.sample_rates, music_info.bits, music_info.channels); + + audio_element_setinfo(i2s_stream_writer, &music_info); + i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels); + + // stream is playing; return + xTaskCreate(&_modaudio_event_listener_task, "modaudio event-listener task", 4096, NULL, 10, NULL); + return mp_const_none; + } + + /* Stop when the last pipeline element (i2s_stream_writer in this case) receives stop event */ + if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_writer + && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (int) msg.data == AEL_STATUS_STATE_STOPPED) { + ESP_LOGW(TAG, "[ * ] Stop event received"); + break; + } + } + + _modaudio_stream_cleanup(); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audio_play_mp3_stream_obj, audio_play_mp3_stream); + +STATIC mp_obj_t audio_stop(void) { + if (pipeline != NULL) { + audio_pipeline_stop(pipeline); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(audio_stop_obj, audio_stop); + +#endif // IIS_SCLK + + +// Module globals +STATIC const mp_rom_map_elem_t audio_module_globals_table[] = { + {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audio)}, + +#ifdef IIS_SCLK + {MP_OBJ_NEW_QSTR(MP_QSTR_volume), (mp_obj_t)&audio_volume_obj}, + + {MP_OBJ_NEW_QSTR(MP_QSTR_play_mp3_file), (mp_obj_t)&audio_play_mp3_file_obj}, + {MP_OBJ_NEW_QSTR(MP_QSTR_play_mp3_stream), (mp_obj_t)&audio_play_mp3_stream_obj}, + + {MP_OBJ_NEW_QSTR(MP_QSTR_stop), (mp_obj_t)&audio_stop_obj}, +#endif // IIS_SCLK +}; + +STATIC MP_DEFINE_CONST_DICT(audio_module_globals, audio_module_globals_table); + +const mp_obj_module_t audio_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&audio_module_globals, +}; diff --git a/esp32/modaudio.h b/esp32/modaudio.h new file mode 100644 index 000000000..1a702c776 --- /dev/null +++ b/esp32/modaudio.h @@ -0,0 +1,3 @@ +#include +#include + diff --git a/esp32/modules_disobey2019/ir.py b/esp32/modules_disobey2019/ir.py index d6ead382b..34bf0eccd 100644 --- a/esp32/modules_disobey2019/ir.py +++ b/esp32/modules_disobey2019/ir.py @@ -1,84 +1,164 @@ -from machine import Pin, PWM -from time import sleep_ms +import gc +import machine +import time -optics_sleep = 50 +# Basics for receiving: +# * Startup by enabling the IR receiver to eat power +# * Enable an interrupt (hardware) for receiving the initial pulse +# * When the interrupt is fired disable the rest of the interrups for the duration of this read +# * Read the pulses with time_pulse_us (it's extremely fast) +# * Store this in a buffer with a fixed size, (256 empty arrays) +# * Set a timer to decode the data instantly after interrupts are enabled again. +# * a class can inherit this class and just define a decoder and specify a send pattern +# * if a send pattern is not the way to go (some totally different protocols) you can just implement another tx function -pin_rx_enable = Pin(21, Pin.OUT) -pin_rx = Pin(18, Pin.IN) -pin_tx = Pin(19, Pin.OUT) -pwm_tx = PWM(pin_tx, freq=38000, duty=0) +class BadgeIr(): + freq = 38000 + ticks = 500 + start = 4000 + rxpin = 18 + txpin = 19 + rxenablepin = 21 + rxtimer = None + readcallback = None + bitform = [] + pwm_tx = None -# Functions for switching power to the receiver on and off + def initbuffer(self): + self.buffer=[[]] * 256 + self.bufpos = 0 + # If it returns 1 the buffer gets emptied out and initialized. + # This is the default, protocol implementations should choose if they want to. + def decoder(self): + return 1 + def real_decoder(self,timer): + self.rxtimer.deinit() + self.rxtimer = None + if self.decoder(): + self.initbuffer() + def cleanbuffer(self,i): + self.buffer=self.buffer[i:256]+[[]]*i + self.bufpos-=i + gc.collect() + def mr(self): + if self.bufpos == len(self.buffer): + return 0 + waitfor=0 if self.pin_rx.value() else 1 + while True: + t=machine.time_pulse_us(self.pin_rx,waitfor,50*1000) + if t<0: + if t == -2: + return 1 + return 0 + elif t>0: + waitfor = 0 if waitfor else 1 + self.buffer[self.bufpos]=[waitfor,round(t/self.ticks)] + self.bufpos+=1 + if self.bufpos == len(self.buffer): + return 1 + def callback(self,pin): + irqs = machine.disable_irq() + hasdata = self.mr() + machine.enable_irq(irqs) + if hasdata and not self.rxtimer: + self.rxtimer = machine.Timer(1) + self.rxtimer.init(mode=machine.Timer.ONE_SHOT, period=1,callback=self.real_decoder) + def rx_enable(self): + self.pin_rx_enable = machine.Pin(self.rxenablepin, machine.Pin.OUT) + self.pin_rx = machine.Pin(self.rxpin, machine.Pin.IN) + self.initbuffer() + self.pin_rx_enable.value(True) + self.pin_rx.irq(trigger=machine.Pin.IRQ_FALLING, handler=self.callback) + def rx_disable(self): + self.pin_rx.irq(trigger=0, handler=self.callback) + self.pin_rx_enable.value(False) + def tx_enable(self): + if not self.pwm_tx: + self.pin_tx = machine.Pin(self.txpin, machine.Pin.OUT) + self.pwm_tx = machine.PWM(self.pin_tx, freq = self.freq, duty = 0) + def tx_disable(self): + self.pwm_tx.duty(0) + def tx_setduty(self,duty): + self.pwm_tx.duty(512 * duty) + def txBit(self,bit): + for (onoff,tijd) in self.bitform[bit]: + self.tx_setduty(onoff) + time.sleep_us(tijd) + def txByte(self,byte): + for bit in range(8): + self.txBit( ( byte >> ( 7 - bit ) ) & 1 ) # MSB -def enableRx(state=True): - pin_rx_enable.value(state) - -def disableRx(): - pin_rx_enable.value(False) +class NecIR(BadgeIr): + # Implements NEC Infrared + # Example: + # IR=NecIR() + # NecIR.command= + # NecIR.repeat= + # NecIR.rx_enable() + # To stop receiving: + # NecIR.rx_disable() + # To send: + # NecIR.tx(,) + # NecIR.tx_repeat() + command = None + repeat = None + bitform = { 0: [[1,562],[0,562]], 1: [[1,562],[0,1687]], 's': [[1,9000],[0,4500]], 'e': [[1,562],[0,100]], 'r': [[1,9000],[0,2500],[1,562],[0,100]] } -# Functions for directly talking to the receiver and LED + def tx(self,addr,cmd): + self.tx_enable() + self.txBit('s') + self.txByte(addr) + self.txByte(addr ^ 0xFF) + self.txByte(cmd) + self.txByte(cmd ^ 0xFF) + self.txBit('e') + self.tx_disable() -def rawTx(p): - pwm_tx.duty(512*p) - -def rawRx(): - return not pin_rx.value() + def tx_repeat(self): + self.txBit('r') -# Helper functions for the (proof of concept) text transmission feature - -def _txByte(byte): - for bit in range(8): - rawTx((byte>>(7-bit))&1) - sleep_ms(optics_sleep) - -buf = 0 - -def _rxBit(): - global buf - buf = ((buf<<1) + rawRx()*1)&0xFFFF - sleep_ms(optics_sleep) - -def _rxByte(): - global buf - for i in range(8): - _rxBit() - return buf & 0xFF - -def _rxWait(timeout=100): - global buf - cnt = 0 - while True: - _rxBit() - if buf&0xFFFF == 0b1110101010101011: - buf = 0 - return True - #rxDebug() - cnt+=1 - if cnt > timeout: - return False - -# Functions for text transmission (proof of concept) - -def tx(data): - rawTx(False) - sleep_ms(optics_sleep*8) - _txByte(0b11101010) - _txByte(0b10101011) - for char in data: - txByte(ord(char)) - rawTx(False) - -def rx(timeout=100): - pin_rx_enable.value(True) - global buf - string = "" - buf = 0 - if not _rxWait(timeout): - pin_rx_enable.value(False) - return None - while True: - b = _rxByte() - if b < 1: - pin_rx_enable.value(False) - return string - string += chr(b) + def decoder(self): + decoded=0 + i=0 + while True and self.bufpos-i>0: + (val,time)=self.buffer[i] + i+=1 + if val==0 and time==9: + if self.bufpos<66: return(0) # Not yet complete.... + p1=None + p2=None + bits=0 + while True and self.bufpos-i>0: + (val,time)=self.buffer[i] + i+=1 + if time>0: + if p1==None: + p1=(val,time) + if bits==32 and p1[1]==1: + self.cleanbuffer(i) + if (decoded >> 24 & 0xFF) == (0xFF ^ (decoded >> 16 & 0xFF)) and (decoded >> 8 & 0xFF) == (0xFF ^ (decoded >> 0 & 0xFF)) and self.command: + self.command(decoded >> 24 & 0xFF,decoded >> 8 & 0xFF) + return(0) + else: + p2=(val,time) + if p1[1]==1 and p2[1]==3: + decoded=decoded<<1 | 1 + bits+=1 + elif p1[1]==1 and p2[1]==1: + decoded=decoded<<1 + bits+=1 + if bits==32 and p2==None: + self.cleanbuffer(i) + return(0) + p1=None + p2=None + elif time<0: + self.cleanbuffer(i) + return(0) + elif val==1 and time==18: + if self.buffer[i] == [0,4] and self.buffer[i+1] == [1,1]: + i+=2 + if self.repeat: self.repeat() + return(0) + self.cleanbuffer(i) + return(0) diff --git a/esp32/mpconfigport.h b/esp32/mpconfigport.h index c6a4c221a..e41776b64 100644 --- a/esp32/mpconfigport.h +++ b/esp32/mpconfigport.h @@ -175,6 +175,7 @@ extern const struct _mp_obj_module_t uos_module; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_network; +extern const struct _mp_obj_module_t audio_module; extern const struct _mp_obj_module_t badge_module; extern const struct _mp_obj_module_t bpp_module; extern const struct _mp_obj_module_t ugfx_module; @@ -188,6 +189,7 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_audio), (mp_obj_t)&audio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_badge), (mp_obj_t)&badge_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_bpp), (mp_obj_t)&bpp_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_ugfx), (mp_obj_t)&ugfx_module }, \