/* uLisp LoRa Extension - Version 1a - 2nd June 2025 See http://www.ulisp.com/show?582Q */ #include #include enum stream2 { LORASTREAM = USERSTREAMS }; // Streams in uLisp Extensions Files start at USERSTREAMS /* (lora-set-pins ss rst dio0) Optionally configures the pins used by the library. Must be called before lora-begin. ss is slave select, default 10. rst is reset, default 9. dio0 is interrupt, default 2. */ object *fn_LoraSetPins (object *args, object *env) { (void) env; LoRa.setPins(checkinteger(first(args)), checkinteger(second(args)), checkinteger(third(args))); return nil; } /* (lora-begin frequency) Starts LoRa on the specified frequency, in MHz; one of 433, 868, or 915. Returns nil on failure. */ object *fn_LoraBegin (object *args, object *env) { (void) env; object *frequency = first(args); if (LoRa.begin((long)1000000*checkinteger(frequency))) return frequency; return nil; } /* (lora-available) Returns the number of bytes available for reading. */ object *fn_LoraAvailable (object *args, object *env) { (void) args, (void) env; return number(LoRa.available()); } /* (lora-packet-rssi) Returns the RSSI of the last received packet. */ object *fn_LoraPacketRssi (object *args, object *env) { (void) args, (void) env; return number(LoRa.packetRssi()); } /* (lora-frequency-error) Returns the frequency error of the last received packet as a floating-point number in Hz. */ object *fn_LoraFrequencyError (object *args, object *env) { (void) args, (void) env; return makefloat(float(LoRa.packetFrequencyError())); } /* (lora-packet-snr) Returns the signal-to-noise ratio of the last packet as a floating-point number in dB. */ object *fn_LoraPacketSnr (object *args, object *env) { (void) args, (void) env; return makefloat(LoRa.packetSnr()); } /* (lora-set-tx-power power) Sets the transmit power of the radio to between 2 and 20 dB, default 17. */ object *fn_LoraSetTXPower (object *args, object *env) { (void) env; object *arg = first(args); int n = checkinteger(arg); if (n<2 || n>20) error("power must be between 2 and 20", arg); LoRa.setTxPower(n); return arg; } /* (lora-set-spreading-factor factor) Sets the spreading factor to between 6 and 12, default 7. */ object *fn_LoraSetSpreadingFactor (object *args, object *env) { (void) args, (void) env; object *arg = first(args); int n = checkinteger(arg); if (n<6 || n>12) error("spreading factor must be between 6 and 12", arg); LoRa.setSpreadingFactor(n); return arg; } /* (lora-set-signal-bandwidth) Sets the signal bandwidth in Hz. Supported values are 7800, 10400, 15600, 20800, 31250, 41700, 62500, 125000, 250000, and 500000, default 125000. */ object *fn_LoraSetSignalBandwidth (object *args, object *env) { (void) env; object *arg = first(args); int n = checkinteger(arg); if (n<7800 || n>500000) error("bandwidth must be between 7800 and 500000", arg); LoRa.setSignalBandwidth(n); return arg; } /* (lora-set-coding-rate rate) Sets the coding rate to 4/rate, where rate can be between 5 and 8, default 4. */ object *fn_LoraSetCodingRate (object *args, object *env) { (void) env; object *arg = first(args); int n = checkinteger(arg); if (n<5 || n>8) error("spreading factor must be between 5 and 8", arg); LoRa.setCodingRate4(n); return arg; } /* (lora-set-preamble-length length) Sets the preamble length of the radio in the range 6 to 65535, default 8. */ object *fn_LoraSetPreambleLength (object *args, object *env) { (void) args, (void) env; object *arg = first(args); int n = checkinteger(arg); if (n<6 || n>65535) error("preamble must be between 6 and 65535", arg); LoRa.setPreambleLength(checkinteger(arg)); return arg; } /* (lora-set-sync-word word) Sets the byte value used as the sync word, default #x12. */ object *fn_LoraSetSyncWord (object *args, object *env) { (void) env; object *arg = first(args); LoRa.setSyncWord(checkinteger(arg)); return arg; } /* (with-lora-output (str [implicit] [async])) Evaluates the forms with str bound to a lora-stream. If implicit is non-nil no header is provided. If async is non-nil the call returns immediately without waiting for the packet to be completed. */ object *sp_WithLoraOutput (object *args, object *env) { object *params = checkarguments(args, 1, 3); bool implicit = false, async = false; object *var = first(params); object *pair = cons(var, stream(LORASTREAM, 0)); push(pair,env); params = cdr(params); if (params != NULL) { if (first(params) != NULL) implicit = true; params = cdr(params); if (params != NULL && first(params) != NULL) async = true; } object *forms = cdr(args); LoRa.beginPacket(implicit); eval(tf_progn(forms,env), env); LoRa.endPacket(async); return nil; } /* (with-lora-input (str [size])) If a packet has been received evaluates the forms with str bound to a lora-stream and returns the size; otherwise returns nil. If size is specified and is > zero implicit header mode is enabled with an expected packet of size bytes. */ object *sp_WithLoraInput (object *args, object *env) { object *params = checkarguments(args, 1, 2); object *var = first(params); object *pair = cons(var, stream(LORASTREAM, 0)); int size = 0; if (cdr(params) != NULL && second(params) != NULL) size = checkinteger(second(params)); size = LoRa.parsePacket(size); if (size == 0) return nil; push(pair,env); object *forms = cdr(args); eval(tf_progn(forms,env), env); return number(size); } // Symbol names const char stringLoraSetPins[] = "lora-set-pins"; const char stringLoraBegin[] = "lora-begin"; const char stringLoraAvailable[] = "lora-available"; const char stringLoraPacketRssi[] = "lora-packet-rssi"; const char stringLoraPacketSnr[] = "lora-packet-snr"; const char stringLoraFrequencyError[] = "lora-frequency-error"; const char stringWithLoraOutput[] = "with-lora-output"; const char stringWithLoraInput[] = "with-lora-input"; const char stringLoraSetTXPower[] = "lora-set-tx-power"; const char stringLoraSetSpreadingFactor[] = "lora-set-spreading-factor"; const char stringLoraSetSignalBandwidth[] = "lora-set-signal-bandwidth"; const char stringLoraSetCodingRate[] = "lora-set-coding-rate"; const char stringLoraSetPreambleLength[] = "lora-set-preamble-length"; const char stringLoraSetSyncWord[] = "lora-set-sync-word"; // Documentation strings const char docLoraSetPins[] = "(lora-set-pins ss rst dio0)\n" "Configures the pins used by the library. Must be called before lora-begin.\n" "ss is slave select, default 10. rst is reset, default 9. dio0 is interrupt, default 2."; const char docLoraBegin[] = "(lora-begin frequency)\n" "Configures LoRa on the specified frequency, in MHz; one of 433, 868, or 915.\n" "Returns nil on failure."; const char docLoraAvailable[] = "(lora-available)\n" "Returns the number of bytes available for reading."; const char docLoraPacketRssi[] = "(lora-packet-rssi)\n" "Returns the RSSI of the last packet."; const char docLoraPacketSnr[] = "(lora-packet-snr)\n" "Returns the signal-to-noise ratio of the last packet as a floating-point number in dB."; const char docLoraFrequencyError[] = "(lora-frequency-error)\n" "Returns the frequency error of the last received packet as a floating-point number in Hz."; const char docLoraSetTXPower[] = "(lora-set-tx-power power)\n" "Sets the transmit power of the radio to between 2 and 20 dB, default 17."; const char docLoraSetSpreadingFactor[] = "(lora-set-spreading-factor factor)\n" "Sets the spreading factor to between 6 and 12, default 7."; const char docLoraSetSignalBandwidth[] = "(lora-set-signal-bandwidth)\n" "Sets the signal bandwidth in Hz. Supported values are 7800, 10400, 15600, 20800,\n" "31250, 41700, 62500, 125000, 250000, and 500000, default 125000."; const char docLoraSetCodingRate[] = "(lora-set-coding-rate rate)\n" "Sets the coding rate to 4/rate, where rate can be between 5 and 8, default 4."; const char docLoraSetPreambleLength[] = "(lora-set-preamble-length length)\n" "Sets the preamble length of the radio in the range 6 to 65535, default 8."; const char docLoraSetSyncWord[] = "(lora-set-sync-word word)\n" "Sets the byte value used as the sync word, default #x12."; const char docWithLoraOutput[] = "(with-lora-output (str [implicit] [async]))\n" "Evaluates the forms with str bound to a lora-stream. If implicit is non-nil no header is provided.\n" "If async is non-nil the call returns immediately without waiting for the packet to be completed."; const char docWithLoraInput[] = "(with-lora-input (str [size]))\n" "If a packet has been received evaluates the forms with str bound to a lora-stream and returns the size; otherwise returns nil.\n" "If size is specified and is > zero implicit header mode is enabled with an expected packet of size bytes."; // Symbol lookup table const tbl_entry_t lookup_table2[] = { { stringLoraSetPins, fn_LoraSetPins, 0233, docLoraSetPins }, { stringLoraBegin, fn_LoraBegin, 0211, docLoraBegin }, { stringLoraAvailable, fn_LoraAvailable, 0200, docLoraAvailable }, { stringLoraPacketRssi, fn_LoraPacketRssi, 0200, docLoraPacketRssi }, { stringLoraPacketSnr, fn_LoraPacketSnr, 0200, docLoraPacketSnr }, { stringLoraFrequencyError, fn_LoraFrequencyError, 0200, docLoraFrequencyError }, { stringWithLoraOutput, sp_WithLoraOutput, 0317, docWithLoraOutput }, { stringWithLoraInput, sp_WithLoraInput, 0317, docWithLoraInput }, { stringLoraSetTXPower, fn_LoraSetTXPower, 0211, NULL }, { stringLoraSetSpreadingFactor, fn_LoraSetSpreadingFactor, 0211, NULL }, { stringLoraSetSignalBandwidth, fn_LoraSetSignalBandwidth, 0211, NULL }, { stringLoraSetCodingRate, fn_LoraSetCodingRate, 0211, NULL }, { stringLoraSetPreambleLength, fn_LoraSetPreambleLength, 0211, NULL }, { stringLoraSetSyncWord, fn_LoraSetSyncWord, 0211, NULL } }; // Stream writing functions void lorawrite (char c) { LoRa.write(c); } pfun_t pfun_lora (uint8_t address) { return lorawrite; } // Stream reading functions int loraread () { if (LastChar) { char temp = LastChar; LastChar = 0; return temp; } while (!LoRa.available()) testescape(); return LoRa.read(); } gfun_t gfun_lora (uint8_t address) { return loraread; } // Stream names used const char lorastreamname[] = "lora"; // Stream lookup table - needs to be in same order as enum stream2 const stream_entry_t stream_table2[] = { { lorastreamname, pfun_lora, gfun_lora }, }; // Stream table cross-reference functions - do not edit below this line stream_entry_t *streamtables[] = {stream_table, stream_table2}; const stream_entry_t *streamtable (int n) { return streamtables[n]; } // Table cross-reference functions - do not edit below this line tbl_entry_t *tables[] = {lookup_table, lookup_table2}; const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) }; const tbl_entry_t *table (int n) { return tables[n]; } unsigned int tablesize (int n) { return tablesizes[n]; }