// create a simple messageing // with the RH_CC110 class. RH_CC110 class does not provide for addressing or // reliability, so you should only use RH_CC110 if you do not need the higher // level messaging abilities. #include #define CONFVER "conf00" #include "config.h" #include #include "EEPROMAnything.h" #include #include #include // Singleton instance of the radio driver RH_CC110 cc110; // GFSK_Rb1_2Fd5_2 = 0, ///< GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity // GFSK_Rb2_4Fd5_2, ///< GFSK, Data Rate: 2.4kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity // GFSK_Rb4_8Fd25_4, ///< GFSK, Data Rate: 4.8kBaud, Dev: 25.4kHz, RX BW 100kHz, optimised for sensitivity // GFSK_Rb10Fd19, ///< GFSK, Data Rate: 10kBaud, Dev: 19kHz, RX BW 100kHz, optimised for sensitivity // GFSK_Rb38_4Fd20, ///< GFSK, Data Rate: 38.4kBaud, Dev: 20kHz, RX BW 100kHz, optimised for sensitivity // GFSK_Rb76_8Fd32, ///< GFSK, Data Rate: 76.8kBaud, Dev: 32kHz, RX BW 232kHz, optimised for sensitivity // GFSK_Rb100Fd47, ///< GFSK, Data Rate: 100kBaud, Dev: 47kHz, RX BW 325kHz, optimised for sensitivity // GFSK_Rb250Fd127, ///< GFSK, Data Rate: 250kBaud, Dev: 127kHz, RX BW 540kHz, optimised for sensitivity // include the aJSON library #include // include the JsonRPC library #include // initialize an instance of the JsonRPC library for registering // exactly 3 method #ifdef CLIENT JsonRPC rpcclient(5,false); //serial port #endif #ifdef SERVER JsonRPC rpcserver(3,true ); //radio port with compact protocoll #endif #ifdef CLIENT // initialize a serial json stream for receiving json objects // through a serial/USB connection aJsonStream stream(&Serial); aJsonObject *serialmsg = NULL; #endif #ifdef SERVER aJsonObject *radiomsg = NULL; #endif char confver[7] = CONFVER; // version of configuration saved on eeprom struct config_t // configuration to save and load fron eeprom { int did; // sample time for mqtt (seconds) void save () { int p=0; // save to eeprom p+=EEPROM_writeAnything(p, confver); p+=EEPROM_writeAnything(p, did); } bool load () { // load from eeprom int p=0; char ver[7]; p+=EEPROM_readAnything(p, ver); if (strcmp(ver,confver ) == 0){ p+=EEPROM_readAnything(p, did); return true; } else{ return false; } } } configuration; //------------- const uint8_t pins [] = {PINS}; //------------- void Reboot() { IF_SDEBUG(DBGSERIAL.println(F("#Reboot"))); wdt_enable(WDTO_30MS); while(1) {} } #ifdef CLIENT int client(aJsonObject* params) { uint8_t status=0; aJsonObject *newrpc=NULL ; newrpc = aJson.createObject(); //aJson.addStringToObject(newrpc, "m", method); #ifdef TWOWAY aJsonObject* id = aJson.getObjectItem(serialmsg, "id"); if(id){ aJson.addNumberToObject(newrpc, "i",id -> valueint ); } #endif aJsonObject* mymethod = aJson.detachItemFromObject(serialmsg, "method"); aJson.addItemToObject(newrpc, "m",mymethod ); aJsonObject* myparams = aJson.detachItemFromObject(serialmsg, "params"); aJson.addItemToObject(newrpc, "p",myparams ); char buf[RH_CC110_MAX_MESSAGE_LEN]; aJson.print(newrpc,buf, sizeof(buf)); aJson.deleteItem(newrpc); IF_SDEBUG(DBGSERIAL.print(F("#send: "))); IF_SDEBUG(DBGSERIAL.println(buf)); cc110.send((uint8_t*)buf, strlen(buf)); cc110.waitPacketSent(); #ifdef TWOWAY // Now wait for a reply uint8_t len = sizeof(buf); if (cc110.waitAvailableTimeout(3000)) { // Should be a reply message for us now if (cc110.recv((uint8_t*)buf, &len)) { IF_SDEBUG(DBGSERIAL.print(F("#got reply: "))); IF_SDEBUG(DBGSERIAL.println((char*)buf)); IF_SDEBUG(DBGSERIAL.print(F("#RSSI: "))); IF_SDEBUG(DBGSERIAL.println(cc110.lastRssi(), DEC)); #endif //IF_SDEBUG(DBGSERIAL.println("{\"jsonrpc\": \"2.0\", \"result\":true, \"id\": 0}")); aJson.addTrueToObject(serialmsg, "result"); #ifdef TWOWAY } else { IF_SDEBUG(DBGSERIAL.println(F("#recv failed"))); aJson.addFalseToObject(serialmsg, "result"); status = 1; } } else { IF_SDEBUG(DBGSERIAL.println(F("#No reply, is cc110_server running?"))); aJson.addFalseToObject(serialmsg, "result"); status = 1; } #endif char serialbuf[SERIALBUFFERSIZE]; aJson.print(serialmsg,serialbuf, sizeof(serialbuf)); Serial.println(serialbuf); return status; } #endif #ifdef CLIENT int setdid(aJsonObject* params) { uint8_t status=1; aJson.deleteItemFromObject(serialmsg, "method"); aJsonObject* myparams = aJson.detachItemFromObject(serialmsg, "params"); aJsonObject* didParam = aJson.getObjectItem(myparams, "d"); if (didParam){ int did = didParam -> valueint; configuration.did=did; aJson.addTrueToObject(serialmsg, "result"); char buf[SERIALBUFFERSIZE]; aJson.print(serialmsg,buf, sizeof(buf)); Serial.println(buf); status= 0; } aJson.deleteItem(params); return status; } int save(aJsonObject* params) { uint8_t status=1; aJson.deleteItemFromObject(serialmsg, "method"); aJsonObject* myparams = aJson.detachItemFromObject(serialmsg, "params"); aJsonObject* saveParam = aJson.getObjectItem(myparams, "eeprom"); if (saveParam){ bool eeprom = saveParam -> valuebool; if (eeprom) configuration.save(); aJson.addTrueToObject(serialmsg, "result"); char buf[SERIALBUFFERSIZE]; aJson.print(serialmsg,buf, sizeof(buf)); Serial.println(buf); status = 0; } aJson.deleteItem(params); return status; } #endif #ifdef SERVER int changedidserver(aJsonObject* params) { aJsonObject* olddidParam = aJson.getObjectItem(params, "olddid"); if (olddidParam){ int olddid = olddidParam -> valueint; if (olddid == configuration.did || olddid == 0 ){ aJsonObject* didParam = aJson.getObjectItem(params, "d"); if (didParam){ int did = didParam -> valueint; configuration.did=did; aJson.deleteItemFromObject(radiomsg, "m"); aJson.deleteItemFromObject(radiomsg, "p"); aJson.addTrueToObject(radiomsg, "r"); } } } //aJson.deleteItem(params); return 0; } int saveserver(aJsonObject* params) { aJsonObject* didParam = aJson.getObjectItem(params, "d"); if (didParam){ int did = didParam -> valueint; if (did == configuration.did || did == 0 ){ //my did or broadcast aJsonObject* saveParam = aJson.getObjectItem(params, "eeprom"); if (saveParam){ boolean eeprom = saveParam -> valuebool; if (eeprom) configuration.save(); aJson.deleteItemFromObject(radiomsg, "m"); aJson.deleteItemFromObject(radiomsg, "p"); aJson.addTrueToObject(radiomsg, "r"); } } } //aJson.deleteItem(params); return 0; } int singleserver(aJsonObject* params) { //{"jsonrpc":"2.0","method":"single","params":{"d":1,"u":1,"o":true},"id":0} aJsonObject* didParam = aJson.getObjectItem(params, "d"); if (didParam){ int did = didParam -> valueint; if (did == configuration.did || did == 0 ){ //my did or broadcast aJsonObject* dstunitParam = aJson.getObjectItem(params, "u"); if (dstunitParam){ int dstunit = dstunitParam -> valueint; if (dstunit >= 0 && dstunit < sizeof(pins)/sizeof(*pins)){ aJsonObject* onoffParam = aJson.getObjectItem(params, "o"); if (onoffParam){ boolean onoff = onoffParam -> valuebool; IF_SDEBUG(DBGSERIAL.print(F("#did: "))); IF_SDEBUG(DBGSERIAL.print(did)); IF_SDEBUG(DBGSERIAL.print(F(" dstunit: "))); IF_SDEBUG(DBGSERIAL.print(dstunit)); IF_SDEBUG(DBGSERIAL.print(F(" onoff: "))); IF_SDEBUG(DBGSERIAL.println(onoff)); digitalWrite(pins[dstunit], ! onoff); aJson.deleteItemFromObject(radiomsg, "m"); aJson.deleteItemFromObject(radiomsg, "p"); aJson.addTrueToObject(radiomsg, "r"); }else{ IF_SDEBUG(DBGSERIAL.println(F("#no onoff"))); } }else{ IF_SDEBUG(DBGSERIAL.println(F("#wrong dstunit"))); } }else{ IF_SDEBUG(DBGSERIAL.println(F("#no dstunit"))); } }else{ IF_SDEBUG(DBGSERIAL.println(F("#not for me"))); } }else{ IF_SDEBUG(DBGSERIAL.println(F("#no did"))); } //IF_SDEBUG(DBGSERIAL.println(F("{\"result\": \"OK\"}")); //aJson.deleteItem(params); return 0; } #endif void setup() { /* Nel caso di un chip in standalone senza bootloader, la prima istruzione che è bene mettere nel setup() è sempre la disattivazione del Watchdog stesso: il Watchdog, infatti, resta attivo dopo il reset e, se non disabilitato, esso può provare il reset perpetuo del microcontrollore */ wdt_disable(); wdt_enable(WDTO_8S); IF_SDEBUG(DBGSERIAL.begin(SERIALBAUDRATE)); Serial.begin(SERIALBAUDRATE); while (!Serial); // wait for serial port to connect. Needed for native USB Serial.println(F("#Started: "VERSION)); #ifdef TWOWAY Serial.println(F("#Twovay: "TWOWAY)); #endif #ifdef CLIENT Serial.println(F("#Client: "CLIENT)); #endif #ifdef SERVER Serial.println(F("#Server: "SERVER)); #endif if (configuration.load()){ IF_SDEBUG(DBGSERIAL.println(F("#Configuration loaded"))); IF_SDEBUG(DBGSERIAL.print(F("#did:"))); IF_SDEBUG(DBGSERIAL.println(configuration.did)); } else { IF_SDEBUG(DBGSERIAL.println(F("#Configuration not loaded"))); } // register the local single method #ifdef SERVER // Radio port rpcserver.registerMethod("single", &singleserver); rpcserver.registerMethod("changedid", &changedidserver); rpcserver.registerMethod("remotesave",&saveserver); #endif #ifdef CLIENT // Serial port rpcclient.registerMethod("single", &client); rpcclient.registerMethod("changedid", &client); rpcclient.registerMethod("remotesave",&client); rpcclient.registerMethod("setdid", &setdid); rpcclient.registerMethod("save", &save); #endif // CC110L may be equipped with either 26 or 27MHz crystals. You MUST // tell the driver if a 27MHz crystal is installed for the correct configuration to // occur. Failure to correctly set this flag will cause incorrect frequency and modulation // characteristics to be used. You can call this function, or pass it to the constructor //cc110.setIs27MHz(true); // Anaren 430BOOST-CC110L Air BoosterPack test boards have 27MHz if (!cc110.init()){ IF_SDEBUG(DBGSERIAL.println(F("init failed"))); Reboot(); } // After init(), the following default values apply: // TxPower: TransmitPower5dBm // Frequency: 915.0 // Modulation: GFSK_Rb1_2Fd5_2 (GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity) // Sync Words: 0xd3, 0x91 // But you can change them: // cc110.setTxPower(RH_CC110::TransmitPowerM30dBm); // cc110.setModemConfig(RH_CC110::GFSK_Rb250Fd127); //cc110.setFrequency(928.0); /* Canale Frequenza (MHz) Canale Frequenza (MHz) Canale Frequenza (MHz) 1 433.075 24 433.650 47 434.225 2 433.100 25 433.675 48 434.250 3 433.125 26 433.700 49 434.275 4 433.150 27 433.725 50 434.300 5 433.175 28 433.750 51 434.325 6 433.200 29 433.775 52 434.350 7 433.225 30 433.800 53 434.375 8 433.250 31 433.825 54 434.400 9 433.275 32 433.850 55 434.425 10 433.300 33 433.875 56 434.450 11 433.325 34 433.900 57 434.475 12 433.350 35 433.925 58 434.500 13 433.375 36 433.950 59 434.525 14 433.400 37 433.975 60 434.550 15 433.425 38 434.000 61 434.575 16 433.450 39 434.025 62 434.600 17 433.475 40 434.050 63 434.625 18 433.500 41 434.075 64 434.650 19 433.525 42 434.100 65 434.675 20 433.550 43 434.125 66 434.700 21 433.575 44 434.150 67 434.725 22 433.600 45 434.175 68 434.750 23 433.625 46 434.200 69 434.775 */ cc110.setTxPower(RH_CC110::TransmitPower0dBm); //cc110.setModemConfig(RH_CC110::GFSK_Rb4_8Fd25_4); // Giacomo cc110.setModemConfig(RH_CC110::GFSK_Rb100Fd47); // Pat1 // For 26MHz crystals //PROGMEM static const RH_CC110::ModemConfig GFSK_R1_2Fd25_4 = static const RH_CC110::ModemConfig GFSK_R1_2Fd25_4 = // 0B 0C 10 11 12 15 19 1A 1B 1C 1D 21 22 23 24 25 26 2C 2D 2E {0x06, 0x00, 0xC5, 0x83, 0x13, 0x40, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}; // GFSK_R1_2Fd47 GFSK, Data Rate: 1.2kBaud, Dev: 47kHz, RX BW 325kHz, optimised for sensitivity //{0x06, 0x00, 0xc7, 0x83, 0x13, 0x40, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}; // GFSK_Rb4_8Fd25_4 //cc110.setModemRegisters(&GFSK_R1_2Fd25_4); cc110.setFrequency(434.0+FREQCORR); for (int dstunit=0 ;dstunit < sizeof(pins)/sizeof(*pins); dstunit++) { pinMode(pins[dstunit], OUTPUT); digitalWrite(pins[dstunit], 1); } #ifdef CLIENT if (stream.available()) { // skip any accidental whitespace like newlines stream.skip(); } #endif } #ifdef CLIENT void mgr_serial(){ unsigned int err; if (stream.available()) { serialmsg = aJson.parse(&stream); if (serialmsg){ IF_SDEBUG(DBGSERIAL.print(F("#rpc.processMessage:"))); char serialbuf[SERIALBUFFERSIZE]; aJson.print(serialmsg, serialbuf, sizeof(serialbuf)); IF_SDEBUG(DBGSERIAL.println(serialbuf)); err=rpcclient.processMessage(serialmsg); IF_SDEBUG(DBGSERIAL.print(F("#rpcclient.processMessage return status:"))); IF_SDEBUG(DBGSERIAL.println(err)); if (!err){ aJson.deleteItem(serialmsg); }else{ err = 1; } }else{ IF_SDEBUG(DBGSERIAL.println(F("#skip wrong message"))); err = 2; if (stream.available()) { stream.flush(); } } if (err == 1){ aJsonObject *result = aJson.createObject(); aJson.addItemToObject(serialmsg, "error", result); aJson.addNumberToObject(result, "code", E_INTERNAL_ERROR); aJson.addStringToObject(result,"message", strerror(E_INTERNAL_ERROR)); /* if (!rpcid || !msg){ IF_SDEBUG(IF_SDEBUG(DBGSERIAL.println(F("#add null id in response")))); aJson.addNullToObject(serialmsg, "id"); } else { IF_SDEBUG(IF_SDEBUG(DBGSERIAL.println(F("#add id in response")))); aJson.addNumberToObject(serialmsg, "id", rpcid->valueint); } */ char serialbuf[SERIALBUFFERSIZE]; aJson.print(serialmsg,serialbuf, sizeof(serialbuf)); Serial.println(serialbuf); aJson.deleteItem(serialmsg); } } } #endif #ifdef SERVER void mgr_radio(){ unsigned int err; if (cc110.available()) { // Should be a message for us now uint8_t buf[RH_CC110_MAX_MESSAGE_LEN+1]; uint8_t len = RH_CC110_MAX_MESSAGE_LEN; if (cc110.recv(buf, &len)) { buf[len]=NULL; // terminate the string //RH_CC110::printBuffer("#request: ", buf, len); IF_SDEBUG(DBGSERIAL.print(F("#got request: "))); IF_SDEBUG(DBGSERIAL.println((char*)buf)); IF_SDEBUG(DBGSERIAL.print(F("#RSSI: "))); IF_SDEBUG(DBGSERIAL.println(cc110.lastRssi(), DEC)); radiomsg = aJson.parse((char*)buf); if (radiomsg){ err=rpcserver.processMessage(radiomsg); IF_SDEBUG(DBGSERIAL.print(F("#rpcserver.processMessage return status:"))); IF_SDEBUG(DBGSERIAL.println(err)); if (!err) { #ifdef TWOWAY // Send a reply // "{\"jsonrpc\": \"2.0\", \"result\":true, \"id\": 0}" aJson.print(radiomsg, (char*)buf, sizeof(buf)); IF_SDEBUG(DBGSERIAL.print(F("#Send: "))); IF_SDEBUG(DBGSERIAL.println((char*)buf)); cc110.send(buf, len); cc110.waitPacketSent(); IF_SDEBUG(DBGSERIAL.println(F("#Sent a reply"))); #endif aJson.deleteItem(radiomsg); }else{ err = 1; aJson.deleteItem(radiomsg); } }else{ IF_SDEBUG(DBGSERIAL.println(F("#skip wrong message"))); err = 2; } } else { IF_SDEBUG(DBGSERIAL.println(F("recv failed"))); err = 3; } } } #endif void loop() { wdt_reset(); #ifdef CLIENT mgr_serial(); #endif #ifdef SERVER wdt_reset(); mgr_radio(); #endif }