-- -- By Midelic on RCGroups -- script to change DIY RX/JUMPER R8 parameters ---transmit-- --physicalId = 0x17 -- primId = 0x30 --primId = 0x31 -- dataId = 0x0C20 ---receive-- --physicalId = 0x1B --primId = 0x32 -- dataId = 0x0C20 LOCAL_SENSOR_ID = 0x17 REQUEST_FRAME_ID = 0x30 DATA_ID = 0x0C20 REMOTE_SENSOR_ID = 0x1B REPLY_FRAME_ID = 0x32 CFG_MODES = 0xE4 CFG_SET_MODES = 0xE4 REQUEST_SET_FRAME_ID = 0x31 CFG_STATISTICS = 0xE5 CFG_STATISTICS2 = 0xE6 local REQ_TIMEOUT = 80 local PAGE_DISPLAY = 2 local EDITING = 3 local PAGE_SAVING = 4 local MENU_DISP = 5 local currentLine = 1 local saveTS = 0 local saveRetries = 0 local gState = PAGE_DISPLAY local cfgRxBuf = {} local cfgRxIdx = 1 local cfgTxBuf = {} local cfgTxIdx = 1 local cfgTxPk = 0 local cfgRequestsSent = 0 local cfgRepliesReceived = 0 local payloadReq = {} local result = 0 local now = 0 local cfgmode = 0 local cfgLastReq = 0 local currentPage = 1 local cfgRxTemp = {} local cfgTxTemp = 225 local currentLine = 1 local saveTS = 0 local saveRetries = 0 local version = 0 local function cfgSendSport(payloadTx) local value = 0 value = payloadTx[1] + bit32.lshift(payloadTx[2],8)+ bit32.lshift(payloadTx[3],16) + bit32.lshift(payloadTx[4],24) local ret = sportTelemetryPush(LOCAL_SENSOR_ID, REQUEST_FRAME_ID, DATA_ID, value) if ret then cfgTxPk = cfgTxPk + 1 end end local function cfgProcessTxQ() if #(cfgTxBuf) == 0 then --important leave it here return false end if not sportTelemetryPush() then return true end local payloadTx = {} payloadTx[1] = cfgTxBuf[1] local temp = 0 if #(payloadReq) then if cfgTxBuf[3] ~= nil then temp = 4 - cfgTxBuf[2] local n = 0 local v = 1 while n <= temp do v = v * 2 n = n + 1 end cfgTxBuf[2] = v cfgTxBuf[3] = cfgTxTemp cfgTxBuf[5] = cfgTxBuf[5]* 4 cfgTxBuf[6] = cfgTxBuf[6]*16 cfgTxBuf[4] = cfgTxBuf[4]+cfgTxBuf[5]+cfgTxBuf[6] local i = 2 while i <= 4 do payloadTx[i] = cfgTxBuf[i] i = i +1 end cfgSendSport(payloadTx) cfgTxBuf = {} cfgTxIx = 1 return end end local i = 2 while i <= 4 do payloadTx[i] = 0 i = i +1 end cfgSendSport(payloadTx) cfgTxBuf = {} return end local function cfgSendRequest(cmd,payloadReq) if #(cfgTxBuf) ~= 0 then return nil end cfgTxBuf[1] = bit32.band(cmd,0xFF) for i = 1,#(payloadReq) do cfgTxBuf[i+1] = payloadReq[i] end cfgLastReq = cmd cfgRequestsSent = cfgRequestsSent+1 return cfgProcessTxQ() end local function cfgReceivedReply(payload) local idx = 1 cfgRxIdx = 1 --local page = SetupPages[currentPage] cfgRxBuf = {} while cfgRxIdx <= 4 do cfgRxBuf[cfgRxIdx] = payload[idx] cfgRxIdx = cfgRxIdx+1 idx = idx+1 end cfgRepliesReceived = 1 return cfgRxBuf end local function cfgPollReply() while true do local sensorId, frameId, dataId, value = sportTelemetryPop() --test --frameId = REPLY_FRAME_ID --dataId = 0x0C20 -- value = 0 if frameId == REPLY_FRAME_ID then payload = {} if dataId == 0x0C20 then payload[1] = bit32.band(value,0xFF) value = bit32.rshift(value,8) payload[2] = bit32.band(value,0xFF) value = bit32.rshift(value,8) payload[3] = bit32.band(value,0xFF) value = bit32.rshift(value,8) payload[4] = bit32.band(value,0xFF) -- if currentPage == 1 then -- payload[1] = 16 -- payload[2] = 225 -- payload[3] = 17 -- payload[4] = 2 -- end local ret = cfgReceivedReply(payload) return cfgLastReq,ret end else break end end return nil end local function processcfgReply(cmd,cfgRxBuf) if cmd == nil or cfgRxBuf == nil then return end local page = SetupPages[currentPage] -- ignore replies to write requests for now if REQUEST_FRAME_ID == REQUEST_SET_FRAME_ID then --cfgSendRequest(CFG_SET_MODES,{}) -- return --end --if REQUEST_FRAME_ID == REQUEST_SET_FRAME_ID then gState = PAGE_DISPLAY page.values = nil saveTS = 0 return end --if cmd ~= page.read then --return --end if #(cfgRxBuf) > 0 then if currentPage == 1 and cfgLastReq == CFG_MODES then page.values = {} cfgmode = cfgRxBuf[1] local n = 0 while n <= 4 do cfgmode = cfgmode / 2 if cfgmode == 1 then break end n = n+ 1 end if n <= 4 then n = 4 - n end if cfgRxBuf[4] == 1 then page.values[1] = 0 else page.values[1]= n end if cfgRxBuf[2] == 225 then page.values[2] = 1 elseif cfgRxBuf[2] == 180 then page.values[2]= 2 elseif cfgRxBuf[2] == 90 then page.values[2] = 3 end page.values[3] = bit32.band(cfgRxBuf[3],0x0F) page.values[4] = bit32.band(cfgRxBuf[3],0xF0)/16 page.values[5] = cfgRxBuf[4] elseif currentPage == 2 then page.values = {} if cfgLastReq == CFG_STATISTICS then cfgRxTemp[1] = cfgRxBuf[1] + bit32.lshift (cfgRxBuf[2],8) cfgRxTemp[2] = cfgRxBuf[3] cfgRxTemp[3] = cfgRxBuf[4] cfgTxBuf = {} cfgSendRequest(CFG_STATISTICS2,{}) elseif cfgLastReq == CFG_STATISTICS2 then cfgRxTemp[4] = cfgRxBuf[1] + bit32.lshift (cfgRxBuf[2],8) cfgRxTemp[5] = cfgRxBuf[3] + bit32.lshift (cfgRxBuf[4],8) end i = 1 while i <= 5 do page.values[i] = cfgRxTemp[i] i = i + 1 end cfgRxIdx = 1 end end end SetupPages = { { title = "RX-MODES", text = { { t = "Modes", x = 1, y = 10 }, { t = "Servo[ms]", x = 1, y = 20 }, { t = "Sbus_Inv", x = 1, y = 30 }, { t = "Sport_Inv", x = 1, y = 40 }, { t = "Protocol", x = 1, y = 50 }, }, fields = { { x = 52, y = 10, i = 1, min=1, max=4,table = {[0]="NS","1-8","1-8NT","9-16","9-16NT"} }, { x = 52, y = 20, i = 2, min=1, max=3,table = {[0]="NS","22.5","18.0","9.0"} }, { x = 52, y = 30, i = 3, min=1, max=2,table = {[0]="NS","NO", "YES"} }, { x = 52, y = 40, i = 4, min=1, max=2,table = {[0]="NS","NO","YES"} }, { x = 52, y = 50, i = 5, min=1, max=3,table = {[0]="NS","D8","D16","LBT"} }, }, read = CFG_MODES, write = CFG_SET_MODES, }, { title = "STATISTICS", text = { { t = "DropPkt", x = 1, y = 10 }, { t = "Drop%", x = 1, y = 20 }, { t = "TotalCrcErr", x = 1, y = 30 }, { t = "LbtBlks", x = 1, y = 40 }, { t = "PktErr", x = 1, y = 50 }, }, fields = { { x = 25, y = 10, sp = 40, i = 1 }, { x = 25, y = 20, sp = 40, i = 2 }, { x = 25, y = 30, sp = 40, i = 3 }, { x = 25, y = 40, sp = 40, i = 4 }, { x = 25, y = 50, sp = 40, i = 5 }, }, read = CFG_STATISTICS, }, } local function saveSettings(new) local page = SetupPages[currentPage] if page.values then REQUEST_FRAME_ID = REQUEST_SET_FRAME_ID if currentPage == 1 then if page.values[2] == 1 then cfgTxTemp = 225 elseif page.values[2] == 2 then cfgTxTemp = 180 elseif page.values[2] == 3 then cfgTxTemp = 90 end cfgSendRequest(page.write,page.values) end saveTS = getTime() if gState == PAGE_SAVING then saveRetries = saveRetries + 1 else gState = PAGE_SAVING end end end local function invalidatePages() for i=1,#(SetupPages) do local page = SetupPages[i] page.values = nil end cfgTxBuf = {} gState = PAGE_DISPLAY saveTS = 0 end local menuList = { { t = "save page", f = saveSettings }, { t = "reload", f = invalidatePages } } local menuActive = false local function MaxLines() return #(SetupPages[currentPage].fields) end local function incPage(inc) currentPage = currentPage + inc if currentPage > #(SetupPages) then currentPage = 1 elseif currentPage < 1 then currentPage = #(SetupPages) end currentLine = 1 end local function incLine(inc) currentLine = currentLine + inc if currentLine > MaxLines() then currentLine = 1 elseif currentLine < 1 then currentLine = MaxLines() end end local function incMenu(inc) menuActive = menuActive + inc if menuActive > #(menuList) then menuActive = 1 elseif menuActive < 1 then menuActive = #(menuList) end end local function requestPage(page) if page.read and ((page.reqTS == nil) or (page.reqTS + REQ_TIMEOUT <= getTime())) then page.reqTS = getTime() cfgSendRequest(page.read,{}) end end function drawScreenTitle(screen_title) lcd.drawFilledRectangle(0, 0, LCD_W, 10) lcd.drawText(1,1,screen_title,INVERS) end local function drawScreen(page,page_locked) local screen_title = page.title drawScreenTitle('DIY RX setup: '..screen_title,currentPage,#(SetupPages)) for i=1,#(page.text) do local f = page.text[i] lcd.drawText(f.x, f.y, f.t, text_options) end for i=1,#(page.fields) do local f = page.fields[i] local text_options = 0 if i == currentLine then text_options = INVERS if gState == EDITING then text_options = text_options + BLINK end end local spacing = 20 -- if f.t ~= nil then -- lcd.drawText(f.x, f.y, f.t .. ":", globalTextOptions) -- draw some value if f.sp ~= nil then spacing = f.sp end --else -- spacing = 20 -- end local idx = f.i or i if page.values and page.values[idx] then local val = page.values[idx] if f.table and f.table[page.values[idx]] then val = f.table[page.values[idx]] end lcd.drawText(f.x + spacing, f.y,val, text_options) else lcd.drawText(f.x + spacing, f.y, "---", text_options) end end end local function clipValue(val,min,max) if val < min then val = min elseif val > max then val = max end return val end local function getCurrentField() local page = SetupPages[currentPage] return page.fields[currentLine] end local function incValue(inc) local page = SetupPages[currentPage] local field = page.fields[currentLine] local idx = field.i or currentLine page.values[idx] = clipValue(page.values[idx] + inc,field.min or 0, field.max) if field.upd then field.upd(page) end end local function drawMenu() local x = 40 local y = 12 local w = 120 local h = #(menuList) * 8 + 6 lcd.drawFilledRectangle(x,y,w,h,ERASE) lcd.drawRectangle(x,y,w-1,h-1,SOLID) lcd.drawText(x+4,y+3,"Menu:") for i,e in ipairs(menuList) do if menuActive == i then lcd.drawText(x+36,y+(i-1)*8+3,e.t,INVERS) else lcd.drawText(x+36,y+(i-1)*8+3,e.t) end end end --local EVT_MENU_LONG = bit32.bor(bit32.band(EVT_MENU_BREAK,0x1f),0x80) local lastRunTS = 0 local function run(event) local now = getTime() -- if lastRunTS old than 500ms if lastRunTS + 50 < now then invalidatePages() end lastRunTS = now -- TODO: implement retry + retry counter if (gState == PAGE_SAVING) and (saveTS + 150 < now) then if saveRetries < 2 then saveSettings() else -- two retries and still no success gState = PAGE_DISPLAY saveTS = 0 end end --if #(cfgTxBuf) > 0 then -- cfgProcessTxQ() -- end -- navigation if event == EVT_MENU_LONG then -- if event == EVT_PAGEDN_LONG then menuActive = 1 gState = MENU_DISP -- menu is currently displayed elseif gState == MENU_DISP then if event == EVT_EXIT_BREAK then gState = PAGE_DISPLAY elseif event == EVT_PLUS_BREAK or event == EVT_ROT_LEFT then incMenu(-1) elseif event == EVT_MINUS_BREAK or event == EVT_ROT_RIGHT then incMenu(1) elseif event == EVT_ENTER_BREAK then gState = PAGE_DISPLAY if currentPage == 1 then menuList[menuActive].f() else menuList[2].f() end end -- normal page viewing elseif gState <= PAGE_DISPLAY then if event == EVT_MENU_BREAK then incPage(1) elseif event == EVT_PLUS_BREAK or event == EVT_ROT_LEFT then incLine(-1) elseif event == EVT_MINUS_BREAK or event == EVT_ROT_RIGHT then incLine(1) elseif event == EVT_ENTER_BREAK then local page = SetupPages[currentPage] local field = page.fields[currentLine] local idx = field.i or currentLine if currentPage == 1 then if page.values and page.values[idx] then gState = EDITING end end end -- editing value elseif gState == EDITING then if (event == EVT_EXIT_BREAK) or (event == EVT_ENTER_BREAK) then gState = PAGE_DISPLAY elseif event == EVT_PLUS_FIRST or event == EVT_PLUS_REPT or event == EVT_ROT_RIGHT then incValue(1) elseif event == EVT_MINUS_FIRST or event == EVT_MINUS_REPT or event == EVT_ROT_LEFT then incValue(-1) end end local page = SetupPages[currentPage] local page_locked = false if not page.values then -- request values requestPage(page) page_locked = true end -- draw screen lcd.clear() drawScreen(page,page_locked) -- do we have valid telemetry data? if getValue("RSSI") == 0 then -- No! lcd.drawText(70,55,"No telemetry",BLINK) else --version = 60320 version = getValue('52F1') lcd.drawText(120,55,"Version : ",SMALL) lcd.drawText(170,55,version,SMALL) --invalidatePages() end if gState == MENU_DISP then drawMenu() elseif gState == PAGE_SAVING then lcd.drawFilledRectangle(40,12,120,30,ERASE) lcd.drawRectangle(40,12,120,30,SOLID) lcd.drawText(44,15,"Saving...",DBLSIZE + BLINK) -- lcd.drawText(20,30,"Turn Tx OFF", BLINK) -- lcd.drawText(10,40,"When saving completed", BLINK) end processcfgReply(cfgPollReply()) return 0 end return { run=run}